Compare commits

...

3 Commits

Author SHA1 Message Date
Anselm
b09e35e372 release 2024-07-29 10:40:10 +01:00
Anselm
d2c8121c9c Fix storage option in jazz-react 2024-07-29 10:34:39 +01:00
Anselm
380bb88ffa Mostly complete OPFS implementation (single-tab only) 2024-07-29 10:33:18 +01:00
33 changed files with 423 additions and 96 deletions

View File

@@ -1,5 +1,14 @@
# jazz-example-chat # jazz-example-chat
## 0.0.70
### Patch Changes
- Updated dependencies
- cojson@0.7.23
- jazz-react@0.7.23
- jazz-tools@0.7.23
## 0.0.69 ## 0.0.69
### Patch Changes ### Patch Changes

View File

@@ -1,7 +1,7 @@
{ {
"name": "jazz-example-chat", "name": "jazz-example-chat",
"private": true, "private": true,
"version": "0.0.69", "version": "0.0.70",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -1,5 +1,13 @@
# jazz-example-chat # jazz-example-chat
## 0.0.51
### Patch Changes
- Updated dependencies
- cojson@0.7.23
- cojson-transport-ws@0.7.23
## 0.0.50 ## 0.0.50
### Patch Changes ### Patch Changes

View File

@@ -1,7 +1,7 @@
{ {
"name": "jazz-inspector", "name": "jazz-inspector",
"private": true, "private": true,
"version": "0.0.50", "version": "0.0.51",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -1,5 +1,14 @@
# jazz-example-pets # jazz-example-pets
## 0.0.88
### Patch Changes
- Updated dependencies
- jazz-react@0.7.23
- jazz-tools@0.7.23
- jazz-browser-media-images@0.7.23
## 0.0.87 ## 0.0.87
### Patch Changes ### Patch Changes

View File

@@ -1,7 +1,7 @@
{ {
"name": "jazz-example-pets", "name": "jazz-example-pets",
"private": true, "private": true,
"version": "0.0.87", "version": "0.0.88",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -1,5 +1,13 @@
# jazz-example-todo # jazz-example-todo
## 0.0.87
### Patch Changes
- Updated dependencies
- jazz-react@0.7.23
- jazz-tools@0.7.23
## 0.0.86 ## 0.0.86
### Patch Changes ### Patch Changes

View File

@@ -1,7 +1,7 @@
{ {
"name": "jazz-example-todo", "name": "jazz-example-todo",
"private": true, "private": true,
"version": "0.0.86", "version": "0.0.87",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -1,5 +1,12 @@
# cojson-storage-indexeddb # cojson-storage-indexeddb
## 0.7.23
### Patch Changes
- Updated dependencies
- cojson@0.7.23
## 0.7.18 ## 0.7.18
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "cojson-storage-indexeddb", "name": "cojson-storage-indexeddb",
"version": "0.7.18", "version": "0.7.23",
"main": "dist/index.js", "main": "dist/index.js",
"type": "module", "type": "module",
"types": "src/index.ts", "types": "src/index.ts",

View File

@@ -1,5 +1,12 @@
# cojson-storage-sqlite # cojson-storage-sqlite
## 0.7.23
### Patch Changes
- Updated dependencies
- cojson@0.7.23
## 0.7.18 ## 0.7.18
### Patch Changes ### Patch Changes

View File

@@ -1,7 +1,7 @@
{ {
"name": "cojson-storage-sqlite", "name": "cojson-storage-sqlite",
"type": "module", "type": "module",
"version": "0.7.18", "version": "0.7.23",
"main": "dist/index.js", "main": "dist/index.js",
"types": "src/index.ts", "types": "src/index.ts",
"license": "MIT", "license": "MIT",

View File

@@ -1,5 +1,12 @@
# cojson-transport-nodejs-ws # cojson-transport-nodejs-ws
## 0.7.23
### Patch Changes
- Updated dependencies
- cojson@0.7.23
## 0.7.22 ## 0.7.22
### Patch Changes ### Patch Changes

View File

@@ -1,7 +1,7 @@
{ {
"name": "cojson-transport-ws", "name": "cojson-transport-ws",
"type": "module", "type": "module",
"version": "0.7.22", "version": "0.7.23",
"main": "dist/index.js", "main": "dist/index.js",
"types": "src/index.ts", "types": "src/index.ts",
"license": "MIT", "license": "MIT",

View File

@@ -1,5 +1,11 @@
# cojson # cojson
## 0.7.23
### Patch Changes
- Mostly complete OPFS implementation (single-tab only)
## 0.7.18 ## 0.7.18
### Patch Changes ### Patch Changes

View File

@@ -5,7 +5,7 @@
"types": "src/index.ts", "types": "src/index.ts",
"type": "module", "type": "module",
"license": "MIT", "license": "MIT",
"version": "0.7.18", "version": "0.7.23",
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.3", "@types/jest": "^29.5.3",
"@typescript-eslint/eslint-plugin": "^6.2.1", "@typescript-eslint/eslint-plugin": "^6.2.1",

View File

@@ -3,7 +3,7 @@ import { CoValueChunk } from "./index.js";
import { RawCoID } from "../ids.js"; import { RawCoID } from "../ids.js";
import { CryptoProvider, StreamingHash } from "../crypto/crypto.js"; import { CryptoProvider, StreamingHash } from "../crypto/crypto.js";
export type BlockFilename = `${string}-L${number}-H${number}.jsonl`; export type BlockFilename = `L${number}-${string}-${string}-H${number}.jsonl`;
export type BlockHeader = { id: RawCoID; start: number; length: number }[]; export type BlockHeader = { id: RawCoID; start: number; length: number }[];
@@ -78,8 +78,9 @@ export function readHeader<RH, FS extends FileSystem<unknown, RH>>(
export function writeBlock<WH, RH, FS extends FileSystem<WH, RH>>( export function writeBlock<WH, RH, FS extends FileSystem<WH, RH>>(
chunks: Map<RawCoID, CoValueChunk>, chunks: Map<RawCoID, CoValueChunk>,
level: number, level: number,
blockNumber: number,
fs: FS, fs: FS,
): Effect.Effect<void, FSErr> { ): Effect.Effect<BlockFilename, FSErr> {
if (chunks.size === 0) { if (chunks.size === 0) {
return Effect.die(new Error("No chunks to write")); return Effect.die(new Error("No chunks to write"));
} }
@@ -125,12 +126,17 @@ export function writeBlock<WH, RH, FS extends FileSystem<WH, RH>>(
// ), // ),
// ); // );
const filename: BlockFilename = `${hash.digest()}-L${level}-H${ const filename: BlockFilename = `L${level}-${(
headerBytes.length blockNumber + ""
}.jsonl`; ).padStart(3, "0")}-${hash
.digest()
.replace("hash_", "")
.slice(0, 15)}-H${headerBytes.length}.jsonl`;
// console.log("renaming to" + filename); // console.log("renaming to" + filename);
yield* $(fs.closeAndRename(file, filename)); yield* $(fs.closeAndRename(file, filename));
return filename;
// console.log("Wrote block", filename, blockHeader); // console.log("Wrote block", filename, blockHeader);
// console.log("IDs in block", blockHeader.map(e => e.id)); // console.log("IDs in block", blockHeader.map(e => e.id));
}); });
@@ -148,6 +154,7 @@ export function writeToWal<WH, RH, FS extends FileSystem<WH, RH>>(
...chunk, ...chunk,
}; };
const bytes = textEncoder.encode(JSON.stringify(walEntry) + "\n"); const bytes = textEncoder.encode(JSON.stringify(walEntry) + "\n");
console.log("writing to WAL", handle, id, bytes.length);
yield* $(fs.append(handle, bytes)); yield* $(fs.append(handle, bytes));
}); });
} }

View File

@@ -1,4 +1,11 @@
import { Effect, Either, Queue, Stream, SynchronizedRef } from "effect"; import {
Effect,
Either,
Queue,
Stream,
SynchronizedRef,
Deferred,
} from "effect";
import { RawCoID } from "../ids.js"; import { RawCoID } from "../ids.js";
import { CoValueHeader, Transaction } from "../coValueCore.js"; import { CoValueHeader, Transaction } from "../coValueCore.js";
import { Signature } from "../crypto/crypto.js"; import { Signature } from "../crypto/crypto.js";
@@ -30,6 +37,8 @@ import {
} from "./FileSystem.js"; } from "./FileSystem.js";
export type { FSErr, BlockFilename, WalFilename } from "./FileSystem.js"; export type { FSErr, BlockFilename, WalFilename } from "./FileSystem.js";
const MAX_N_LEVELS = 3;
export type CoValueChunk = { export type CoValueChunk = {
header?: CoValueHeader; header?: CoValueHeader;
sessionEntries: { sessionEntries: {
@@ -51,6 +60,10 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
BlockFilename, BlockFilename,
{ [id: RawCoID]: { start: number; length: number } } { [id: RawCoID]: { start: number; length: number } }
>(); >();
blockFileHandles = new Map<
BlockFilename,
Deferred.Deferred<{ handle: RH; size: number }, FSErr>
>();
constructor( constructor(
public fs: FS, public fs: FS,
@@ -192,7 +205,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
let newWal = wal; let newWal = wal;
if (!newWal) { if (!newWal) {
newWal = yield* this.fs.createFile( newWal = yield* this.fs.createFile(
`wal-${new Date().toISOString()}-${Math.random() `wal-${Date.now()}-${Math.random()
.toString(36) .toString(36)
.slice(2)}.jsonl`, .slice(2)}.jsonl`,
); );
@@ -314,24 +327,63 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
); );
} }
loadCoValue<WH, RH, FS extends FileSystem<WH, RH>>( getBlockHandle(
blockFile: BlockFilename,
fs: FS,
): Effect.Effect<{ handle: RH; size: number }, FSErr> {
return Effect.gen(this, function* () {
let handleAndSize = this.blockFileHandles.get(blockFile);
if (!handleAndSize) {
handleAndSize = yield* Deferred.make<
{ handle: RH; size: number },
FSErr
>();
this.blockFileHandles.set(blockFile, handleAndSize);
yield* Deferred.complete(
handleAndSize,
fs.openToRead(blockFile),
);
}
return yield* Deferred.await(handleAndSize);
});
}
loadCoValue(
id: RawCoID, id: RawCoID,
fs: FS, fs: FS,
): Effect.Effect<CoValueChunk | undefined, FSErr> { ): Effect.Effect<CoValueChunk | undefined, FSErr> {
// return _loadChunkFromWal(id, fs);
return Effect.gen(this, function* () { return Effect.gen(this, function* () {
const files = this.fileCache || (yield* fs.listFiles()); const files = this.fileCache || (yield* fs.listFiles());
this.fileCache = files; this.fileCache = files;
const blockFiles = files.filter((name) => const blockFiles = (
name.startsWith("hash_"), files.filter((name) => name.startsWith("L")) as BlockFilename[]
) as BlockFilename[]; ).sort();
let result;
for (const blockFile of blockFiles) { for (const blockFile of blockFiles) {
let cachedHeader: let cachedHeader:
| { [id: RawCoID]: { start: number; length: number } } | { [id: RawCoID]: { start: number; length: number } }
| undefined = this.headerCache.get(blockFile); | undefined = this.headerCache.get(blockFile);
const { handle, size } = yield* fs.openToRead(blockFile); let handleAndSize = this.blockFileHandles.get(blockFile);
if (!handleAndSize) {
handleAndSize = yield* Deferred.make<
{ handle: RH; size: number },
FSErr
>();
this.blockFileHandles.set(blockFile, handleAndSize);
yield* Deferred.complete(
handleAndSize,
fs.openToRead(blockFile),
);
}
const { handle, size } = yield* this.getBlockHandle(
blockFile,
fs,
);
// console.log("Attempting to load", id, blockFile); // console.log("Attempting to load", id, blockFile);
@@ -356,17 +408,29 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
// console.log("Header entry", id, headerEntry); // console.log("Header entry", id, headerEntry);
let result;
if (headerEntry) { if (headerEntry) {
result = yield* readChunk(handle, headerEntry, fs); const nextChunk = yield* readChunk(handle, headerEntry, fs);
if (result) {
const merged = mergeChunks(result, nextChunk);
if (Either.isRight(merged)) {
yield* Effect.logWarning(
"Non-contigous chunks while loading " + id,
result,
nextChunk,
);
} else {
result = merged.left;
}
} else {
result = nextChunk;
}
} }
yield* fs.close(handle); // yield* fs.close(handle);
return result;
} }
return undefined; return result;
}); });
} }
@@ -434,11 +498,150 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
yield* this.fs.close(handle); yield* this.fs.close(handle);
} }
yield* writeBlock(coValues, 0, this.fs); const highestBlockNumber = fileNames.reduce((acc, name) => {
if (name.startsWith("L" + MAX_N_LEVELS)) {
const num = parseInt(name.split("-")[1]!);
if (num > acc) {
return num;
}
}
return acc;
}, 0);
console.log(
[...coValues.keys()],
fileNames,
highestBlockNumber,
);
yield* writeBlock(
coValues,
MAX_N_LEVELS,
highestBlockNumber + 1,
this.fs,
);
for (const walFile of walFiles) { for (const walFile of walFiles) {
yield* this.fs.removeFile(walFile); yield* this.fs.removeFile(walFile);
} }
this.fileCache = undefined; this.fileCache = undefined;
const fileNames2 = yield* this.fs.listFiles();
const blockFiles = (
fileNames2.filter((name) =>
name.startsWith("L"),
) as BlockFilename[]
).sort();
const blockFilesByLevelInOrder: {
[level: number]: BlockFilename[];
} = {};
for (const blockFile of blockFiles) {
const level = parseInt(blockFile.split("-")[0]!.slice(1));
if (!blockFilesByLevelInOrder[level]) {
blockFilesByLevelInOrder[level] = [];
}
blockFilesByLevelInOrder[level]!.push(blockFile);
}
console.log(blockFilesByLevelInOrder);
for (let level = MAX_N_LEVELS; level > 0; level--) {
const nBlocksDesired = Math.pow(2, level);
const blocksInLevel = blockFilesByLevelInOrder[level];
if (
blocksInLevel &&
blocksInLevel.length > nBlocksDesired
) {
yield* Effect.log("Compacting blocks in level", level, blocksInLevel);
const coValues = new Map<RawCoID, CoValueChunk>();
for (const blockFile of blocksInLevel) {
const {
handle,
size,
}: { handle: RH; size: number } =
yield* this.getBlockHandle(blockFile, this.fs);
if (size === 0) {
continue;
}
const header = yield* readHeader(
blockFile,
handle,
size,
this.fs,
);
for (const entry of header) {
const chunk = yield* readChunk(
handle,
entry,
this.fs,
);
const existingChunk = coValues.get(entry.id);
if (existingChunk) {
const merged = mergeChunks(
existingChunk,
chunk,
);
if (Either.isRight(merged)) {
yield* Effect.logWarning(
"Non-contigous chunks in " +
entry.id +
", " +
blockFile,
existingChunk,
chunk,
);
} else {
coValues.set(entry.id, merged.left);
}
} else {
coValues.set(entry.id, chunk);
}
}
}
let levelBelow = blockFilesByLevelInOrder[level - 1];
if (!levelBelow) {
levelBelow = [];
blockFilesByLevelInOrder[level - 1] = levelBelow;
}
const highestBlockNumberInLevelBelow =
levelBelow.reduce((acc, name) => {
const num = parseInt(name.split("-")[1]!);
if (num > acc) {
return num;
}
return acc;
}, 0);
const newBlockName = yield* writeBlock(
coValues,
level - 1,
highestBlockNumberInLevelBelow + 1,
this.fs,
);
levelBelow.push(newBlockName);
// delete blocks that went into this one
for (const blockFile of blocksInLevel) {
const handle = yield* this.getBlockHandle(
blockFile,
this.fs,
);
yield* this.fs.close(handle.handle);
yield* this.fs.removeFile(blockFile);
}
}
}
}), }),
); );

View File

@@ -1,5 +1,13 @@
# jazz-browser-media-images # jazz-browser-media-images
## 0.7.23
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.23
- jazz-browser@0.7.23
## 0.7.22 ## 0.7.22
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "jazz-browser-media-images", "name": "jazz-browser-media-images",
"version": "0.7.22", "version": "0.7.23",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",
"types": "src/index.ts", "types": "src/index.ts",

View File

@@ -1,5 +1,15 @@
# jazz-browser # jazz-browser
## 0.7.23
### Patch Changes
- Updated dependencies
- cojson@0.7.23
- jazz-tools@0.7.23
- cojson-storage-indexeddb@0.7.23
- cojson-transport-ws@0.7.23
## 0.7.22 ## 0.7.22
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "jazz-browser", "name": "jazz-browser",
"version": "0.7.22", "version": "0.7.23",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",
"types": "src/index.ts", "types": "src/index.ts",

View File

@@ -7,7 +7,7 @@ import {
} from "cojson"; } from "cojson";
import { Effect } from "effect"; import { Effect } from "effect";
export class OPFSFilesystem implements FileSystem<number, number> { export class OPFSFilesystem implements FileSystem<{id: number, filename: string}, {id: number, filename: string}> {
opfsWorker: Worker; opfsWorker: Worker;
callbacks: Map<number, (event: MessageEvent) => void> = new Map(); callbacks: Map<number, (event: MessageEvent) => void> = new Map();
nextRequestId = 0; nextRequestId = 0;
@@ -31,13 +31,13 @@ export class OPFSFilesystem implements FileSystem<number, number> {
listFiles(): Effect.Effect<string[], FSErr, never> { listFiles(): Effect.Effect<string[], FSErr, never> {
return Effect.async((cb) => { return Effect.async((cb) => {
const requestId = this.nextRequestId++; const requestId = this.nextRequestId++;
performance.mark("listFiles" + requestId); performance.mark("listFiles" + requestId + "_listFiles");
this.callbacks.set(requestId, (event) => { this.callbacks.set(requestId, (event) => {
performance.mark("listFilesEnd" + requestId); performance.mark("listFilesEnd" + requestId + "_listFiles");
performance.measure( performance.measure(
"listFiles" + requestId, "listFiles" + requestId + "_listFiles",
"listFiles" + requestId, "listFiles" + requestId + "_listFiles",
"listFilesEnd" + requestId, "listFilesEnd" + requestId + "_listFiles",
); );
cb(Effect.succeed(event.data.fileNames)); cb(Effect.succeed(event.data.fileNames));
}); });
@@ -47,22 +47,22 @@ export class OPFSFilesystem implements FileSystem<number, number> {
openToRead( openToRead(
filename: string, filename: string,
): Effect.Effect<{ handle: number; size: number }, FSErr, never> { ): Effect.Effect<{ handle: {id: number, filename: string}; size: number }, FSErr, never> {
return Effect.async((cb) => { return Effect.async((cb) => {
const requestId = this.nextRequestId++; const requestId = this.nextRequestId++;
performance.mark("openToRead" + requestId); performance.mark("openToRead" + "_" + filename);
this.callbacks.set(requestId, (event) => { this.callbacks.set(requestId, (event) => {
cb( cb(
Effect.succeed({ Effect.succeed({
handle: event.data.handle, handle: {id: event.data.handle, filename},
size: event.data.size, size: event.data.size,
}), }),
); );
performance.mark("openToReadEnd" + requestId); performance.mark("openToReadEnd" + "_" + filename);
performance.measure( performance.measure(
"openToRead" + requestId, "openToRead" + "_" + filename,
"openToRead" + requestId, "openToRead" + "_" + filename,
"openToReadEnd" + requestId, "openToReadEnd" + "_" + filename,
); );
}); });
this.opfsWorker.postMessage({ this.opfsWorker.postMessage({
@@ -73,18 +73,18 @@ export class OPFSFilesystem implements FileSystem<number, number> {
}); });
} }
createFile(filename: string): Effect.Effect<number, FSErr, never> { createFile(filename: string): Effect.Effect<{id: number, filename: string}, FSErr, never> {
return Effect.async((cb) => { return Effect.async((cb) => {
const requestId = this.nextRequestId++; const requestId = this.nextRequestId++;
performance.mark("createFile" + requestId); performance.mark("createFile" + "_" + filename);
this.callbacks.set(requestId, (event) => { this.callbacks.set(requestId, (event) => {
performance.mark("createFileEnd" + requestId); performance.mark("createFileEnd" + "_" + filename);
performance.measure( performance.measure(
"createFile" + requestId, "createFile" + "_" + filename,
"createFile" + requestId, "createFile" + "_" + filename,
"createFileEnd" + requestId, "createFileEnd" + "_" + filename,
); );
cb(Effect.succeed(event.data.handle)); cb(Effect.succeed({id: event.data.handle, filename}));
}); });
this.opfsWorker.postMessage({ this.opfsWorker.postMessage({
type: "createFile", type: "createFile",
@@ -96,18 +96,18 @@ export class OPFSFilesystem implements FileSystem<number, number> {
openToWrite( openToWrite(
filename: string, filename: string,
): Effect.Effect<FileSystemFileHandle, FSErr, never> { ): Effect.Effect<{id: number, filename: string}, FSErr, never> {
return Effect.async((cb) => { return Effect.async((cb) => {
const requestId = this.nextRequestId++; const requestId = this.nextRequestId++;
performance.mark("openToWrite" + requestId); performance.mark("openToWrite" + "_" + filename);
this.callbacks.set(requestId, (event) => { this.callbacks.set(requestId, (event) => {
performance.mark("openToWriteEnd" + requestId); performance.mark("openToWriteEnd" + "_" + filename);
performance.measure( performance.measure(
"openToWrite" + requestId, "openToWrite" + "_" + filename,
"openToWrite" + requestId, "openToWrite" + "_" + filename,
"openToWriteEnd" + requestId, "openToWriteEnd" + "_" + filename,
); );
cb(Effect.succeed(event.data.handle)); cb(Effect.succeed({id: event.data.handle, filename}));
}); });
this.opfsWorker.postMessage({ this.opfsWorker.postMessage({
type: "openToWrite", type: "openToWrite",
@@ -118,24 +118,24 @@ export class OPFSFilesystem implements FileSystem<number, number> {
} }
append( append(
handle: number, handle: {id: number, filename: string},
data: Uint8Array, data: Uint8Array,
): Effect.Effect<void, FSErr, never> { ): Effect.Effect<void, FSErr, never> {
return Effect.async((cb) => { return Effect.async((cb) => {
const requestId = this.nextRequestId++; const requestId = this.nextRequestId++;
performance.mark("append" + requestId); performance.mark("append" + "_" + handle.filename);
this.callbacks.set(requestId, (_) => { this.callbacks.set(requestId, (_) => {
performance.mark("appendEnd" + requestId); performance.mark("appendEnd" + "_" + handle.filename);
performance.measure( performance.measure(
"append" + requestId, "append" + "_" + handle.filename,
"append" + requestId, "append" + "_" + handle.filename,
"appendEnd" + requestId, "appendEnd" + "_" + handle.filename,
); );
cb(Effect.succeed(undefined)); cb(Effect.succeed(undefined));
}); });
this.opfsWorker.postMessage({ this.opfsWorker.postMessage({
type: "append", type: "append",
handle, handle: handle.id,
data, data,
requestId, requestId,
}); });
@@ -143,25 +143,25 @@ export class OPFSFilesystem implements FileSystem<number, number> {
} }
read( read(
handle: number, handle: {id: number, filename: string},
offset: number, offset: number,
length: number, length: number,
): Effect.Effect<Uint8Array, FSErr, never> { ): Effect.Effect<Uint8Array, FSErr, never> {
return Effect.async((cb) => { return Effect.async((cb) => {
const requestId = this.nextRequestId++; const requestId = this.nextRequestId++;
performance.mark("read" + requestId); performance.mark("read" + "_" + handle.filename);
this.callbacks.set(requestId, (event) => { this.callbacks.set(requestId, (event) => {
performance.mark("readEnd" + requestId); performance.mark("readEnd" + "_" + handle.filename);
performance.measure( performance.measure(
"read" + requestId, "read" + "_" + handle.filename,
"read" + requestId, "read" + "_" + handle.filename,
"readEnd" + requestId, "readEnd" + "_" + handle.filename,
); );
cb(Effect.succeed(event.data.data)); cb(Effect.succeed(event.data.data));
}); });
this.opfsWorker.postMessage({ this.opfsWorker.postMessage({
type: "read", type: "read",
handle, handle: handle.id,
offset, offset,
length, length,
requestId, requestId,
@@ -169,46 +169,48 @@ export class OPFSFilesystem implements FileSystem<number, number> {
}); });
} }
close(handle: number): Effect.Effect<void, FSErr, never> { close(handle: {id: number, filename: string}): Effect.Effect<void, FSErr, never> {
return Effect.async((cb) => { return Effect.async((cb) => {
const requestId = this.nextRequestId++; const requestId = this.nextRequestId++;
performance.mark("close" + requestId); performance.mark("close" + "_" + handle.filename);
this.callbacks.set(requestId, (_) => { this.callbacks.set(requestId, (_) => {
performance.mark("closeEnd" + requestId); performance.mark("closeEnd" + "_" + handle.filename);
performance.measure( performance.measure(
"close" + requestId, "close" + "_" + handle.filename,
"close" + requestId, "close" + "_" + handle.filename,
"closeEnd" + requestId, "closeEnd" + "_" + handle.filename,
); );
cb(Effect.succeed(undefined)); cb(Effect.succeed(undefined));
}); });
this.opfsWorker.postMessage({ this.opfsWorker.postMessage({
type: "close", type: "close",
handle, handle: handle.id,
requestId, requestId,
}); });
}); });
} }
closeAndRename( closeAndRename(
handle: number, handle: {id: number, filename: string},
filename: BlockFilename, filename: BlockFilename,
): Effect.Effect<void, FSErr, never> { ): Effect.Effect<void, FSErr, never> {
return Effect.async((cb) => { return Effect.async((cb) => {
const requestId = this.nextRequestId++; const requestId = this.nextRequestId++;
performance.mark("closeAndRename" + requestId); performance.mark("closeAndRename" + "_" + handle.filename);
this.callbacks.set(requestId, () => { this.callbacks.set(requestId, () => {
performance.mark("closeAndRenameEnd" + requestId); performance.mark(
"closeAndRenameEnd" + "_" + handle.filename,
);
performance.measure( performance.measure(
"closeAndRename" + requestId, "closeAndRename" + "_" + handle.filename,
"closeAndRename" + requestId, "closeAndRename" + "_" + handle.filename,
"closeAndRenameEnd" + requestId, "closeAndRenameEnd" + "_" + handle.filename,
); );
cb(Effect.succeed(undefined)); cb(Effect.succeed(undefined));
}); });
this.opfsWorker.postMessage({ this.opfsWorker.postMessage({
type: "closeAndRename", type: "closeAndRename",
handle, handle: handle.id,
filename, filename,
requestId, requestId,
}); });
@@ -220,13 +222,13 @@ export class OPFSFilesystem implements FileSystem<number, number> {
): Effect.Effect<void, FSErr, never> { ): Effect.Effect<void, FSErr, never> {
return Effect.async((cb) => { return Effect.async((cb) => {
const requestId = this.nextRequestId++; const requestId = this.nextRequestId++;
performance.mark("removeFile" + requestId); performance.mark("removeFile" + "_" + filename);
this.callbacks.set(requestId, () => { this.callbacks.set(requestId, () => {
performance.mark("removeFileEnd" + requestId); performance.mark("removeFileEnd" + "_" + filename);
performance.measure( performance.measure(
"removeFile" + requestId, "removeFile" + "_" + filename,
"removeFile" + requestId, "removeFile" + "_" + filename,
"removeFileEnd" + requestId, "removeFileEnd" + "_" + filename,
); );
cb(Effect.succeed(undefined)); cb(Effect.succeed(undefined));
}); });

View File

@@ -36,7 +36,7 @@ export async function createJazzBrowserContext<Acc extends Account>({
auth: AuthProvider<Acc>; auth: AuthProvider<Acc>;
peer: `wss://${string}` | `ws://${string}`; peer: `wss://${string}` | `ws://${string}`;
reconnectionTimeout?: number; reconnectionTimeout?: number;
storage?: "indexedDB" | "experimentalOPFSdoNotUseOrYouWillBeFired"; storage?: "indexedDB" | "singleTabOPFS";
crypto?: CryptoProvider; crypto?: CryptoProvider;
}): Promise<BrowserContext<Acc>> { }): Promise<BrowserContext<Acc>> {
const crypto = customCrypto || (await WasmCrypto.create()); const crypto = customCrypto || (await WasmCrypto.create());

View File

@@ -1,5 +1,14 @@
# jazz-autosub # jazz-autosub
## 0.7.23
### Patch Changes
- Updated dependencies
- cojson@0.7.23
- jazz-tools@0.7.23
- cojson-transport-ws@0.7.23
## 0.7.22 ## 0.7.22
### Patch Changes ### Patch Changes

View File

@@ -5,7 +5,7 @@
"types": "src/index.ts", "types": "src/index.ts",
"type": "module", "type": "module",
"license": "MIT", "license": "MIT",
"version": "0.7.22", "version": "0.7.23",
"dependencies": { "dependencies": {
"cojson": "workspace:*", "cojson": "workspace:*",
"cojson-transport-ws": "workspace:*", "cojson-transport-ws": "workspace:*",

View File

@@ -1,5 +1,15 @@
# jazz-react # jazz-react
## 0.7.23
### Patch Changes
- Mostly complete OPFS implementation (single-tab only)
- Updated dependencies
- cojson@0.7.23
- jazz-tools@0.7.23
- jazz-browser@0.7.23
## 0.7.22 ## 0.7.22
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "jazz-react", "name": "jazz-react",
"version": "0.7.22", "version": "0.7.23",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",
"types": "src/index.ts", "types": "src/index.ts",

View File

@@ -23,7 +23,7 @@ export function createJazzReactContext<Acc extends Account>({
}: { }: {
auth: ReactAuthHook<Acc>; auth: ReactAuthHook<Acc>;
peer: `wss://${string}` | `ws://${string}`; peer: `wss://${string}` | `ws://${string}`;
storage?: "indexedDB" | "experimentalOPFSdoNotUseOrYouWillBeFired"; storage?: "indexedDB" | "singleTabOPFS";
}): JazzReactContext<Acc> { }): JazzReactContext<Acc> {
const JazzContext = React.createContext< const JazzContext = React.createContext<
| { | {

View File

@@ -1,5 +1,14 @@
# jazz-autosub # jazz-autosub
## 0.7.23
### Patch Changes
- Updated dependencies
- cojson@0.7.23
- jazz-tools@0.7.23
- cojson-transport-ws@0.7.23
## 0.7.22 ## 0.7.22
### Patch Changes ### Patch Changes

View File

@@ -3,7 +3,7 @@
"bin": "./dist/index.js", "bin": "./dist/index.js",
"type": "module", "type": "module",
"license": "MIT", "license": "MIT",
"version": "0.7.22", "version": "0.7.23",
"scripts": { "scripts": {
"lint": "eslint . --ext ts,tsx", "lint": "eslint . --ext ts,tsx",
"format": "prettier --write './src/**/*.{ts,tsx}'", "format": "prettier --write './src/**/*.{ts,tsx}'",

View File

@@ -1,5 +1,13 @@
# jazz-autosub # jazz-autosub
## 0.7.23
### Patch Changes
- Mostly complete OPFS implementation (single-tab only)
- Updated dependencies
- cojson@0.7.23
## 0.7.21 ## 0.7.21
### Patch Changes ### Patch Changes

View File

@@ -5,7 +5,7 @@
"types": "./src/index.ts", "types": "./src/index.ts",
"type": "module", "type": "module",
"license": "MIT", "license": "MIT",
"version": "0.7.21", "version": "0.7.23",
"dependencies": { "dependencies": {
"@effect/schema": "^0.66.16", "@effect/schema": "^0.66.16",
"cojson": "workspace:*", "cojson": "workspace:*",