cojson 0.0.10 → 0.0.12
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/README.md +2 -2
- package/dist/account.d.ts +57 -0
- package/dist/account.js +76 -0
- package/dist/account.js.map +1 -0
- package/dist/account.test.d.ts +1 -0
- package/dist/account.test.js +40 -0
- package/dist/account.test.js.map +1 -0
- package/dist/coValue.d.ts +16 -35
- package/dist/coValue.js +49 -112
- package/dist/coValue.js.map +1 -1
- package/dist/coValue.test.js +16 -16
- package/dist/coValue.test.js.map +1 -1
- package/dist/contentType.d.ts +9 -9
- package/dist/contentType.js.map +1 -1
- package/dist/contentType.test.js +13 -17
- package/dist/contentType.test.js.map +1 -1
- package/dist/contentTypes/coList.d.ts +3 -3
- package/dist/contentTypes/coList.js.map +1 -1
- package/dist/contentTypes/coMap.d.ts +31 -21
- package/dist/contentTypes/coMap.js +28 -0
- package/dist/contentTypes/coMap.js.map +1 -1
- package/dist/contentTypes/coStream.d.ts +3 -3
- package/dist/contentTypes/coStream.js.map +1 -1
- package/dist/contentTypes/static.d.ts +4 -4
- package/dist/contentTypes/static.js.map +1 -1
- package/dist/crypto.d.ts +45 -39
- package/dist/crypto.js +68 -49
- package/dist/crypto.js.map +1 -1
- package/dist/crypto.test.js +45 -49
- package/dist/crypto.test.js.map +1 -1
- package/dist/ids.d.ts +5 -3
- package/dist/ids.js +3 -1
- package/dist/ids.js.map +1 -1
- package/dist/index.d.ts +12 -14
- package/dist/index.js +6 -8
- package/dist/index.js.map +1 -1
- package/dist/jsonValue.d.ts +2 -2
- package/dist/node.d.ts +25 -15
- package/dist/node.js +88 -33
- package/dist/node.js.map +1 -1
- package/dist/permissions.d.ts +27 -33
- package/dist/permissions.js +55 -47
- package/dist/permissions.js.map +1 -1
- package/dist/permissions.test.js +231 -314
- package/dist/permissions.test.js.map +1 -1
- package/dist/sync.d.ts +26 -28
- package/dist/sync.js +81 -67
- package/dist/sync.js.map +1 -1
- package/dist/sync.test.js +194 -293
- package/dist/sync.test.js.map +1 -1
- package/dist/testUtils.d.ts +37 -0
- package/dist/testUtils.js +157 -0
- package/dist/testUtils.js.map +1 -0
- package/package.json +1 -1
- package/src/account.test.ts +67 -0
- package/src/account.ts +152 -0
- package/src/coValue.test.ts +17 -31
- package/src/coValue.ts +93 -179
- package/src/contentType.test.ts +18 -45
- package/src/contentType.ts +15 -13
- package/src/contentTypes/coList.ts +4 -4
- package/src/contentTypes/coMap.ts +55 -29
- package/src/contentTypes/coStream.ts +4 -4
- package/src/contentTypes/static.ts +5 -5
- package/src/crypto.test.ts +53 -59
- package/src/crypto.ts +123 -95
- package/src/ids.ts +9 -3
- package/src/index.ts +14 -25
- package/src/jsonValue.ts +2 -2
- package/src/node.ts +189 -61
- package/src/permissions.test.ts +370 -404
- package/src/permissions.ts +126 -109
- package/src/sync.test.ts +284 -426
- package/src/sync.ts +115 -105
- package/src/testUtils.ts +229 -0
package/src/sync.test.ts
CHANGED
|
@@ -1,115 +1,102 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from
|
|
7
|
-
import { LocalNode } from './node.js';
|
|
8
|
-
import { Peer, PeerID, SyncMessage } from './sync.js';
|
|
9
|
-
import { expectMap } from './contentType.js';
|
|
10
|
-
import { MapOpPayload } from './contentTypes/coMap.js';
|
|
11
|
-
import { Team } from './permissions.js';
|
|
1
|
+
import { newRandomSessionID } from "./coValue.js";
|
|
2
|
+
import { LocalNode } from "./node.js";
|
|
3
|
+
import { Peer, PeerID, SyncMessage } from "./sync.js";
|
|
4
|
+
import { expectMap } from "./contentType.js";
|
|
5
|
+
import { MapOpPayload } from "./contentTypes/coMap.js";
|
|
6
|
+
import { Team } from "./permissions.js";
|
|
12
7
|
import {
|
|
13
8
|
ReadableStream,
|
|
14
9
|
WritableStream,
|
|
15
10
|
TransformStream,
|
|
16
11
|
} from "isomorphic-streams";
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
test(
|
|
20
|
-
"Node replies with initial tx and header to empty subscribe",
|
|
21
|
-
async () => {
|
|
22
|
-
const admin = newRandomAgentCredential("admin");
|
|
23
|
-
const adminID = getAgentID(getAgent(admin));
|
|
24
|
-
|
|
25
|
-
const node = new LocalNode(admin, newRandomSessionID(adminID));
|
|
12
|
+
import { connectedPeers, newStreamPair, randomAnonymousAccountAndSessionID, shouldNotResolve } from "./testUtils.js";
|
|
13
|
+
import { AccountID } from "./account.js";
|
|
26
14
|
|
|
27
|
-
|
|
15
|
+
test("Node replies with initial tx and header to empty subscribe", async () => {
|
|
16
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
17
|
+
const node = new LocalNode(admin, session);
|
|
28
18
|
|
|
29
|
-
|
|
19
|
+
const team = node.createTeam();
|
|
30
20
|
|
|
31
|
-
|
|
32
|
-
editable.set("hello", "world", "trusting");
|
|
33
|
-
});
|
|
21
|
+
const map = team.createMap();
|
|
34
22
|
|
|
35
|
-
|
|
36
|
-
|
|
23
|
+
map.edit((editable) => {
|
|
24
|
+
editable.set("hello", "world", "trusting");
|
|
25
|
+
});
|
|
37
26
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
incoming: inRx,
|
|
41
|
-
outgoing: outTx,
|
|
42
|
-
role: "peer",
|
|
43
|
-
});
|
|
27
|
+
const [inRx, inTx] = newStreamPair<SyncMessage>();
|
|
28
|
+
const [outRx, outTx] = newStreamPair<SyncMessage>();
|
|
44
29
|
|
|
45
|
-
|
|
30
|
+
node.sync.addPeer({
|
|
31
|
+
id: "test",
|
|
32
|
+
incoming: inRx,
|
|
33
|
+
outgoing: outTx,
|
|
34
|
+
role: "peer",
|
|
35
|
+
});
|
|
46
36
|
|
|
47
|
-
|
|
48
|
-
action: "subscribe",
|
|
49
|
-
coValueID: map.coValue.id,
|
|
50
|
-
header: false,
|
|
51
|
-
sessions: {},
|
|
52
|
-
});
|
|
37
|
+
const writer = inTx.getWriter();
|
|
53
38
|
|
|
54
|
-
|
|
39
|
+
await writer.write({
|
|
40
|
+
action: "load",
|
|
41
|
+
id: map.coValue.id,
|
|
42
|
+
header: false,
|
|
43
|
+
sessions: {},
|
|
44
|
+
});
|
|
55
45
|
|
|
56
|
-
|
|
57
|
-
expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
|
46
|
+
const reader = outRx.getReader();
|
|
58
47
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
action: "tellKnownState",
|
|
62
|
-
...map.coValue.knownState(),
|
|
63
|
-
} satisfies SyncMessage);
|
|
48
|
+
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
|
49
|
+
expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
|
64
50
|
|
|
65
|
-
|
|
66
|
-
|
|
51
|
+
const mapTellKnownStateMsg = await reader.read();
|
|
52
|
+
expect(mapTellKnownStateMsg.value).toEqual({
|
|
53
|
+
action: "known",
|
|
54
|
+
...map.coValue.knownState(),
|
|
55
|
+
} satisfies SyncMessage);
|
|
67
56
|
|
|
68
|
-
|
|
57
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
58
|
+
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
|
69
59
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
},
|
|
60
|
+
const newContentMsg = await reader.read();
|
|
61
|
+
|
|
62
|
+
expect(newContentMsg.value).toEqual({
|
|
63
|
+
action: "content",
|
|
64
|
+
id: map.coValue.id,
|
|
65
|
+
header: {
|
|
66
|
+
type: "comap",
|
|
67
|
+
ruleset: { type: "ownedByTeam", team: team.id },
|
|
68
|
+
meta: null,
|
|
69
|
+
createdAt: map.coValue.header.createdAt,
|
|
70
|
+
uniqueness: map.coValue.header.uniqueness,
|
|
71
|
+
},
|
|
72
|
+
new: {
|
|
73
|
+
[node.ownSessionID]: {
|
|
74
|
+
after: 0,
|
|
75
|
+
newTransactions: [
|
|
76
|
+
{
|
|
77
|
+
privacy: "trusting" as const,
|
|
78
|
+
madeAt: map.coValue.sessions[node.ownSessionID]!
|
|
79
|
+
.transactions[0]!.madeAt,
|
|
80
|
+
changes: [
|
|
81
|
+
{
|
|
82
|
+
op: "insert",
|
|
83
|
+
key: "hello",
|
|
84
|
+
value: "world",
|
|
85
|
+
} satisfies MapOpPayload<string, string>,
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
lastHash: map.coValue.sessions[node.ownSessionID]!.lastHash!,
|
|
90
|
+
lastSignature:
|
|
91
|
+
map.coValue.sessions[node.ownSessionID]!.lastSignature!,
|
|
103
92
|
},
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
);
|
|
93
|
+
},
|
|
94
|
+
} satisfies SyncMessage);
|
|
95
|
+
});
|
|
107
96
|
|
|
108
97
|
test("Node replies with only new tx to subscribe with some known state", async () => {
|
|
109
|
-
const admin =
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
const node = new LocalNode(admin, newRandomSessionID(adminID));
|
|
98
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
99
|
+
const node = new LocalNode(admin, session);
|
|
113
100
|
|
|
114
101
|
const team = node.createTeam();
|
|
115
102
|
|
|
@@ -133,8 +120,8 @@ test("Node replies with only new tx to subscribe with some known state", async (
|
|
|
133
120
|
const writer = inTx.getWriter();
|
|
134
121
|
|
|
135
122
|
await writer.write({
|
|
136
|
-
action: "
|
|
137
|
-
|
|
123
|
+
action: "load",
|
|
124
|
+
id: map.coValue.id,
|
|
138
125
|
header: true,
|
|
139
126
|
sessions: {
|
|
140
127
|
[node.ownSessionID]: 1,
|
|
@@ -143,30 +130,30 @@ test("Node replies with only new tx to subscribe with some known state", async (
|
|
|
143
130
|
|
|
144
131
|
const reader = outRx.getReader();
|
|
145
132
|
|
|
146
|
-
expect((await reader.read()).value).toMatchObject(admStateEx(
|
|
133
|
+
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
|
147
134
|
expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
|
148
135
|
|
|
149
136
|
const mapTellKnownStateMsg = await reader.read();
|
|
150
137
|
expect(mapTellKnownStateMsg.value).toEqual({
|
|
151
|
-
action: "
|
|
138
|
+
action: "known",
|
|
152
139
|
...map.coValue.knownState(),
|
|
153
140
|
} satisfies SyncMessage);
|
|
154
141
|
|
|
155
|
-
expect((await reader.read()).value).toMatchObject(admContEx(
|
|
142
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
156
143
|
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
|
157
144
|
|
|
158
145
|
const mapNewContentMsg = await reader.read();
|
|
159
146
|
|
|
160
147
|
expect(mapNewContentMsg.value).toEqual({
|
|
161
|
-
action: "
|
|
162
|
-
|
|
148
|
+
action: "content",
|
|
149
|
+
id: map.coValue.id,
|
|
163
150
|
header: undefined,
|
|
164
|
-
|
|
151
|
+
new: {
|
|
165
152
|
[node.ownSessionID]: {
|
|
166
153
|
after: 1,
|
|
167
154
|
newTransactions: [
|
|
168
155
|
{
|
|
169
|
-
privacy: "trusting",
|
|
156
|
+
privacy: "trusting" as const,
|
|
170
157
|
madeAt: map.coValue.sessions[node.ownSessionID]!
|
|
171
158
|
.transactions[1]!.madeAt,
|
|
172
159
|
changes: [
|
|
@@ -187,14 +174,12 @@ test("Node replies with only new tx to subscribe with some known state", async (
|
|
|
187
174
|
});
|
|
188
175
|
|
|
189
176
|
test.todo(
|
|
190
|
-
"TODO: node only replies with new tx to subscribe with some known state, even in the depended on coValues"
|
|
177
|
+
"TODO: node only replies with new tx to subscribe with some known state, even in the depended on coValues"
|
|
191
178
|
);
|
|
192
179
|
|
|
193
180
|
test("After subscribing, node sends own known state and new txs to peer", async () => {
|
|
194
|
-
const admin =
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
const node = new LocalNode(admin, newRandomSessionID(adminID));
|
|
181
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
182
|
+
const node = new LocalNode(admin, session);
|
|
198
183
|
|
|
199
184
|
const team = node.createTeam();
|
|
200
185
|
|
|
@@ -213,8 +198,8 @@ test("After subscribing, node sends own known state and new txs to peer", async
|
|
|
213
198
|
const writer = inTx.getWriter();
|
|
214
199
|
|
|
215
200
|
await writer.write({
|
|
216
|
-
action: "
|
|
217
|
-
|
|
201
|
+
action: "load",
|
|
202
|
+
id: map.coValue.id,
|
|
218
203
|
header: false,
|
|
219
204
|
sessions: {
|
|
220
205
|
[node.ownSessionID]: 0,
|
|
@@ -223,25 +208,25 @@ test("After subscribing, node sends own known state and new txs to peer", async
|
|
|
223
208
|
|
|
224
209
|
const reader = outRx.getReader();
|
|
225
210
|
|
|
226
|
-
expect((await reader.read()).value).toMatchObject(admStateEx(
|
|
211
|
+
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
|
227
212
|
expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
|
228
213
|
|
|
229
214
|
const mapTellKnownStateMsg = await reader.read();
|
|
230
215
|
expect(mapTellKnownStateMsg.value).toEqual({
|
|
231
|
-
action: "
|
|
216
|
+
action: "known",
|
|
232
217
|
...map.coValue.knownState(),
|
|
233
218
|
} satisfies SyncMessage);
|
|
234
219
|
|
|
235
|
-
expect((await reader.read()).value).toMatchObject(admContEx(
|
|
220
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
236
221
|
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
|
237
222
|
|
|
238
223
|
const mapNewContentHeaderOnlyMsg = await reader.read();
|
|
239
224
|
|
|
240
225
|
expect(mapNewContentHeaderOnlyMsg.value).toEqual({
|
|
241
|
-
action: "
|
|
242
|
-
|
|
226
|
+
action: "content",
|
|
227
|
+
id: map.coValue.id,
|
|
243
228
|
header: map.coValue.header,
|
|
244
|
-
|
|
229
|
+
new: {},
|
|
245
230
|
} satisfies SyncMessage);
|
|
246
231
|
|
|
247
232
|
map.edit((editable) => {
|
|
@@ -251,14 +236,14 @@ test("After subscribing, node sends own known state and new txs to peer", async
|
|
|
251
236
|
const mapEditMsg1 = await reader.read();
|
|
252
237
|
|
|
253
238
|
expect(mapEditMsg1.value).toEqual({
|
|
254
|
-
action: "
|
|
255
|
-
|
|
256
|
-
|
|
239
|
+
action: "content",
|
|
240
|
+
id: map.coValue.id,
|
|
241
|
+
new: {
|
|
257
242
|
[node.ownSessionID]: {
|
|
258
243
|
after: 0,
|
|
259
244
|
newTransactions: [
|
|
260
245
|
{
|
|
261
|
-
privacy: "trusting",
|
|
246
|
+
privacy: "trusting" as const,
|
|
262
247
|
madeAt: map.coValue.sessions[node.ownSessionID]!
|
|
263
248
|
.transactions[0]!.madeAt,
|
|
264
249
|
changes: [
|
|
@@ -284,14 +269,14 @@ test("After subscribing, node sends own known state and new txs to peer", async
|
|
|
284
269
|
const mapEditMsg2 = await reader.read();
|
|
285
270
|
|
|
286
271
|
expect(mapEditMsg2.value).toEqual({
|
|
287
|
-
action: "
|
|
288
|
-
|
|
289
|
-
|
|
272
|
+
action: "content",
|
|
273
|
+
id: map.coValue.id,
|
|
274
|
+
new: {
|
|
290
275
|
[node.ownSessionID]: {
|
|
291
276
|
after: 1,
|
|
292
277
|
newTransactions: [
|
|
293
278
|
{
|
|
294
|
-
privacy: "trusting",
|
|
279
|
+
privacy: "trusting" as const,
|
|
295
280
|
madeAt: map.coValue.sessions[node.ownSessionID]!
|
|
296
281
|
.transactions[1]!.madeAt,
|
|
297
282
|
changes: [
|
|
@@ -312,10 +297,8 @@ test("After subscribing, node sends own known state and new txs to peer", async
|
|
|
312
297
|
});
|
|
313
298
|
|
|
314
299
|
test("Client replies with known new content to tellKnownState from server", async () => {
|
|
315
|
-
const admin =
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
const node = new LocalNode(admin, newRandomSessionID(adminID));
|
|
300
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
301
|
+
const node = new LocalNode(admin, session);
|
|
319
302
|
|
|
320
303
|
const team = node.createTeam();
|
|
321
304
|
|
|
@@ -342,38 +325,38 @@ test("Client replies with known new content to tellKnownState from server", asyn
|
|
|
342
325
|
const writer = inTx.getWriter();
|
|
343
326
|
|
|
344
327
|
await writer.write({
|
|
345
|
-
action: "
|
|
346
|
-
|
|
328
|
+
action: "known",
|
|
329
|
+
id: map.coValue.id,
|
|
347
330
|
header: false,
|
|
348
331
|
sessions: {
|
|
349
332
|
[node.ownSessionID]: 0,
|
|
350
333
|
},
|
|
351
334
|
});
|
|
352
335
|
|
|
353
|
-
expect((await reader.read()).value).toMatchObject(admStateEx(
|
|
336
|
+
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
|
354
337
|
expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
|
355
338
|
|
|
356
339
|
const mapTellKnownStateMsg = await reader.read();
|
|
357
340
|
expect(mapTellKnownStateMsg.value).toEqual({
|
|
358
|
-
action: "
|
|
341
|
+
action: "known",
|
|
359
342
|
...map.coValue.knownState(),
|
|
360
343
|
} satisfies SyncMessage);
|
|
361
344
|
|
|
362
|
-
expect((await reader.read()).value).toMatchObject(admContEx(
|
|
345
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
363
346
|
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
|
364
347
|
|
|
365
348
|
const mapNewContentMsg = await reader.read();
|
|
366
349
|
|
|
367
350
|
expect(mapNewContentMsg.value).toEqual({
|
|
368
|
-
action: "
|
|
369
|
-
|
|
351
|
+
action: "content",
|
|
352
|
+
id: map.coValue.id,
|
|
370
353
|
header: map.coValue.header,
|
|
371
|
-
|
|
354
|
+
new: {
|
|
372
355
|
[node.ownSessionID]: {
|
|
373
356
|
after: 0,
|
|
374
357
|
newTransactions: [
|
|
375
358
|
{
|
|
376
|
-
privacy: "trusting",
|
|
359
|
+
privacy: "trusting" as const,
|
|
377
360
|
madeAt: map.coValue.sessions[node.ownSessionID]!
|
|
378
361
|
.transactions[0]!.madeAt,
|
|
379
362
|
changes: [
|
|
@@ -394,10 +377,8 @@ test("Client replies with known new content to tellKnownState from server", asyn
|
|
|
394
377
|
});
|
|
395
378
|
|
|
396
379
|
test("No matter the optimistic known state, node respects invalid known state messages and resyncs", async () => {
|
|
397
|
-
const admin =
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
const node = new LocalNode(admin, newRandomSessionID(adminID));
|
|
380
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
381
|
+
const node = new LocalNode(admin, session);
|
|
401
382
|
|
|
402
383
|
const team = node.createTeam();
|
|
403
384
|
|
|
@@ -416,8 +397,8 @@ test("No matter the optimistic known state, node respects invalid known state me
|
|
|
416
397
|
const writer = inTx.getWriter();
|
|
417
398
|
|
|
418
399
|
await writer.write({
|
|
419
|
-
action: "
|
|
420
|
-
|
|
400
|
+
action: "load",
|
|
401
|
+
id: map.coValue.id,
|
|
421
402
|
header: false,
|
|
422
403
|
sessions: {
|
|
423
404
|
[node.ownSessionID]: 0,
|
|
@@ -426,25 +407,25 @@ test("No matter the optimistic known state, node respects invalid known state me
|
|
|
426
407
|
|
|
427
408
|
const reader = outRx.getReader();
|
|
428
409
|
|
|
429
|
-
expect((await reader.read()).value).toMatchObject(admStateEx(
|
|
410
|
+
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
|
430
411
|
expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
|
431
412
|
|
|
432
413
|
const mapTellKnownStateMsg = await reader.read();
|
|
433
414
|
expect(mapTellKnownStateMsg.value).toEqual({
|
|
434
|
-
action: "
|
|
415
|
+
action: "known",
|
|
435
416
|
...map.coValue.knownState(),
|
|
436
417
|
} satisfies SyncMessage);
|
|
437
418
|
|
|
438
|
-
expect((await reader.read()).value).toMatchObject(admContEx(
|
|
419
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
439
420
|
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
|
440
421
|
|
|
441
422
|
const mapNewContentHeaderOnlyMsg = await reader.read();
|
|
442
423
|
|
|
443
424
|
expect(mapNewContentHeaderOnlyMsg.value).toEqual({
|
|
444
|
-
action: "
|
|
445
|
-
|
|
425
|
+
action: "content",
|
|
426
|
+
id: map.coValue.id,
|
|
446
427
|
header: map.coValue.header,
|
|
447
|
-
|
|
428
|
+
new: {},
|
|
448
429
|
} satisfies SyncMessage);
|
|
449
430
|
|
|
450
431
|
map.edit((editable) => {
|
|
@@ -459,8 +440,9 @@ test("No matter the optimistic known state, node respects invalid known state me
|
|
|
459
440
|
const _mapEditMsg2 = await reader.read();
|
|
460
441
|
|
|
461
442
|
await writer.write({
|
|
462
|
-
action: "
|
|
463
|
-
|
|
443
|
+
action: "known",
|
|
444
|
+
isCorrection: true,
|
|
445
|
+
id: map.coValue.id,
|
|
464
446
|
header: true,
|
|
465
447
|
sessions: {
|
|
466
448
|
[node.ownSessionID]: 1,
|
|
@@ -470,15 +452,15 @@ test("No matter the optimistic known state, node respects invalid known state me
|
|
|
470
452
|
const newContentAfterWrongAssumedState = await reader.read();
|
|
471
453
|
|
|
472
454
|
expect(newContentAfterWrongAssumedState.value).toEqual({
|
|
473
|
-
action: "
|
|
474
|
-
|
|
455
|
+
action: "content",
|
|
456
|
+
id: map.coValue.id,
|
|
475
457
|
header: undefined,
|
|
476
|
-
|
|
458
|
+
new: {
|
|
477
459
|
[node.ownSessionID]: {
|
|
478
460
|
after: 1,
|
|
479
461
|
newTransactions: [
|
|
480
462
|
{
|
|
481
|
-
privacy: "trusting",
|
|
463
|
+
privacy: "trusting" as const,
|
|
482
464
|
madeAt: map.coValue.sessions[node.ownSessionID]!
|
|
483
465
|
.transactions[1]!.madeAt,
|
|
484
466
|
changes: [
|
|
@@ -499,10 +481,8 @@ test("No matter the optimistic known state, node respects invalid known state me
|
|
|
499
481
|
});
|
|
500
482
|
|
|
501
483
|
test("If we add a peer, but it never subscribes to a coValue, it won't get any messages", async () => {
|
|
502
|
-
const admin =
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
const node = new LocalNode(admin, newRandomSessionID(adminID));
|
|
484
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
485
|
+
const node = new LocalNode(admin, session);
|
|
506
486
|
|
|
507
487
|
const team = node.createTeam();
|
|
508
488
|
|
|
@@ -524,14 +504,14 @@ test("If we add a peer, but it never subscribes to a coValue, it won't get any m
|
|
|
524
504
|
|
|
525
505
|
const reader = outRx.getReader();
|
|
526
506
|
|
|
527
|
-
await expect(
|
|
507
|
+
await expect(
|
|
508
|
+
shouldNotResolve(reader.read(), { timeout: 100 })
|
|
509
|
+
).resolves.toBeUndefined();
|
|
528
510
|
});
|
|
529
511
|
|
|
530
512
|
test("If we add a server peer, all updates to all coValues are sent to it, even if it doesn't subscribe", async () => {
|
|
531
|
-
const admin =
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
const node = new LocalNode(admin, newRandomSessionID(adminID));
|
|
513
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
514
|
+
const node = new LocalNode(admin, session);
|
|
535
515
|
|
|
536
516
|
const team = node.createTeam();
|
|
537
517
|
|
|
@@ -548,20 +528,20 @@ test("If we add a server peer, all updates to all coValues are sent to it, even
|
|
|
548
528
|
});
|
|
549
529
|
|
|
550
530
|
const reader = outRx.getReader();
|
|
531
|
+
// expect((await reader.read()).value).toMatchObject({
|
|
532
|
+
// action: "load",
|
|
533
|
+
// id: adminID,
|
|
534
|
+
// });
|
|
551
535
|
expect((await reader.read()).value).toMatchObject({
|
|
552
|
-
action: "
|
|
553
|
-
|
|
554
|
-
});
|
|
555
|
-
expect((await reader.read()).value).toMatchObject({
|
|
556
|
-
action: "subscribe",
|
|
557
|
-
coValueID: team.teamMap.coValue.id,
|
|
536
|
+
action: "load",
|
|
537
|
+
id: team.teamMap.coValue.id,
|
|
558
538
|
});
|
|
559
539
|
|
|
560
540
|
const mapSubscribeMsg = await reader.read();
|
|
561
541
|
|
|
562
542
|
expect(mapSubscribeMsg.value).toEqual({
|
|
563
|
-
action: "
|
|
564
|
-
|
|
543
|
+
action: "load",
|
|
544
|
+
id: map.coValue.id,
|
|
565
545
|
header: true,
|
|
566
546
|
sessions: {},
|
|
567
547
|
} satisfies SyncMessage);
|
|
@@ -570,21 +550,21 @@ test("If we add a server peer, all updates to all coValues are sent to it, even
|
|
|
570
550
|
editable.set("hello", "world", "trusting");
|
|
571
551
|
});
|
|
572
552
|
|
|
573
|
-
expect((await reader.read()).value).toMatchObject(admContEx(
|
|
553
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
574
554
|
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
|
575
555
|
|
|
576
556
|
const mapNewContentMsg = await reader.read();
|
|
577
557
|
|
|
578
558
|
expect(mapNewContentMsg.value).toEqual({
|
|
579
|
-
action: "
|
|
580
|
-
|
|
559
|
+
action: "content",
|
|
560
|
+
id: map.coValue.id,
|
|
581
561
|
header: map.coValue.header,
|
|
582
|
-
|
|
562
|
+
new: {
|
|
583
563
|
[node.ownSessionID]: {
|
|
584
564
|
after: 0,
|
|
585
565
|
newTransactions: [
|
|
586
566
|
{
|
|
587
|
-
privacy: "trusting",
|
|
567
|
+
privacy: "trusting" as const,
|
|
588
568
|
madeAt: map.coValue.sessions[node.ownSessionID]!
|
|
589
569
|
.transactions[0]!.madeAt,
|
|
590
570
|
changes: [
|
|
@@ -605,10 +585,8 @@ test("If we add a server peer, all updates to all coValues are sent to it, even
|
|
|
605
585
|
});
|
|
606
586
|
|
|
607
587
|
test("If we add a server peer, newly created coValues are auto-subscribed to", async () => {
|
|
608
|
-
const admin =
|
|
609
|
-
const
|
|
610
|
-
|
|
611
|
-
const node = new LocalNode(admin, newRandomSessionID(adminID));
|
|
588
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
589
|
+
const node = new LocalNode(admin, session);
|
|
612
590
|
|
|
613
591
|
const team = node.createTeam();
|
|
614
592
|
|
|
@@ -623,13 +601,13 @@ test("If we add a server peer, newly created coValues are auto-subscribed to", a
|
|
|
623
601
|
});
|
|
624
602
|
|
|
625
603
|
const reader = outRx.getReader();
|
|
604
|
+
// expect((await reader.read()).value).toMatchObject({
|
|
605
|
+
// action: "load",
|
|
606
|
+
// id: admin.id,
|
|
607
|
+
// });
|
|
626
608
|
expect((await reader.read()).value).toMatchObject({
|
|
627
|
-
action: "
|
|
628
|
-
|
|
629
|
-
});
|
|
630
|
-
expect((await reader.read()).value).toMatchObject({
|
|
631
|
-
action: "subscribe",
|
|
632
|
-
coValueID: team.teamMap.coValue.id,
|
|
609
|
+
action: "load",
|
|
610
|
+
id: team.teamMap.coValue.id,
|
|
633
611
|
});
|
|
634
612
|
|
|
635
613
|
const map = team.createMap();
|
|
@@ -637,32 +615,30 @@ test("If we add a server peer, newly created coValues are auto-subscribed to", a
|
|
|
637
615
|
const mapSubscribeMsg = await reader.read();
|
|
638
616
|
|
|
639
617
|
expect(mapSubscribeMsg.value).toEqual({
|
|
640
|
-
action: "
|
|
618
|
+
action: "load",
|
|
641
619
|
...map.coValue.knownState(),
|
|
642
620
|
} satisfies SyncMessage);
|
|
643
621
|
|
|
644
|
-
expect((await reader.read()).value).toMatchObject(admContEx(adminID));
|
|
622
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(adminID));
|
|
645
623
|
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
|
646
624
|
|
|
647
625
|
const mapContentMsg = await reader.read();
|
|
648
626
|
|
|
649
627
|
expect(mapContentMsg.value).toEqual({
|
|
650
|
-
action: "
|
|
651
|
-
|
|
628
|
+
action: "content",
|
|
629
|
+
id: map.coValue.id,
|
|
652
630
|
header: map.coValue.header,
|
|
653
|
-
|
|
631
|
+
new: {},
|
|
654
632
|
} satisfies SyncMessage);
|
|
655
633
|
});
|
|
656
634
|
|
|
657
635
|
test.todo(
|
|
658
|
-
"TODO: when receiving a subscribe response that is behind our optimistic state (due to already sent content), we ignore it"
|
|
636
|
+
"TODO: when receiving a subscribe response that is behind our optimistic state (due to already sent content), we ignore it"
|
|
659
637
|
);
|
|
660
638
|
|
|
661
639
|
test("When we connect a new server peer, we try to sync all existing coValues to it", async () => {
|
|
662
|
-
const admin =
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
const node = new LocalNode(admin, newRandomSessionID(adminID));
|
|
640
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
641
|
+
const node = new LocalNode(admin, session);
|
|
666
642
|
|
|
667
643
|
const team = node.createTeam();
|
|
668
644
|
|
|
@@ -680,27 +656,25 @@ test("When we connect a new server peer, we try to sync all existing coValues to
|
|
|
680
656
|
|
|
681
657
|
const reader = outRx.getReader();
|
|
682
658
|
|
|
683
|
-
const _adminSubscribeMessage = await reader.read();
|
|
659
|
+
// const _adminSubscribeMessage = await reader.read();
|
|
684
660
|
const teamSubscribeMessage = await reader.read();
|
|
685
661
|
|
|
686
662
|
expect(teamSubscribeMessage.value).toEqual({
|
|
687
|
-
action: "
|
|
663
|
+
action: "load",
|
|
688
664
|
...team.teamMap.coValue.knownState(),
|
|
689
665
|
} satisfies SyncMessage);
|
|
690
666
|
|
|
691
667
|
const secondMessage = await reader.read();
|
|
692
668
|
|
|
693
669
|
expect(secondMessage.value).toEqual({
|
|
694
|
-
action: "
|
|
670
|
+
action: "load",
|
|
695
671
|
...map.coValue.knownState(),
|
|
696
672
|
} satisfies SyncMessage);
|
|
697
673
|
});
|
|
698
674
|
|
|
699
675
|
test("When receiving a subscribe with a known state that is ahead of our own, peers should respond with a corresponding subscribe response message", async () => {
|
|
700
|
-
const admin =
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
const node = new LocalNode(admin, newRandomSessionID(adminID));
|
|
676
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
677
|
+
const node = new LocalNode(admin, session);
|
|
704
678
|
|
|
705
679
|
const team = node.createTeam();
|
|
706
680
|
|
|
@@ -719,8 +693,8 @@ test("When receiving a subscribe with a known state that is ahead of our own, pe
|
|
|
719
693
|
const writer = inTx.getWriter();
|
|
720
694
|
|
|
721
695
|
await writer.write({
|
|
722
|
-
action: "
|
|
723
|
-
|
|
696
|
+
action: "load",
|
|
697
|
+
id: map.coValue.id,
|
|
724
698
|
header: true,
|
|
725
699
|
sessions: {
|
|
726
700
|
[node.ownSessionID]: 1,
|
|
@@ -729,22 +703,21 @@ test("When receiving a subscribe with a known state that is ahead of our own, pe
|
|
|
729
703
|
|
|
730
704
|
const reader = outRx.getReader();
|
|
731
705
|
|
|
732
|
-
expect((await reader.read()).value).toMatchObject(admStateEx(
|
|
706
|
+
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
|
733
707
|
expect((await reader.read()).value).toMatchObject(teamStateEx(team));
|
|
734
708
|
const mapTellKnownState = await reader.read();
|
|
735
709
|
|
|
736
710
|
expect(mapTellKnownState.value).toEqual({
|
|
737
|
-
action: "
|
|
711
|
+
action: "known",
|
|
738
712
|
...map.coValue.knownState(),
|
|
739
713
|
} satisfies SyncMessage);
|
|
740
714
|
});
|
|
741
715
|
|
|
742
716
|
test.skip("When replaying creation and transactions of a coValue as new content, the receiving peer integrates this information", async () => {
|
|
743
717
|
// TODO: this test is mostly correct but also slightly unrealistic, make sure we pass all messages back and forth as expected and then it should work
|
|
744
|
-
const admin =
|
|
745
|
-
const adminID = getAgentID(getAgent(admin));
|
|
718
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
746
719
|
|
|
747
|
-
const node1 = new LocalNode(admin,
|
|
720
|
+
const node1 = new LocalNode(admin, session);
|
|
748
721
|
|
|
749
722
|
const team = node1.createTeam();
|
|
750
723
|
|
|
@@ -761,7 +734,7 @@ test.skip("When replaying creation and transactions of a coValue as new content,
|
|
|
761
734
|
const to1 = inTx1.getWriter();
|
|
762
735
|
const from1 = outRx1.getReader();
|
|
763
736
|
|
|
764
|
-
const node2 = new LocalNode(admin, newRandomSessionID(
|
|
737
|
+
const node2 = new LocalNode(admin, newRandomSessionID(admin.id));
|
|
765
738
|
|
|
766
739
|
const [inRx2, inTx2] = newStreamPair<SyncMessage>();
|
|
767
740
|
const [outRx2, outTx2] = newStreamPair<SyncMessage>();
|
|
@@ -778,20 +751,20 @@ test.skip("When replaying creation and transactions of a coValue as new content,
|
|
|
778
751
|
|
|
779
752
|
const adminSubscribeMessage = await from1.read();
|
|
780
753
|
expect(adminSubscribeMessage.value).toMatchObject({
|
|
781
|
-
action: "
|
|
782
|
-
|
|
754
|
+
action: "load",
|
|
755
|
+
id: admin.id,
|
|
783
756
|
});
|
|
784
757
|
const teamSubscribeMsg = await from1.read();
|
|
785
758
|
expect(teamSubscribeMsg.value).toMatchObject({
|
|
786
|
-
action: "
|
|
787
|
-
|
|
759
|
+
action: "load",
|
|
760
|
+
id: team.teamMap.coValue.id,
|
|
788
761
|
});
|
|
789
762
|
|
|
790
763
|
await to2.write(adminSubscribeMessage.value!);
|
|
791
764
|
await to2.write(teamSubscribeMsg.value!);
|
|
792
765
|
|
|
793
|
-
const adminTellKnownStateMsg = await from2.read();
|
|
794
|
-
expect(adminTellKnownStateMsg.value).toMatchObject(admStateEx(
|
|
766
|
+
// const adminTellKnownStateMsg = await from2.read();
|
|
767
|
+
// expect(adminTellKnownStateMsg.value).toMatchObject(admStateEx(admin.id));
|
|
795
768
|
|
|
796
769
|
const teamTellKnownStateMsg = await from2.read();
|
|
797
770
|
expect(teamTellKnownStateMsg.value).toMatchObject(teamStateEx(team));
|
|
@@ -802,40 +775,40 @@ test.skip("When replaying creation and transactions of a coValue as new content,
|
|
|
802
775
|
]
|
|
803
776
|
).toBeDefined();
|
|
804
777
|
|
|
805
|
-
await to1.write(adminTellKnownStateMsg.value!);
|
|
778
|
+
// await to1.write(adminTellKnownStateMsg.value!);
|
|
806
779
|
await to1.write(teamTellKnownStateMsg.value!);
|
|
807
780
|
|
|
808
|
-
const adminContentMsg = await from1.read();
|
|
809
|
-
expect(adminContentMsg.value).toMatchObject(admContEx(
|
|
781
|
+
// const adminContentMsg = await from1.read();
|
|
782
|
+
// expect(adminContentMsg.value).toMatchObject(admContEx(admin.id));
|
|
810
783
|
|
|
811
784
|
const teamContentMsg = await from1.read();
|
|
812
785
|
expect(teamContentMsg.value).toMatchObject(teamContentEx(team));
|
|
813
786
|
|
|
814
|
-
await to2.write(adminContentMsg.value!);
|
|
787
|
+
// await to2.write(adminContentMsg.value!);
|
|
815
788
|
await to2.write(teamContentMsg.value!);
|
|
816
789
|
|
|
817
790
|
const map = team.createMap();
|
|
818
791
|
|
|
819
792
|
const mapSubscriptionMsg = await from1.read();
|
|
820
793
|
expect(mapSubscriptionMsg.value).toMatchObject({
|
|
821
|
-
action: "
|
|
822
|
-
|
|
794
|
+
action: "load",
|
|
795
|
+
id: map.coValue.id,
|
|
823
796
|
});
|
|
824
797
|
|
|
825
798
|
const mapNewContentMsg = await from1.read();
|
|
826
799
|
expect(mapNewContentMsg.value).toEqual({
|
|
827
|
-
action: "
|
|
828
|
-
|
|
800
|
+
action: "content",
|
|
801
|
+
id: map.coValue.id,
|
|
829
802
|
header: map.coValue.header,
|
|
830
|
-
|
|
803
|
+
new: {},
|
|
831
804
|
} satisfies SyncMessage);
|
|
832
805
|
|
|
833
806
|
await to2.write(mapSubscriptionMsg.value!);
|
|
834
807
|
|
|
835
808
|
const mapTellKnownStateMsg = await from2.read();
|
|
836
809
|
expect(mapTellKnownStateMsg.value).toEqual({
|
|
837
|
-
action: "
|
|
838
|
-
|
|
810
|
+
action: "known",
|
|
811
|
+
id: map.coValue.id,
|
|
839
812
|
header: false,
|
|
840
813
|
sessions: {},
|
|
841
814
|
} satisfies SyncMessage);
|
|
@@ -863,10 +836,9 @@ test.skip("When replaying creation and transactions of a coValue as new content,
|
|
|
863
836
|
|
|
864
837
|
test.skip("When loading a coValue on one node, the server node it is requested from replies with all the necessary depended on coValues to make it work", async () => {
|
|
865
838
|
// TODO: this test is mostly correct but also slightly unrealistic, make sure we pass all messages back and forth as expected and then it should work
|
|
866
|
-
const admin =
|
|
867
|
-
const adminID = getAgentID(getAgent(admin));
|
|
839
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
868
840
|
|
|
869
|
-
const node1 = new LocalNode(admin,
|
|
841
|
+
const node1 = new LocalNode(admin, session);
|
|
870
842
|
|
|
871
843
|
const team = node1.createTeam();
|
|
872
844
|
|
|
@@ -875,7 +847,7 @@ test.skip("When loading a coValue on one node, the server node it is requested f
|
|
|
875
847
|
editable.set("hello", "world", "trusting");
|
|
876
848
|
});
|
|
877
849
|
|
|
878
|
-
const node2 = new LocalNode(admin, newRandomSessionID(
|
|
850
|
+
const node2 = new LocalNode(admin, newRandomSessionID(admin.id));
|
|
879
851
|
|
|
880
852
|
const [node1asPeer, node2asPeer] = connectedPeers("peer1", "peer2");
|
|
881
853
|
|
|
@@ -892,10 +864,9 @@ test.skip("When loading a coValue on one node, the server node it is requested f
|
|
|
892
864
|
});
|
|
893
865
|
|
|
894
866
|
test("Can sync a coValue through a server to another client", async () => {
|
|
895
|
-
const admin =
|
|
896
|
-
const adminID = getAgentID(getAgent(admin));
|
|
867
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
897
868
|
|
|
898
|
-
const client1 = new LocalNode(admin,
|
|
869
|
+
const client1 = new LocalNode(admin, session);
|
|
899
870
|
|
|
900
871
|
const team = client1.createTeam();
|
|
901
872
|
|
|
@@ -904,10 +875,9 @@ test("Can sync a coValue through a server to another client", async () => {
|
|
|
904
875
|
editable.set("hello", "world", "trusting");
|
|
905
876
|
});
|
|
906
877
|
|
|
907
|
-
const serverUser =
|
|
908
|
-
const serverUserID = getAgentID(getAgent(serverUser));
|
|
878
|
+
const [serverUser, serverSession] = randomAnonymousAccountAndSessionID();
|
|
909
879
|
|
|
910
|
-
const server = new LocalNode(serverUser,
|
|
880
|
+
const server = new LocalNode(serverUser, serverSession);
|
|
911
881
|
|
|
912
882
|
const [serverAsPeer, client1AsPeer] = connectedPeers("server", "client1", {
|
|
913
883
|
peer1role: "server",
|
|
@@ -917,7 +887,7 @@ test("Can sync a coValue through a server to another client", async () => {
|
|
|
917
887
|
client1.sync.addPeer(serverAsPeer);
|
|
918
888
|
server.sync.addPeer(client1AsPeer);
|
|
919
889
|
|
|
920
|
-
const client2 = new LocalNode(admin, newRandomSessionID(
|
|
890
|
+
const client2 = new LocalNode(admin, newRandomSessionID(admin.id));
|
|
921
891
|
|
|
922
892
|
const [serverAsOtherPeer, client2AsPeer] = connectedPeers(
|
|
923
893
|
"server",
|
|
@@ -936,10 +906,9 @@ test("Can sync a coValue through a server to another client", async () => {
|
|
|
936
906
|
});
|
|
937
907
|
|
|
938
908
|
test("Can sync a coValue with private transactions through a server to another client", async () => {
|
|
939
|
-
const admin =
|
|
940
|
-
const adminID = getAgentID(getAgent(admin));
|
|
909
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
941
910
|
|
|
942
|
-
const client1 = new LocalNode(admin,
|
|
911
|
+
const client1 = new LocalNode(admin, session);
|
|
943
912
|
|
|
944
913
|
const team = client1.createTeam();
|
|
945
914
|
|
|
@@ -948,10 +917,9 @@ test("Can sync a coValue with private transactions through a server to another c
|
|
|
948
917
|
editable.set("hello", "world", "private");
|
|
949
918
|
});
|
|
950
919
|
|
|
951
|
-
const serverUser =
|
|
952
|
-
const serverUserID = getAgentID(getAgent(serverUser));
|
|
920
|
+
const [serverUser, serverSession] = randomAnonymousAccountAndSessionID();
|
|
953
921
|
|
|
954
|
-
const server = new LocalNode(serverUser,
|
|
922
|
+
const server = new LocalNode(serverUser, serverSession);
|
|
955
923
|
|
|
956
924
|
const [serverAsPeer, client1AsPeer] = connectedPeers("server", "client1", {
|
|
957
925
|
trace: true,
|
|
@@ -962,7 +930,7 @@ test("Can sync a coValue with private transactions through a server to another c
|
|
|
962
930
|
client1.sync.addPeer(serverAsPeer);
|
|
963
931
|
server.sync.addPeer(client1AsPeer);
|
|
964
932
|
|
|
965
|
-
const client2 = new LocalNode(admin, newRandomSessionID(
|
|
933
|
+
const client2 = new LocalNode(admin, newRandomSessionID(admin.id));
|
|
966
934
|
|
|
967
935
|
const [serverAsOtherPeer, client2AsPeer] = connectedPeers(
|
|
968
936
|
"server",
|
|
@@ -981,10 +949,8 @@ test("Can sync a coValue with private transactions through a server to another c
|
|
|
981
949
|
});
|
|
982
950
|
|
|
983
951
|
test("When a peer's incoming/readable stream closes, we remove the peer", async () => {
|
|
984
|
-
const admin =
|
|
985
|
-
const
|
|
986
|
-
|
|
987
|
-
const node = new LocalNode(admin, newRandomSessionID(adminID));
|
|
952
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
953
|
+
const node = new LocalNode(admin, session);
|
|
988
954
|
|
|
989
955
|
const team = node.createTeam();
|
|
990
956
|
|
|
@@ -999,13 +965,13 @@ test("When a peer's incoming/readable stream closes, we remove the peer", async
|
|
|
999
965
|
});
|
|
1000
966
|
|
|
1001
967
|
const reader = outRx.getReader();
|
|
968
|
+
// expect((await reader.read()).value).toMatchObject({
|
|
969
|
+
// action: "load",
|
|
970
|
+
// id: admin.id,
|
|
971
|
+
// });
|
|
1002
972
|
expect((await reader.read()).value).toMatchObject({
|
|
1003
|
-
action: "
|
|
1004
|
-
|
|
1005
|
-
});
|
|
1006
|
-
expect((await reader.read()).value).toMatchObject({
|
|
1007
|
-
action: "subscribe",
|
|
1008
|
-
coValueID: team.teamMap.coValue.id,
|
|
973
|
+
action: "load",
|
|
974
|
+
id: team.teamMap.coValue.id,
|
|
1009
975
|
});
|
|
1010
976
|
|
|
1011
977
|
const map = team.createMap();
|
|
@@ -1013,20 +979,20 @@ test("When a peer's incoming/readable stream closes, we remove the peer", async
|
|
|
1013
979
|
const mapSubscribeMsg = await reader.read();
|
|
1014
980
|
|
|
1015
981
|
expect(mapSubscribeMsg.value).toEqual({
|
|
1016
|
-
action: "
|
|
982
|
+
action: "load",
|
|
1017
983
|
...map.coValue.knownState(),
|
|
1018
984
|
} satisfies SyncMessage);
|
|
1019
985
|
|
|
1020
|
-
expect((await reader.read()).value).toMatchObject(admContEx(
|
|
986
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
1021
987
|
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
|
1022
988
|
|
|
1023
989
|
const mapContentMsg = await reader.read();
|
|
1024
990
|
|
|
1025
991
|
expect(mapContentMsg.value).toEqual({
|
|
1026
|
-
action: "
|
|
1027
|
-
|
|
992
|
+
action: "content",
|
|
993
|
+
id: map.coValue.id,
|
|
1028
994
|
header: map.coValue.header,
|
|
1029
|
-
|
|
995
|
+
new: {},
|
|
1030
996
|
} satisfies SyncMessage);
|
|
1031
997
|
|
|
1032
998
|
await inTx.abort();
|
|
@@ -1037,10 +1003,8 @@ test("When a peer's incoming/readable stream closes, we remove the peer", async
|
|
|
1037
1003
|
});
|
|
1038
1004
|
|
|
1039
1005
|
test("When a peer's outgoing/writable stream closes, we remove the peer", async () => {
|
|
1040
|
-
const admin =
|
|
1041
|
-
const
|
|
1042
|
-
|
|
1043
|
-
const node = new LocalNode(admin, newRandomSessionID(adminID));
|
|
1006
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
1007
|
+
const node = new LocalNode(admin, session);
|
|
1044
1008
|
|
|
1045
1009
|
const team = node.createTeam();
|
|
1046
1010
|
|
|
@@ -1055,13 +1019,13 @@ test("When a peer's outgoing/writable stream closes, we remove the peer", async
|
|
|
1055
1019
|
});
|
|
1056
1020
|
|
|
1057
1021
|
const reader = outRx.getReader();
|
|
1022
|
+
// expect((await reader.read()).value).toMatchObject({
|
|
1023
|
+
// action: "load",
|
|
1024
|
+
// id: admin.id,
|
|
1025
|
+
// });
|
|
1058
1026
|
expect((await reader.read()).value).toMatchObject({
|
|
1059
|
-
action: "
|
|
1060
|
-
|
|
1061
|
-
});
|
|
1062
|
-
expect((await reader.read()).value).toMatchObject({
|
|
1063
|
-
action: "subscribe",
|
|
1064
|
-
coValueID: team.teamMap.coValue.id,
|
|
1027
|
+
action: "load",
|
|
1028
|
+
id: team.teamMap.coValue.id,
|
|
1065
1029
|
});
|
|
1066
1030
|
|
|
1067
1031
|
const map = team.createMap();
|
|
@@ -1069,20 +1033,20 @@ test("When a peer's outgoing/writable stream closes, we remove the peer", async
|
|
|
1069
1033
|
const mapSubscribeMsg = await reader.read();
|
|
1070
1034
|
|
|
1071
1035
|
expect(mapSubscribeMsg.value).toEqual({
|
|
1072
|
-
action: "
|
|
1036
|
+
action: "load",
|
|
1073
1037
|
...map.coValue.knownState(),
|
|
1074
1038
|
} satisfies SyncMessage);
|
|
1075
1039
|
|
|
1076
|
-
expect((await reader.read()).value).toMatchObject(admContEx(
|
|
1040
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
1077
1041
|
expect((await reader.read()).value).toMatchObject(teamContentEx(team));
|
|
1078
1042
|
|
|
1079
1043
|
const mapContentMsg = await reader.read();
|
|
1080
1044
|
|
|
1081
1045
|
expect(mapContentMsg.value).toEqual({
|
|
1082
|
-
action: "
|
|
1083
|
-
|
|
1046
|
+
action: "content",
|
|
1047
|
+
id: map.coValue.id,
|
|
1084
1048
|
header: map.coValue.header,
|
|
1085
|
-
|
|
1049
|
+
new: {},
|
|
1086
1050
|
} satisfies SyncMessage);
|
|
1087
1051
|
|
|
1088
1052
|
reader.releaseLock();
|
|
@@ -1095,175 +1059,69 @@ test("When a peer's outgoing/writable stream closes, we remove the peer", async
|
|
|
1095
1059
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1096
1060
|
|
|
1097
1061
|
expect(node.sync.peers["test"]).toBeUndefined();
|
|
1098
|
-
})
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
test("If we start loading a coValue before connecting to a peer that has it, it will load it once we connect", async () => {
|
|
1065
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
1066
|
+
|
|
1067
|
+
const node1 = new LocalNode(admin, session);
|
|
1068
|
+
|
|
1069
|
+
const team = node1.createTeam();
|
|
1070
|
+
|
|
1071
|
+
const map = team.createMap();
|
|
1072
|
+
map.edit((editable) => {
|
|
1073
|
+
editable.set("hello", "world", "trusting");
|
|
1074
|
+
});
|
|
1075
|
+
|
|
1076
|
+
const node2 = new LocalNode(admin, newRandomSessionID(admin.id));
|
|
1077
|
+
|
|
1078
|
+
const [node1asPeer, node2asPeer] = connectedPeers("peer1", "peer2", {
|
|
1079
|
+
peer1role: "server",
|
|
1080
|
+
peer2role: "client",
|
|
1081
|
+
trace: true,
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
node1.sync.addPeer(node2asPeer);
|
|
1085
|
+
|
|
1086
|
+
const mapOnNode2Promise = node2.loadCoValue(map.coValue.id);
|
|
1087
|
+
|
|
1088
|
+
expect(node2.coValues[map.coValue.id]?.state).toEqual("loading");
|
|
1089
|
+
|
|
1090
|
+
node2.sync.addPeer(node1asPeer);
|
|
1091
|
+
|
|
1092
|
+
const mapOnNode2 = await mapOnNode2Promise;
|
|
1093
|
+
|
|
1094
|
+
expect(expectMap(mapOnNode2.getCurrentContent()).get("hello")).toEqual(
|
|
1095
|
+
"world"
|
|
1096
|
+
);
|
|
1097
|
+
});
|
|
1099
1098
|
|
|
1100
1099
|
function teamContentEx(team: Team) {
|
|
1101
1100
|
return {
|
|
1102
|
-
action: "
|
|
1103
|
-
|
|
1101
|
+
action: "content",
|
|
1102
|
+
id: team.teamMap.coValue.id,
|
|
1104
1103
|
};
|
|
1105
1104
|
}
|
|
1106
1105
|
|
|
1107
|
-
function admContEx(adminID:
|
|
1106
|
+
function admContEx(adminID: AccountID) {
|
|
1108
1107
|
return {
|
|
1109
|
-
action: "
|
|
1110
|
-
|
|
1108
|
+
action: "content",
|
|
1109
|
+
id: adminID,
|
|
1111
1110
|
};
|
|
1112
1111
|
}
|
|
1113
1112
|
|
|
1114
1113
|
function teamStateEx(team: Team) {
|
|
1115
1114
|
return {
|
|
1116
|
-
action: "
|
|
1117
|
-
|
|
1115
|
+
action: "known",
|
|
1116
|
+
id: team.teamMap.coValue.id,
|
|
1118
1117
|
};
|
|
1119
1118
|
}
|
|
1120
1119
|
|
|
1121
|
-
function admStateEx(adminID:
|
|
1120
|
+
function admStateEx(adminID: AccountID) {
|
|
1122
1121
|
return {
|
|
1123
|
-
action: "
|
|
1124
|
-
|
|
1122
|
+
action: "known",
|
|
1123
|
+
id: adminID,
|
|
1125
1124
|
};
|
|
1126
1125
|
}
|
|
1127
1126
|
|
|
1128
|
-
function newStreamPair<T>(): [ReadableStream<T>, WritableStream<T>] {
|
|
1129
|
-
const queue: T[] = [];
|
|
1130
|
-
let resolveNextItemReady: () => void = () => {};
|
|
1131
|
-
let nextItemReady: Promise<void> = new Promise((resolve) => {
|
|
1132
|
-
resolveNextItemReady = resolve;
|
|
1133
|
-
});
|
|
1134
|
-
|
|
1135
|
-
let writerClosed = false;
|
|
1136
|
-
let readerClosed = false;
|
|
1137
|
-
|
|
1138
|
-
const readable = new ReadableStream<T>({
|
|
1139
|
-
async pull(controller) {
|
|
1140
|
-
let retriesLeft = 3;
|
|
1141
|
-
while (retriesLeft > 0) {
|
|
1142
|
-
if (writerClosed) {
|
|
1143
|
-
controller.close();
|
|
1144
|
-
return;
|
|
1145
|
-
}
|
|
1146
|
-
retriesLeft--;
|
|
1147
|
-
if (queue.length > 0) {
|
|
1148
|
-
controller.enqueue(queue.shift()!);
|
|
1149
|
-
if (queue.length === 0) {
|
|
1150
|
-
nextItemReady = new Promise((resolve) => {
|
|
1151
|
-
resolveNextItemReady = resolve;
|
|
1152
|
-
});
|
|
1153
|
-
}
|
|
1154
|
-
return;
|
|
1155
|
-
} else {
|
|
1156
|
-
await nextItemReady;
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
throw new Error("Should only use one retry to get next item in queue.")
|
|
1160
|
-
},
|
|
1161
|
-
|
|
1162
|
-
cancel(reason) {
|
|
1163
|
-
console.log("Manually closing reader")
|
|
1164
|
-
readerClosed = true;
|
|
1165
|
-
},
|
|
1166
|
-
});
|
|
1167
|
-
|
|
1168
|
-
const writable = new WritableStream<T>({
|
|
1169
|
-
write(chunk, controller) {
|
|
1170
|
-
if (readerClosed) {
|
|
1171
|
-
console.log("Reader closed, not writing chunk", chunk);
|
|
1172
|
-
throw new Error("Reader closed, not writing chunk");
|
|
1173
|
-
}
|
|
1174
|
-
queue.push(chunk);
|
|
1175
|
-
if (queue.length === 1) {
|
|
1176
|
-
// make sure that await write resolves before corresponding read
|
|
1177
|
-
process.nextTick(() => resolveNextItemReady());
|
|
1178
|
-
}
|
|
1179
|
-
},
|
|
1180
|
-
abort(reason) {
|
|
1181
|
-
console.log("Manually closing writer")
|
|
1182
|
-
writerClosed = true;
|
|
1183
|
-
resolveNextItemReady();
|
|
1184
|
-
return Promise.resolve();
|
|
1185
|
-
},
|
|
1186
|
-
});
|
|
1187
|
-
|
|
1188
|
-
return [readable, writable];
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
function shouldNotResolve<T>(promise: Promise<T>, ops: { timeout: number }): Promise<void> {
|
|
1192
|
-
return new Promise((resolve, reject) => {
|
|
1193
|
-
promise
|
|
1194
|
-
.then((v) =>
|
|
1195
|
-
reject(
|
|
1196
|
-
new Error(
|
|
1197
|
-
"Should not have resolved, but resolved to " +
|
|
1198
|
-
JSON.stringify(v)
|
|
1199
|
-
)
|
|
1200
|
-
)
|
|
1201
|
-
)
|
|
1202
|
-
.catch(reject);
|
|
1203
|
-
setTimeout(resolve, ops.timeout);
|
|
1204
|
-
});
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
function connectedPeers(
|
|
1208
|
-
peer1id: PeerID,
|
|
1209
|
-
peer2id: PeerID,
|
|
1210
|
-
{
|
|
1211
|
-
trace = false,
|
|
1212
|
-
peer1role = "peer",
|
|
1213
|
-
peer2role = "peer",
|
|
1214
|
-
}: {
|
|
1215
|
-
trace?: boolean;
|
|
1216
|
-
peer1role?: Peer["role"];
|
|
1217
|
-
peer2role?: Peer["role"];
|
|
1218
|
-
} = {}
|
|
1219
|
-
): [Peer, Peer] {
|
|
1220
|
-
const [inRx1, inTx1] = newStreamPair<SyncMessage>();
|
|
1221
|
-
const [outRx1, outTx1] = newStreamPair<SyncMessage>();
|
|
1222
|
-
|
|
1223
|
-
const [inRx2, inTx2] = newStreamPair<SyncMessage>();
|
|
1224
|
-
const [outRx2, outTx2] = newStreamPair<SyncMessage>();
|
|
1225
|
-
|
|
1226
|
-
void outRx2
|
|
1227
|
-
.pipeThrough(
|
|
1228
|
-
new TransformStream({
|
|
1229
|
-
transform(
|
|
1230
|
-
chunk: SyncMessage,
|
|
1231
|
-
controller: { enqueue: (msg: SyncMessage) => void }
|
|
1232
|
-
) {
|
|
1233
|
-
trace && console.log(`${peer2id} -> ${peer1id}`, chunk);
|
|
1234
|
-
controller.enqueue(chunk);
|
|
1235
|
-
},
|
|
1236
|
-
})
|
|
1237
|
-
)
|
|
1238
|
-
.pipeTo(inTx1);
|
|
1239
|
-
|
|
1240
|
-
void outRx1
|
|
1241
|
-
.pipeThrough(
|
|
1242
|
-
new TransformStream({
|
|
1243
|
-
transform(
|
|
1244
|
-
chunk: SyncMessage,
|
|
1245
|
-
controller: { enqueue: (msg: SyncMessage) => void }
|
|
1246
|
-
) {
|
|
1247
|
-
trace && console.log(`${peer1id} -> ${peer2id}`, chunk);
|
|
1248
|
-
controller.enqueue(chunk);
|
|
1249
|
-
},
|
|
1250
|
-
})
|
|
1251
|
-
)
|
|
1252
|
-
.pipeTo(inTx2);
|
|
1253
|
-
|
|
1254
|
-
const peer2AsPeer: Peer = {
|
|
1255
|
-
id: peer2id,
|
|
1256
|
-
incoming: inRx1,
|
|
1257
|
-
outgoing: outTx1,
|
|
1258
|
-
role: peer2role,
|
|
1259
|
-
};
|
|
1260
|
-
|
|
1261
|
-
const peer1AsPeer: Peer = {
|
|
1262
|
-
id: peer1id,
|
|
1263
|
-
incoming: inRx2,
|
|
1264
|
-
outgoing: outTx2,
|
|
1265
|
-
role: peer1role,
|
|
1266
|
-
};
|
|
1267
1127
|
|
|
1268
|
-
return [peer1AsPeer, peer2AsPeer];
|
|
1269
|
-
}
|