cojson 0.7.0-alpha.37 → 0.7.0-alpha.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.cjs +3 -2
- package/.prettierrc.js +9 -0
- package/.turbo/turbo-build.log +1 -34
- package/.turbo/turbo-lint.log +4 -0
- package/.turbo/turbo-test.log +1106 -0
- package/CHANGELOG.md +6 -0
- package/README.md +3 -1
- package/dist/base64url.test.js +25 -0
- package/dist/base64url.test.js.map +1 -0
- package/dist/coValueCore.js +16 -15
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValues/account.js +16 -15
- package/dist/coValues/account.js.map +1 -1
- package/dist/coValues/group.js +13 -14
- package/dist/coValues/group.js.map +1 -1
- package/dist/coreToCoValue.js.map +1 -1
- package/dist/crypto/PureJSCrypto.js +89 -0
- package/dist/crypto/PureJSCrypto.js.map +1 -0
- package/dist/crypto/WasmCrypto.js +127 -0
- package/dist/crypto/WasmCrypto.js.map +1 -0
- package/dist/crypto/crypto.js +151 -0
- package/dist/crypto/crypto.js.map +1 -0
- package/dist/ids.js +4 -2
- package/dist/ids.js.map +1 -1
- package/dist/index.js +4 -8
- package/dist/index.js.map +1 -1
- package/dist/jsonStringify.js.map +1 -1
- package/dist/localNode.js +24 -24
- package/dist/localNode.js.map +1 -1
- package/dist/permissions.js.map +1 -1
- package/dist/storage/FileSystem.js +2 -2
- package/dist/storage/FileSystem.js.map +1 -1
- package/dist/storage/chunksAndKnownStates.js +2 -2
- package/dist/storage/chunksAndKnownStates.js.map +1 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/sync.js +6 -2
- package/dist/sync.js.map +1 -1
- package/dist/tests/account.test.js +58 -0
- package/dist/tests/account.test.js.map +1 -0
- package/dist/tests/coList.test.js +76 -0
- package/dist/tests/coList.test.js.map +1 -0
- package/dist/tests/coMap.test.js +136 -0
- package/dist/tests/coMap.test.js.map +1 -0
- package/dist/tests/coStream.test.js +172 -0
- package/dist/tests/coStream.test.js.map +1 -0
- package/dist/tests/coValueCore.test.js +114 -0
- package/dist/tests/coValueCore.test.js.map +1 -0
- package/dist/tests/crypto.test.js +118 -0
- package/dist/tests/crypto.test.js.map +1 -0
- package/dist/tests/cryptoImpl.test.js +113 -0
- package/dist/tests/cryptoImpl.test.js.map +1 -0
- package/dist/tests/group.test.js +34 -0
- package/dist/tests/group.test.js.map +1 -0
- package/dist/tests/permissions.test.js +1060 -0
- package/dist/tests/permissions.test.js.map +1 -0
- package/dist/tests/sync.test.js +816 -0
- package/dist/tests/sync.test.js.map +1 -0
- package/dist/tests/testUtils.js +10 -9
- package/dist/tests/testUtils.js.map +1 -1
- package/dist/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
- package/dist/typeUtils/isAccountID.js.map +1 -1
- package/dist/typeUtils/isCoValue.js.map +1 -1
- package/package.json +14 -28
- package/src/base64url.test.ts +6 -6
- package/src/coValue.ts +1 -1
- package/src/coValueCore.ts +87 -85
- package/src/coValues/account.ts +26 -28
- package/src/coValues/coList.ts +10 -10
- package/src/coValues/coMap.ts +10 -10
- package/src/coValues/coStream.ts +17 -17
- package/src/coValues/group.ts +93 -109
- package/src/coreToCoValue.ts +5 -2
- package/src/crypto/PureJSCrypto.ts +200 -0
- package/src/crypto/WasmCrypto.ts +259 -0
- package/src/crypto/crypto.ts +336 -0
- package/src/ids.ts +8 -7
- package/src/index.ts +11 -19
- package/src/jsonStringify.ts +6 -4
- package/src/jsonValue.ts +2 -2
- package/src/localNode.ts +86 -80
- package/src/media.ts +3 -3
- package/src/permissions.ts +14 -16
- package/src/storage/FileSystem.ts +31 -30
- package/src/storage/chunksAndKnownStates.ts +24 -17
- package/src/storage/index.ts +41 -37
- package/src/streamUtils.ts +12 -12
- package/src/sync.ts +56 -40
- package/src/tests/account.test.ts +8 -12
- package/src/tests/coList.test.ts +19 -25
- package/src/tests/coMap.test.ts +25 -30
- package/src/tests/coStream.test.ts +28 -38
- package/src/tests/coValueCore.test.ts +35 -36
- package/src/tests/crypto.test.ts +66 -72
- package/src/tests/cryptoImpl.test.ts +183 -0
- package/src/tests/group.test.ts +16 -17
- package/src/tests/permissions.test.ts +237 -254
- package/src/tests/sync.test.ts +119 -120
- package/src/tests/testUtils.ts +22 -19
- package/src/typeUtils/accountOrAgentIDfromSessionID.ts +1 -2
- package/src/typeUtils/expectGroup.ts +1 -1
- package/src/typeUtils/isAccountID.ts +0 -1
- package/src/typeUtils/isCoValue.ts +1 -2
- package/tsconfig.json +0 -1
- package/dist/crypto.js +0 -255
- package/dist/crypto.js.map +0 -1
- package/src/crypto.ts +0 -485
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { CoValueChunk } from "./index.js";
|
|
3
3
|
import { RawCoID } from "../ids.js";
|
|
4
|
-
import { StreamingHash } from "../crypto.js";
|
|
4
|
+
import { CryptoProvider, StreamingHash } from "../crypto/crypto.js";
|
|
5
5
|
|
|
6
|
-
export type BlockFilename =
|
|
7
|
-
`${string}-L${number}-H${number}.jsonl`;
|
|
6
|
+
export type BlockFilename = `${string}-L${number}-H${number}.jsonl`;
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
export type BlockHeader = { id: RawCoID; start: number; length: number }[];
|
|
10
9
|
|
|
11
10
|
export type WalEntry = { id: RawCoID } & CoValueChunk;
|
|
12
11
|
|
|
@@ -18,25 +17,26 @@ export type FSErr = {
|
|
|
18
17
|
};
|
|
19
18
|
|
|
20
19
|
export interface FileSystem<WriteHandle, ReadHandle> {
|
|
20
|
+
crypto: CryptoProvider;
|
|
21
21
|
createFile(filename: string): Effect.Effect<WriteHandle, FSErr>;
|
|
22
22
|
append(handle: WriteHandle, data: Uint8Array): Effect.Effect<void, FSErr>;
|
|
23
|
-
close(
|
|
24
|
-
handle: ReadHandle | WriteHandle,
|
|
25
|
-
): Effect.Effect<void, FSErr>;
|
|
23
|
+
close(handle: ReadHandle | WriteHandle): Effect.Effect<void, FSErr>;
|
|
26
24
|
closeAndRename(
|
|
27
25
|
handle: WriteHandle,
|
|
28
|
-
filename: BlockFilename
|
|
26
|
+
filename: BlockFilename,
|
|
29
27
|
): Effect.Effect<void, FSErr>;
|
|
30
28
|
openToRead(
|
|
31
|
-
filename: string
|
|
32
|
-
): Effect.Effect<{ handle: ReadHandle; size: number
|
|
29
|
+
filename: string,
|
|
30
|
+
): Effect.Effect<{ handle: ReadHandle; size: number }, FSErr>;
|
|
33
31
|
read(
|
|
34
32
|
handle: ReadHandle,
|
|
35
33
|
offset: number,
|
|
36
|
-
length: number
|
|
34
|
+
length: number,
|
|
37
35
|
): Effect.Effect<Uint8Array, FSErr>;
|
|
38
36
|
listFiles(): Effect.Effect<string[], FSErr>;
|
|
39
|
-
removeFile(
|
|
37
|
+
removeFile(
|
|
38
|
+
filename: BlockFilename | WalFilename,
|
|
39
|
+
): Effect.Effect<void, FSErr>;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export const textEncoder = new TextEncoder();
|
|
@@ -44,12 +44,12 @@ export const textDecoder = new TextDecoder();
|
|
|
44
44
|
|
|
45
45
|
export function readChunk<RH, FS extends FileSystem<unknown, RH>>(
|
|
46
46
|
handle: RH,
|
|
47
|
-
header: { start: number; length: number
|
|
48
|
-
fs: FS
|
|
47
|
+
header: { start: number; length: number },
|
|
48
|
+
fs: FS,
|
|
49
49
|
): Effect.Effect<CoValueChunk, FSErr> {
|
|
50
50
|
return Effect.gen(function* ($) {
|
|
51
51
|
const chunkBytes = yield* $(
|
|
52
|
-
fs.read(handle, header.start, header.length)
|
|
52
|
+
fs.read(handle, header.start, header.length),
|
|
53
53
|
);
|
|
54
54
|
|
|
55
55
|
const chunk = JSON.parse(textDecoder.decode(chunkBytes));
|
|
@@ -61,15 +61,13 @@ export function readHeader<RH, FS extends FileSystem<unknown, RH>>(
|
|
|
61
61
|
filename: string,
|
|
62
62
|
handle: RH,
|
|
63
63
|
size: number,
|
|
64
|
-
fs: FS
|
|
64
|
+
fs: FS,
|
|
65
65
|
): Effect.Effect<BlockHeader, FSErr> {
|
|
66
66
|
return Effect.gen(function* ($) {
|
|
67
|
-
|
|
68
|
-
|
|
69
67
|
const headerLength = Number(filename.match(/-H(\d+)\.jsonl$/)![1]!);
|
|
70
68
|
|
|
71
69
|
const headerBytes = yield* $(
|
|
72
|
-
fs.read(handle, size - headerLength, headerLength)
|
|
70
|
+
fs.read(handle, size - headerLength, headerLength),
|
|
73
71
|
);
|
|
74
72
|
|
|
75
73
|
const header = JSON.parse(textDecoder.decode(headerBytes));
|
|
@@ -80,7 +78,7 @@ export function readHeader<RH, FS extends FileSystem<unknown, RH>>(
|
|
|
80
78
|
export function writeBlock<WH, RH, FS extends FileSystem<WH, RH>>(
|
|
81
79
|
chunks: Map<RawCoID, CoValueChunk>,
|
|
82
80
|
level: number,
|
|
83
|
-
fs: FS
|
|
81
|
+
fs: FS,
|
|
84
82
|
): Effect.Effect<void, FSErr> {
|
|
85
83
|
if (chunks.size === 0) {
|
|
86
84
|
return Effect.die(new Error("No chunks to write"));
|
|
@@ -94,19 +92,20 @@ export function writeBlock<WH, RH, FS extends FileSystem<WH, RH>>(
|
|
|
94
92
|
const file = yield* $(
|
|
95
93
|
fs.createFile(
|
|
96
94
|
"wipBlock" +
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
)
|
|
95
|
+
Math.random().toString(36).substring(7) +
|
|
96
|
+
".tmp.jsonl",
|
|
97
|
+
),
|
|
100
98
|
);
|
|
101
|
-
const hash = new StreamingHash();
|
|
99
|
+
const hash = new StreamingHash(fs.crypto);
|
|
102
100
|
|
|
103
|
-
const chunksSortedById = Array.from(chunks).sort(([id1], [id2]) =>
|
|
101
|
+
const chunksSortedById = Array.from(chunks).sort(([id1], [id2]) =>
|
|
102
|
+
id1.localeCompare(id2),
|
|
104
103
|
);
|
|
105
104
|
|
|
106
105
|
for (const [id, chunk] of chunksSortedById) {
|
|
107
106
|
const encodedBytes = hash.update(chunk);
|
|
108
107
|
const encodedBytesWithNewline = new Uint8Array(
|
|
109
|
-
encodedBytes.length + 1
|
|
108
|
+
encodedBytes.length + 1,
|
|
110
109
|
);
|
|
111
110
|
encodedBytesWithNewline.set(encodedBytes);
|
|
112
111
|
encodedBytesWithNewline[encodedBytes.length] = 10;
|
|
@@ -122,11 +121,13 @@ export function writeBlock<WH, RH, FS extends FileSystem<WH, RH>>(
|
|
|
122
121
|
console.log(
|
|
123
122
|
"full file",
|
|
124
123
|
yield* $(
|
|
125
|
-
fs.read(file as unknown as RH, 0, offset + headerBytes.length)
|
|
126
|
-
)
|
|
124
|
+
fs.read(file as unknown as RH, 0, offset + headerBytes.length),
|
|
125
|
+
),
|
|
127
126
|
);
|
|
128
127
|
|
|
129
|
-
const filename: BlockFilename = `${hash.digest()}-L${level}-H${
|
|
128
|
+
const filename: BlockFilename = `${hash.digest()}-L${level}-H${
|
|
129
|
+
headerBytes.length
|
|
130
|
+
}.jsonl`;
|
|
130
131
|
console.log("renaming to" + filename);
|
|
131
132
|
yield* $(fs.closeAndRename(file, filename));
|
|
132
133
|
|
|
@@ -138,7 +139,7 @@ export function writeToWal<WH, RH, FS extends FileSystem<WH, RH>>(
|
|
|
138
139
|
handle: WH,
|
|
139
140
|
fs: FS,
|
|
140
141
|
id: RawCoID,
|
|
141
|
-
chunk: CoValueChunk
|
|
142
|
+
chunk: CoValueChunk,
|
|
142
143
|
): Effect.Effect<void, FSErr> {
|
|
143
144
|
return Effect.gen(function* ($) {
|
|
144
145
|
const walEntry: WalEntry = {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Either } from
|
|
2
|
-
import { RawCoID, SessionID } from
|
|
3
|
-
import { MAX_RECOMMENDED_TX_SIZE } from
|
|
1
|
+
import { Either } from "effect";
|
|
2
|
+
import { RawCoID, SessionID } from "../ids.js";
|
|
3
|
+
import { MAX_RECOMMENDED_TX_SIZE } from "../index.js";
|
|
4
4
|
import { CoValueKnownState, NewContentMessage } from "../sync.js";
|
|
5
5
|
import { CoValueChunk } from "./index.js";
|
|
6
6
|
|
|
7
7
|
export function contentSinceChunk(
|
|
8
8
|
id: RawCoID,
|
|
9
9
|
chunk: CoValueChunk,
|
|
10
|
-
known?: CoValueKnownState
|
|
10
|
+
known?: CoValueKnownState,
|
|
11
11
|
): NewContentMessage[] {
|
|
12
12
|
const newContentPieces: NewContentMessage[] = [];
|
|
13
13
|
|
|
@@ -19,7 +19,7 @@ export function contentSinceChunk(
|
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
for (const [sessionID, sessionsEntry] of Object.entries(
|
|
22
|
-
chunk.sessionEntries
|
|
22
|
+
chunk.sessionEntries,
|
|
23
23
|
)) {
|
|
24
24
|
for (const entry of sessionsEntry) {
|
|
25
25
|
const knownStart = known?.sessions[sessionID as SessionID] || 0;
|
|
@@ -29,13 +29,15 @@ export function contentSinceChunk(
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
const actuallyNewTransactions = entry.transactions.slice(
|
|
32
|
-
Math.max(0, knownStart - entry.after)
|
|
32
|
+
Math.max(0, knownStart - entry.after),
|
|
33
33
|
);
|
|
34
34
|
|
|
35
|
-
const newAfter =
|
|
35
|
+
const newAfter =
|
|
36
|
+
entry.after +
|
|
36
37
|
(actuallyNewTransactions.length - entry.transactions.length);
|
|
37
38
|
|
|
38
|
-
let newContentEntry =
|
|
39
|
+
let newContentEntry =
|
|
40
|
+
newContentPieces[0]?.new[sessionID as SessionID];
|
|
39
41
|
|
|
40
42
|
if (!newContentEntry) {
|
|
41
43
|
newContentEntry = {
|
|
@@ -47,7 +49,7 @@ export function contentSinceChunk(
|
|
|
47
49
|
newContentEntry;
|
|
48
50
|
} else {
|
|
49
51
|
newContentEntry.newTransactions.push(
|
|
50
|
-
...actuallyNewTransactions
|
|
52
|
+
...actuallyNewTransactions,
|
|
51
53
|
);
|
|
52
54
|
newContentEntry.lastSignature = entry.lastSignature;
|
|
53
55
|
}
|
|
@@ -65,7 +67,7 @@ export function chunkToKnownState(id: RawCoID, chunk: CoValueChunk) {
|
|
|
65
67
|
};
|
|
66
68
|
|
|
67
69
|
for (const [sessionID, sessionEntries] of Object.entries(
|
|
68
|
-
chunk.sessionEntries
|
|
70
|
+
chunk.sessionEntries,
|
|
69
71
|
)) {
|
|
70
72
|
for (const entry of sessionEntries) {
|
|
71
73
|
ourKnown.sessions[sessionID as SessionID] =
|
|
@@ -77,7 +79,7 @@ export function chunkToKnownState(id: RawCoID, chunk: CoValueChunk) {
|
|
|
77
79
|
|
|
78
80
|
export function mergeChunks(
|
|
79
81
|
chunkA: CoValueChunk,
|
|
80
|
-
chunkB: CoValueChunk
|
|
82
|
+
chunkB: CoValueChunk,
|
|
81
83
|
): Either.Either<"nonContigous", CoValueChunk> {
|
|
82
84
|
const header = chunkA.header || chunkB.header;
|
|
83
85
|
|
|
@@ -95,21 +97,26 @@ export function mergeChunks(
|
|
|
95
97
|
const lastEntryOfA = sessionEntriesA[sessionEntriesA.length - 1]!;
|
|
96
98
|
const firstEntryOfB = sessionEntriesB[0]!;
|
|
97
99
|
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
+
if (
|
|
101
|
+
lastEntryOfA.after + lastEntryOfA.transactions.length ===
|
|
102
|
+
firstEntryOfB.after
|
|
103
|
+
) {
|
|
100
104
|
const newEntries = [];
|
|
101
105
|
let bytesSinceLastSignature = 0;
|
|
102
106
|
for (const entry of sessionEntriesA.concat(sessionEntriesB)) {
|
|
103
107
|
const entryByteLength = entry.transactions.reduce(
|
|
104
|
-
(sum, tx) =>
|
|
108
|
+
(sum, tx) =>
|
|
109
|
+
sum +
|
|
105
110
|
(tx.privacy === "private"
|
|
106
111
|
? tx.encryptedChanges.length
|
|
107
112
|
: tx.changes.length),
|
|
108
|
-
0
|
|
113
|
+
0,
|
|
109
114
|
);
|
|
110
|
-
if (
|
|
115
|
+
if (
|
|
116
|
+
newEntries.length === 0 ||
|
|
111
117
|
bytesSinceLastSignature + entryByteLength >
|
|
112
|
-
|
|
118
|
+
MAX_RECOMMENDED_TX_SIZE
|
|
119
|
+
) {
|
|
113
120
|
newEntries.push({
|
|
114
121
|
after: entry.after,
|
|
115
122
|
lastSignature: entry.lastSignature,
|
package/src/storage/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import { Effect, Either, SynchronizedRef } from "effect";
|
|
8
8
|
import { RawCoID } from "../ids.js";
|
|
9
9
|
import { CoValueHeader, Transaction } from "../coValueCore.js";
|
|
10
|
-
import { Signature } from "../crypto.js";
|
|
10
|
+
import { Signature } from "../crypto/crypto.js";
|
|
11
11
|
import {
|
|
12
12
|
CoValueKnownState,
|
|
13
13
|
NewContentMessage,
|
|
@@ -63,7 +63,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
63
63
|
constructor(
|
|
64
64
|
fs: FS,
|
|
65
65
|
fromLocalNode: ReadableStream<SyncMessage>,
|
|
66
|
-
toLocalNode: WritableStream<SyncMessage
|
|
66
|
+
toLocalNode: WritableStream<SyncMessage>,
|
|
67
67
|
) {
|
|
68
68
|
this.fs = fs;
|
|
69
69
|
this.fromLocalNode = fromLocalNode.getReader();
|
|
@@ -76,7 +76,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
76
76
|
let done = false;
|
|
77
77
|
while (!done) {
|
|
78
78
|
const result = yield* Effect.promise(() =>
|
|
79
|
-
this.fromLocalNode.read()
|
|
79
|
+
this.fromLocalNode.read(),
|
|
80
80
|
);
|
|
81
81
|
done = result.done;
|
|
82
82
|
|
|
@@ -91,14 +91,14 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
91
91
|
yield* this.sendNewContent(
|
|
92
92
|
result.value.id,
|
|
93
93
|
result.value,
|
|
94
|
-
undefined
|
|
94
|
+
undefined,
|
|
95
95
|
);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
return;
|
|
101
|
-
})
|
|
101
|
+
}),
|
|
102
102
|
);
|
|
103
103
|
|
|
104
104
|
setTimeout(() => this.compact(), 20000);
|
|
@@ -107,10 +107,10 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
107
107
|
sendNewContent(
|
|
108
108
|
id: RawCoID,
|
|
109
109
|
known: CoValueKnownState | undefined,
|
|
110
|
-
asDependencyOf: RawCoID | undefined
|
|
110
|
+
asDependencyOf: RawCoID | undefined,
|
|
111
111
|
): Effect.Effect<void, FSErr> {
|
|
112
112
|
return SynchronizedRef.updateEffect(this.coValues, (coValues) =>
|
|
113
|
-
this.sendNewContentInner(coValues, id, known, asDependencyOf)
|
|
113
|
+
this.sendNewContentInner(coValues, id, known, asDependencyOf),
|
|
114
114
|
);
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -118,7 +118,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
118
118
|
coValues: { [id: `co_z${string}`]: CoValueChunk | undefined },
|
|
119
119
|
id: RawCoID,
|
|
120
120
|
known: CoValueKnownState | undefined,
|
|
121
|
-
asDependencyOf: RawCoID | undefined
|
|
121
|
+
asDependencyOf: RawCoID | undefined,
|
|
122
122
|
): Effect.Effect<
|
|
123
123
|
{ [id: `co_z${string}`]: CoValueChunk | undefined },
|
|
124
124
|
FSErr,
|
|
@@ -139,7 +139,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
139
139
|
header: false,
|
|
140
140
|
sessions: {},
|
|
141
141
|
asDependencyOf,
|
|
142
|
-
})
|
|
142
|
+
}),
|
|
143
143
|
);
|
|
144
144
|
|
|
145
145
|
return coValues;
|
|
@@ -153,7 +153,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
153
153
|
coValues,
|
|
154
154
|
coValue.header.ruleset.group,
|
|
155
155
|
undefined,
|
|
156
|
-
asDependencyOf || id
|
|
156
|
+
asDependencyOf || id,
|
|
157
157
|
);
|
|
158
158
|
} else if (
|
|
159
159
|
!known?.header &&
|
|
@@ -182,7 +182,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
182
182
|
coValues,
|
|
183
183
|
account as CoID<RawCoValue>,
|
|
184
184
|
undefined,
|
|
185
|
-
asDependencyOf || id
|
|
185
|
+
asDependencyOf || id,
|
|
186
186
|
);
|
|
187
187
|
}
|
|
188
188
|
}
|
|
@@ -190,7 +190,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
190
190
|
const newContentMessages = contentSinceChunk(
|
|
191
191
|
id,
|
|
192
192
|
coValue,
|
|
193
|
-
known
|
|
193
|
+
known,
|
|
194
194
|
).map((message) => ({ ...message, asDependencyOf }));
|
|
195
195
|
|
|
196
196
|
const ourKnown: CoValueKnownState = chunkToKnownState(id, coValue);
|
|
@@ -200,7 +200,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
200
200
|
action: "known",
|
|
201
201
|
...ourKnown,
|
|
202
202
|
asDependencyOf,
|
|
203
|
-
})
|
|
203
|
+
}),
|
|
204
204
|
);
|
|
205
205
|
|
|
206
206
|
for (const message of newContentMessages) {
|
|
@@ -213,7 +213,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
withWAL(
|
|
216
|
-
handler: (wal: WH) => Effect.Effect<void, FSErr
|
|
216
|
+
handler: (wal: WH) => Effect.Effect<void, FSErr>,
|
|
217
217
|
): Effect.Effect<void, FSErr> {
|
|
218
218
|
return SynchronizedRef.updateEffect(this.currentWal, (wal) =>
|
|
219
219
|
Effect.gen(this, function* () {
|
|
@@ -222,17 +222,17 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
222
222
|
newWal = yield* this.fs.createFile(
|
|
223
223
|
`wal-${new Date().toISOString()}-${Math.random()
|
|
224
224
|
.toString(36)
|
|
225
|
-
.slice(2)}.jsonl
|
|
225
|
+
.slice(2)}.jsonl`,
|
|
226
226
|
);
|
|
227
227
|
}
|
|
228
228
|
yield* handler(newWal);
|
|
229
229
|
return newWal;
|
|
230
|
-
})
|
|
230
|
+
}),
|
|
231
231
|
);
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
handleNewContent(
|
|
235
|
-
newContent: NewContentMessage
|
|
235
|
+
newContent: NewContentMessage,
|
|
236
236
|
): Effect.Effect<void, FSErr> {
|
|
237
237
|
return SynchronizedRef.updateEffect(this.coValues, (coValues) =>
|
|
238
238
|
Effect.gen(this, function* () {
|
|
@@ -253,8 +253,8 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
253
253
|
newInSession.newTransactions,
|
|
254
254
|
},
|
|
255
255
|
],
|
|
256
|
-
]
|
|
257
|
-
)
|
|
256
|
+
],
|
|
257
|
+
),
|
|
258
258
|
),
|
|
259
259
|
};
|
|
260
260
|
|
|
@@ -266,8 +266,8 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
266
266
|
wal,
|
|
267
267
|
this.fs,
|
|
268
268
|
newContent.id,
|
|
269
|
-
newContentAsChunk
|
|
270
|
-
)
|
|
269
|
+
newContentAsChunk,
|
|
270
|
+
),
|
|
271
271
|
);
|
|
272
272
|
|
|
273
273
|
return {
|
|
@@ -287,7 +287,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
287
287
|
// )
|
|
288
288
|
// );
|
|
289
289
|
console.warn(
|
|
290
|
-
"Incontiguous incoming update for " + newContent.id
|
|
290
|
+
"Incontiguous incoming update for " + newContent.id,
|
|
291
291
|
);
|
|
292
292
|
return coValues;
|
|
293
293
|
}
|
|
@@ -295,7 +295,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
295
295
|
const merged = mergeChunks(coValue, newContentAsChunk);
|
|
296
296
|
if (Either.isRight(merged)) {
|
|
297
297
|
yield* Effect.logWarning(
|
|
298
|
-
"Non-contigous new content for " + newContent.id
|
|
298
|
+
"Non-contigous new content for " + newContent.id,
|
|
299
299
|
);
|
|
300
300
|
|
|
301
301
|
// yield* Effect.promise(() =>
|
|
@@ -314,27 +314,27 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
314
314
|
wal,
|
|
315
315
|
this.fs,
|
|
316
316
|
newContent.id,
|
|
317
|
-
newContentAsChunk
|
|
318
|
-
)
|
|
317
|
+
newContentAsChunk,
|
|
318
|
+
),
|
|
319
319
|
);
|
|
320
320
|
|
|
321
321
|
return { ...coValues, [newContent.id]: merged.left };
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
|
-
})
|
|
324
|
+
}),
|
|
325
325
|
);
|
|
326
326
|
}
|
|
327
327
|
|
|
328
328
|
loadCoValue<WH, RH, FS extends FileSystem<WH, RH>>(
|
|
329
329
|
id: RawCoID,
|
|
330
|
-
fs: FS
|
|
330
|
+
fs: FS,
|
|
331
331
|
): Effect.Effect<CoValueChunk | undefined, FSErr> {
|
|
332
332
|
// return _loadChunkFromWal(id, fs);
|
|
333
333
|
return Effect.gen(this, function* () {
|
|
334
334
|
const files = this.fileCache || (yield* fs.listFiles());
|
|
335
335
|
this.fileCache = files;
|
|
336
336
|
const blockFiles = files.filter((name) =>
|
|
337
|
-
name.startsWith("hash_")
|
|
337
|
+
name.startsWith("hash_"),
|
|
338
338
|
) as BlockFilename[];
|
|
339
339
|
|
|
340
340
|
for (const blockFile of blockFiles) {
|
|
@@ -346,7 +346,12 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
346
346
|
|
|
347
347
|
if (!cachedHeader) {
|
|
348
348
|
cachedHeader = {};
|
|
349
|
-
const header = yield* readHeader(
|
|
349
|
+
const header = yield* readHeader(
|
|
350
|
+
blockFile,
|
|
351
|
+
handle,
|
|
352
|
+
size,
|
|
353
|
+
fs,
|
|
354
|
+
);
|
|
350
355
|
for (const entry of header) {
|
|
351
356
|
cachedHeader[entry.id] = {
|
|
352
357
|
start: entry.start,
|
|
@@ -361,12 +366,11 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
361
366
|
let result;
|
|
362
367
|
if (headerEntry) {
|
|
363
368
|
result = yield* readChunk(handle, headerEntry, fs);
|
|
364
|
-
|
|
365
369
|
}
|
|
366
370
|
|
|
367
371
|
yield* fs.close(handle);
|
|
368
372
|
|
|
369
|
-
return result
|
|
373
|
+
return result;
|
|
370
374
|
}
|
|
371
375
|
|
|
372
376
|
return undefined;
|
|
@@ -379,7 +383,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
379
383
|
const fileNames = yield* this.fs.listFiles();
|
|
380
384
|
|
|
381
385
|
const walFiles = fileNames.filter((name) =>
|
|
382
|
-
name.startsWith("wal-")
|
|
386
|
+
name.startsWith("wal-"),
|
|
383
387
|
) as WalFilename[];
|
|
384
388
|
walFiles.sort();
|
|
385
389
|
|
|
@@ -394,7 +398,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
394
398
|
yield* this.fs.close(wal);
|
|
395
399
|
}
|
|
396
400
|
return undefined;
|
|
397
|
-
})
|
|
401
|
+
}),
|
|
398
402
|
);
|
|
399
403
|
|
|
400
404
|
for (const fileName of walFiles) {
|
|
@@ -402,7 +406,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
402
406
|
yield* this.fs.openToRead(fileName);
|
|
403
407
|
if (size === 0) {
|
|
404
408
|
yield* this.fs.close(handle);
|
|
405
|
-
continue
|
|
409
|
+
continue;
|
|
406
410
|
}
|
|
407
411
|
const bytes = yield* this.fs.read(handle, 0, size);
|
|
408
412
|
|
|
@@ -424,7 +428,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
424
428
|
", " +
|
|
425
429
|
fileName,
|
|
426
430
|
existingChunk,
|
|
427
|
-
chunk
|
|
431
|
+
chunk,
|
|
428
432
|
);
|
|
429
433
|
} else {
|
|
430
434
|
coValues.set(chunk.id, merged.left);
|
|
@@ -442,7 +446,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
442
446
|
yield* this.fs.removeFile(walFile);
|
|
443
447
|
}
|
|
444
448
|
this.fileCache = undefined;
|
|
445
|
-
})
|
|
449
|
+
}),
|
|
446
450
|
);
|
|
447
451
|
|
|
448
452
|
setTimeout(() => this.compact(), 5000);
|
|
@@ -464,7 +468,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
|
464
468
|
peer1role: "client",
|
|
465
469
|
peer2role: "server",
|
|
466
470
|
trace,
|
|
467
|
-
}
|
|
471
|
+
},
|
|
468
472
|
);
|
|
469
473
|
|
|
470
474
|
new LSMStorage(fs, localNodeAsPeer.incoming, localNodeAsPeer.outgoing);
|
package/src/streamUtils.ts
CHANGED
|
@@ -16,7 +16,7 @@ export function connectedPeers(
|
|
|
16
16
|
trace?: boolean;
|
|
17
17
|
peer1role?: Peer["role"];
|
|
18
18
|
peer2role?: Peer["role"];
|
|
19
|
-
} = {}
|
|
19
|
+
} = {},
|
|
20
20
|
): [Peer, Peer] {
|
|
21
21
|
const [inRx1, inTx1] = newStreamPair<SyncMessage>(peer1id + "_in");
|
|
22
22
|
const [outRx1, outTx1] = newStreamPair<SyncMessage>(peer1id + "_out");
|
|
@@ -29,7 +29,7 @@ export function connectedPeers(
|
|
|
29
29
|
new TransformStream({
|
|
30
30
|
transform(
|
|
31
31
|
chunk: SyncMessage,
|
|
32
|
-
controller: { enqueue: (msg: SyncMessage) => void }
|
|
32
|
+
controller: { enqueue: (msg: SyncMessage) => void },
|
|
33
33
|
) {
|
|
34
34
|
trace &&
|
|
35
35
|
console.debug(
|
|
@@ -40,12 +40,12 @@ export function connectedPeers(
|
|
|
40
40
|
k === "changes" || k === "encryptedChanges"
|
|
41
41
|
? v.slice(0, 20) + "..."
|
|
42
42
|
: v,
|
|
43
|
-
2
|
|
44
|
-
)
|
|
43
|
+
2,
|
|
44
|
+
),
|
|
45
45
|
);
|
|
46
46
|
controller.enqueue(chunk);
|
|
47
47
|
},
|
|
48
|
-
})
|
|
48
|
+
}),
|
|
49
49
|
)
|
|
50
50
|
.pipeTo(inTx1);
|
|
51
51
|
|
|
@@ -54,7 +54,7 @@ export function connectedPeers(
|
|
|
54
54
|
new TransformStream({
|
|
55
55
|
transform(
|
|
56
56
|
chunk: SyncMessage,
|
|
57
|
-
controller: { enqueue: (msg: SyncMessage) => void }
|
|
57
|
+
controller: { enqueue: (msg: SyncMessage) => void },
|
|
58
58
|
) {
|
|
59
59
|
trace &&
|
|
60
60
|
console.debug(
|
|
@@ -65,12 +65,12 @@ export function connectedPeers(
|
|
|
65
65
|
k === "changes" || k === "encryptedChanges"
|
|
66
66
|
? v.slice(0, 20) + "..."
|
|
67
67
|
: v,
|
|
68
|
-
2
|
|
69
|
-
)
|
|
68
|
+
2,
|
|
69
|
+
),
|
|
70
70
|
);
|
|
71
71
|
controller.enqueue(chunk);
|
|
72
72
|
},
|
|
73
|
-
})
|
|
73
|
+
}),
|
|
74
74
|
)
|
|
75
75
|
.pipeTo(inTx2);
|
|
76
76
|
|
|
@@ -92,7 +92,7 @@ export function connectedPeers(
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
export function newStreamPair<T>(
|
|
95
|
-
pairName?: string
|
|
95
|
+
pairName?: string,
|
|
96
96
|
): [ReadableStream<T>, WritableStream<T>] {
|
|
97
97
|
let queueLength = 0;
|
|
98
98
|
let readerClosed = false;
|
|
@@ -138,13 +138,13 @@ export function newStreamPair<T>(
|
|
|
138
138
|
new TransformStream<any, any>({
|
|
139
139
|
transform(
|
|
140
140
|
chunk: SyncMessage,
|
|
141
|
-
controller: { enqueue: (msg: SyncMessage) => void }
|
|
141
|
+
controller: { enqueue: (msg: SyncMessage) => void },
|
|
142
142
|
) {
|
|
143
143
|
queueLength -= 1;
|
|
144
144
|
maybeReportQueueLength();
|
|
145
145
|
controller.enqueue(chunk);
|
|
146
146
|
},
|
|
147
|
-
})
|
|
147
|
+
}),
|
|
148
148
|
) as ReadableStream<T>;
|
|
149
149
|
|
|
150
150
|
let lastWritePromise = Promise.resolve();
|