cojson 0.7.0-alpha.37 → 0.7.0-alpha.39
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 +12 -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
|
@@ -0,0 +1,816 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { newRandomSessionID } from "../coValueCore.js";
|
|
3
|
+
import { LocalNode } from "../localNode.js";
|
|
4
|
+
import { expectMap } from "../coValue.js";
|
|
5
|
+
import { randomAnonymousAccountAndSessionID, shouldNotResolve, } from "./testUtils.js";
|
|
6
|
+
import { connectedPeers, newStreamPair } from "../streamUtils.js";
|
|
7
|
+
import { stableStringify } from "../jsonStringify.js";
|
|
8
|
+
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
9
|
+
const Crypto = await WasmCrypto.create();
|
|
10
|
+
test("Node replies with initial tx and header to empty subscribe", async () => {
|
|
11
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
12
|
+
const node = new LocalNode(admin, session, Crypto);
|
|
13
|
+
const group = node.createGroup();
|
|
14
|
+
const map = group.createMap();
|
|
15
|
+
map.set("hello", "world", "trusting");
|
|
16
|
+
const [inRx, inTx] = newStreamPair();
|
|
17
|
+
const [outRx, outTx] = newStreamPair();
|
|
18
|
+
node.syncManager.addPeer({
|
|
19
|
+
id: "test",
|
|
20
|
+
incoming: inRx,
|
|
21
|
+
outgoing: outTx,
|
|
22
|
+
role: "peer",
|
|
23
|
+
});
|
|
24
|
+
const writer = inTx.getWriter();
|
|
25
|
+
await writer.write({
|
|
26
|
+
action: "load",
|
|
27
|
+
id: map.core.id,
|
|
28
|
+
header: false,
|
|
29
|
+
sessions: {},
|
|
30
|
+
});
|
|
31
|
+
const reader = outRx.getReader();
|
|
32
|
+
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
|
33
|
+
expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
|
34
|
+
const mapTellKnownStateMsg = await reader.read();
|
|
35
|
+
expect(mapTellKnownStateMsg.value).toEqual({
|
|
36
|
+
action: "known",
|
|
37
|
+
...map.core.knownState(),
|
|
38
|
+
});
|
|
39
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
40
|
+
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
|
41
|
+
const newContentMsg = await reader.read();
|
|
42
|
+
expect(newContentMsg.value).toEqual({
|
|
43
|
+
action: "content",
|
|
44
|
+
id: map.core.id,
|
|
45
|
+
header: {
|
|
46
|
+
type: "comap",
|
|
47
|
+
ruleset: { type: "ownedByGroup", group: group.id },
|
|
48
|
+
meta: null,
|
|
49
|
+
createdAt: map.core.header.createdAt,
|
|
50
|
+
uniqueness: map.core.header.uniqueness,
|
|
51
|
+
},
|
|
52
|
+
new: {
|
|
53
|
+
[node.currentSessionID]: {
|
|
54
|
+
after: 0,
|
|
55
|
+
newTransactions: [
|
|
56
|
+
{
|
|
57
|
+
privacy: "trusting",
|
|
58
|
+
madeAt: map.core.sessionLogs.get(node.currentSessionID)
|
|
59
|
+
.transactions[0].madeAt,
|
|
60
|
+
changes: stableStringify([
|
|
61
|
+
{
|
|
62
|
+
op: "set",
|
|
63
|
+
key: "hello",
|
|
64
|
+
value: "world",
|
|
65
|
+
},
|
|
66
|
+
]),
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
lastSignature: map.core.sessionLogs.get(node.currentSessionID)
|
|
70
|
+
.lastSignature,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
test("Node replies with only new tx to subscribe with some known state", async () => {
|
|
76
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
77
|
+
const node = new LocalNode(admin, session, Crypto);
|
|
78
|
+
const group = node.createGroup();
|
|
79
|
+
const map = group.createMap();
|
|
80
|
+
map.set("hello", "world", "trusting");
|
|
81
|
+
map.set("goodbye", "world", "trusting");
|
|
82
|
+
const [inRx, inTx] = newStreamPair();
|
|
83
|
+
const [outRx, outTx] = newStreamPair();
|
|
84
|
+
node.syncManager.addPeer({
|
|
85
|
+
id: "test",
|
|
86
|
+
incoming: inRx,
|
|
87
|
+
outgoing: outTx,
|
|
88
|
+
role: "peer",
|
|
89
|
+
});
|
|
90
|
+
const writer = inTx.getWriter();
|
|
91
|
+
await writer.write({
|
|
92
|
+
action: "load",
|
|
93
|
+
id: map.core.id,
|
|
94
|
+
header: true,
|
|
95
|
+
sessions: {
|
|
96
|
+
[node.currentSessionID]: 1,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
const reader = outRx.getReader();
|
|
100
|
+
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
|
101
|
+
expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
|
102
|
+
const mapTellKnownStateMsg = await reader.read();
|
|
103
|
+
expect(mapTellKnownStateMsg.value).toEqual({
|
|
104
|
+
action: "known",
|
|
105
|
+
...map.core.knownState(),
|
|
106
|
+
});
|
|
107
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
108
|
+
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
|
109
|
+
const mapNewContentMsg = await reader.read();
|
|
110
|
+
expect(mapNewContentMsg.value).toEqual({
|
|
111
|
+
action: "content",
|
|
112
|
+
id: map.core.id,
|
|
113
|
+
header: undefined,
|
|
114
|
+
new: {
|
|
115
|
+
[node.currentSessionID]: {
|
|
116
|
+
after: 1,
|
|
117
|
+
newTransactions: [
|
|
118
|
+
{
|
|
119
|
+
privacy: "trusting",
|
|
120
|
+
madeAt: map.core.sessionLogs.get(node.currentSessionID)
|
|
121
|
+
.transactions[1].madeAt,
|
|
122
|
+
changes: stableStringify([
|
|
123
|
+
{
|
|
124
|
+
op: "set",
|
|
125
|
+
key: "goodbye",
|
|
126
|
+
value: "world",
|
|
127
|
+
},
|
|
128
|
+
]),
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
lastSignature: map.core.sessionLogs.get(node.currentSessionID)
|
|
132
|
+
.lastSignature,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
test.todo("TODO: node only replies with new tx to subscribe with some known state, even in the depended on coValues");
|
|
138
|
+
test("After subscribing, node sends own known state and new txs to peer", async () => {
|
|
139
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
140
|
+
const node = new LocalNode(admin, session, Crypto);
|
|
141
|
+
const group = node.createGroup();
|
|
142
|
+
const map = group.createMap();
|
|
143
|
+
const [inRx, inTx] = newStreamPair();
|
|
144
|
+
const [outRx, outTx] = newStreamPair();
|
|
145
|
+
node.syncManager.addPeer({
|
|
146
|
+
id: "test",
|
|
147
|
+
incoming: inRx,
|
|
148
|
+
outgoing: outTx,
|
|
149
|
+
role: "peer",
|
|
150
|
+
});
|
|
151
|
+
const writer = inTx.getWriter();
|
|
152
|
+
await writer.write({
|
|
153
|
+
action: "load",
|
|
154
|
+
id: map.core.id,
|
|
155
|
+
header: false,
|
|
156
|
+
sessions: {
|
|
157
|
+
[node.currentSessionID]: 0,
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
const reader = outRx.getReader();
|
|
161
|
+
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
|
162
|
+
expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
|
163
|
+
const mapTellKnownStateMsg = await reader.read();
|
|
164
|
+
expect(mapTellKnownStateMsg.value).toEqual({
|
|
165
|
+
action: "known",
|
|
166
|
+
...map.core.knownState(),
|
|
167
|
+
});
|
|
168
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
169
|
+
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
|
170
|
+
const mapNewContentHeaderOnlyMsg = await reader.read();
|
|
171
|
+
expect(mapNewContentHeaderOnlyMsg.value).toEqual({
|
|
172
|
+
action: "content",
|
|
173
|
+
id: map.core.id,
|
|
174
|
+
header: map.core.header,
|
|
175
|
+
new: {},
|
|
176
|
+
});
|
|
177
|
+
map.set("hello", "world", "trusting");
|
|
178
|
+
const mapEditMsg1 = await reader.read();
|
|
179
|
+
expect(mapEditMsg1.value).toEqual({
|
|
180
|
+
action: "content",
|
|
181
|
+
id: map.core.id,
|
|
182
|
+
new: {
|
|
183
|
+
[node.currentSessionID]: {
|
|
184
|
+
after: 0,
|
|
185
|
+
newTransactions: [
|
|
186
|
+
{
|
|
187
|
+
privacy: "trusting",
|
|
188
|
+
madeAt: map.core.sessionLogs.get(node.currentSessionID)
|
|
189
|
+
.transactions[0].madeAt,
|
|
190
|
+
changes: stableStringify([
|
|
191
|
+
{
|
|
192
|
+
op: "set",
|
|
193
|
+
key: "hello",
|
|
194
|
+
value: "world",
|
|
195
|
+
},
|
|
196
|
+
]),
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
lastSignature: map.core.sessionLogs.get(node.currentSessionID)
|
|
200
|
+
.lastSignature,
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
map.set("goodbye", "world", "trusting");
|
|
205
|
+
const mapEditMsg2 = await reader.read();
|
|
206
|
+
expect(mapEditMsg2.value).toEqual({
|
|
207
|
+
action: "content",
|
|
208
|
+
id: map.core.id,
|
|
209
|
+
new: {
|
|
210
|
+
[node.currentSessionID]: {
|
|
211
|
+
after: 1,
|
|
212
|
+
newTransactions: [
|
|
213
|
+
{
|
|
214
|
+
privacy: "trusting",
|
|
215
|
+
madeAt: map.core.sessionLogs.get(node.currentSessionID)
|
|
216
|
+
.transactions[1].madeAt,
|
|
217
|
+
changes: stableStringify([
|
|
218
|
+
{
|
|
219
|
+
op: "set",
|
|
220
|
+
key: "goodbye",
|
|
221
|
+
value: "world",
|
|
222
|
+
},
|
|
223
|
+
]),
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
lastSignature: map.core.sessionLogs.get(node.currentSessionID)
|
|
227
|
+
.lastSignature,
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
test("Client replies with known new content to tellKnownState from server", async () => {
|
|
233
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
234
|
+
const node = new LocalNode(admin, session, Crypto);
|
|
235
|
+
const group = node.createGroup();
|
|
236
|
+
const map = group.createMap();
|
|
237
|
+
map.set("hello", "world", "trusting");
|
|
238
|
+
const [inRx, inTx] = newStreamPair();
|
|
239
|
+
const [outRx, outTx] = newStreamPair();
|
|
240
|
+
node.syncManager.addPeer({
|
|
241
|
+
id: "test",
|
|
242
|
+
incoming: inRx,
|
|
243
|
+
outgoing: outTx,
|
|
244
|
+
role: "peer",
|
|
245
|
+
});
|
|
246
|
+
const reader = outRx.getReader();
|
|
247
|
+
// expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
|
248
|
+
const writer = inTx.getWriter();
|
|
249
|
+
await writer.write({
|
|
250
|
+
action: "known",
|
|
251
|
+
id: map.core.id,
|
|
252
|
+
header: false,
|
|
253
|
+
sessions: {
|
|
254
|
+
[node.currentSessionID]: 0,
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
|
258
|
+
expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
|
259
|
+
const mapTellKnownStateMsg = await reader.read();
|
|
260
|
+
expect(mapTellKnownStateMsg.value).toEqual({
|
|
261
|
+
action: "known",
|
|
262
|
+
...map.core.knownState(),
|
|
263
|
+
});
|
|
264
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
265
|
+
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
|
266
|
+
const mapNewContentMsg = await reader.read();
|
|
267
|
+
expect(mapNewContentMsg.value).toEqual({
|
|
268
|
+
action: "content",
|
|
269
|
+
id: map.core.id,
|
|
270
|
+
header: map.core.header,
|
|
271
|
+
new: {
|
|
272
|
+
[node.currentSessionID]: {
|
|
273
|
+
after: 0,
|
|
274
|
+
newTransactions: [
|
|
275
|
+
{
|
|
276
|
+
privacy: "trusting",
|
|
277
|
+
madeAt: map.core.sessionLogs.get(node.currentSessionID)
|
|
278
|
+
.transactions[0].madeAt,
|
|
279
|
+
changes: stableStringify([
|
|
280
|
+
{
|
|
281
|
+
op: "set",
|
|
282
|
+
key: "hello",
|
|
283
|
+
value: "world",
|
|
284
|
+
},
|
|
285
|
+
]),
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
lastSignature: map.core.sessionLogs.get(node.currentSessionID)
|
|
289
|
+
.lastSignature,
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
test("No matter the optimistic known state, node respects invalid known state messages and resyncs", async () => {
|
|
295
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
296
|
+
const node = new LocalNode(admin, session, Crypto);
|
|
297
|
+
const group = node.createGroup();
|
|
298
|
+
const map = group.createMap();
|
|
299
|
+
const [inRx, inTx] = newStreamPair();
|
|
300
|
+
const [outRx, outTx] = newStreamPair();
|
|
301
|
+
node.syncManager.addPeer({
|
|
302
|
+
id: "test",
|
|
303
|
+
incoming: inRx,
|
|
304
|
+
outgoing: outTx,
|
|
305
|
+
role: "peer",
|
|
306
|
+
});
|
|
307
|
+
const writer = inTx.getWriter();
|
|
308
|
+
await writer.write({
|
|
309
|
+
action: "load",
|
|
310
|
+
id: map.core.id,
|
|
311
|
+
header: false,
|
|
312
|
+
sessions: {
|
|
313
|
+
[node.currentSessionID]: 0,
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
const reader = outRx.getReader();
|
|
317
|
+
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
|
318
|
+
expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
|
319
|
+
const mapTellKnownStateMsg = await reader.read();
|
|
320
|
+
expect(mapTellKnownStateMsg.value).toEqual({
|
|
321
|
+
action: "known",
|
|
322
|
+
...map.core.knownState(),
|
|
323
|
+
});
|
|
324
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
325
|
+
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
|
326
|
+
const mapNewContentHeaderOnlyMsg = await reader.read();
|
|
327
|
+
expect(mapNewContentHeaderOnlyMsg.value).toEqual({
|
|
328
|
+
action: "content",
|
|
329
|
+
id: map.core.id,
|
|
330
|
+
header: map.core.header,
|
|
331
|
+
new: {},
|
|
332
|
+
});
|
|
333
|
+
map.set("hello", "world", "trusting");
|
|
334
|
+
map.set("goodbye", "world", "trusting");
|
|
335
|
+
const _mapEditMsgs = await reader.read();
|
|
336
|
+
console.log("Sending correction");
|
|
337
|
+
await writer.write({
|
|
338
|
+
action: "known",
|
|
339
|
+
isCorrection: true,
|
|
340
|
+
id: map.core.id,
|
|
341
|
+
header: true,
|
|
342
|
+
sessions: {
|
|
343
|
+
[node.currentSessionID]: 1,
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
const newContentAfterWrongAssumedState = await reader.read();
|
|
347
|
+
expect(newContentAfterWrongAssumedState.value).toEqual({
|
|
348
|
+
action: "content",
|
|
349
|
+
id: map.core.id,
|
|
350
|
+
header: undefined,
|
|
351
|
+
new: {
|
|
352
|
+
[node.currentSessionID]: {
|
|
353
|
+
after: 1,
|
|
354
|
+
newTransactions: [
|
|
355
|
+
{
|
|
356
|
+
privacy: "trusting",
|
|
357
|
+
madeAt: map.core.sessionLogs.get(node.currentSessionID)
|
|
358
|
+
.transactions[1].madeAt,
|
|
359
|
+
changes: stableStringify([
|
|
360
|
+
{
|
|
361
|
+
op: "set",
|
|
362
|
+
key: "goodbye",
|
|
363
|
+
value: "world",
|
|
364
|
+
},
|
|
365
|
+
]),
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
lastSignature: map.core.sessionLogs.get(node.currentSessionID)
|
|
369
|
+
.lastSignature,
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
test("If we add a peer, but it never subscribes to a coValue, it won't get any messages", async () => {
|
|
375
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
376
|
+
const node = new LocalNode(admin, session, Crypto);
|
|
377
|
+
const group = node.createGroup();
|
|
378
|
+
const map = group.createMap();
|
|
379
|
+
const [inRx, _inTx] = newStreamPair();
|
|
380
|
+
const [outRx, outTx] = newStreamPair();
|
|
381
|
+
node.syncManager.addPeer({
|
|
382
|
+
id: "test",
|
|
383
|
+
incoming: inRx,
|
|
384
|
+
outgoing: outTx,
|
|
385
|
+
role: "peer",
|
|
386
|
+
});
|
|
387
|
+
map.set("hello", "world", "trusting");
|
|
388
|
+
const reader = outRx.getReader();
|
|
389
|
+
await expect(shouldNotResolve(reader.read(), { timeout: 100 })).resolves.toBeUndefined();
|
|
390
|
+
});
|
|
391
|
+
test.todo("If we add a server peer, all updates to all coValues are sent to it, even if it doesn't subscribe", async () => {
|
|
392
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
393
|
+
const node = new LocalNode(admin, session, Crypto);
|
|
394
|
+
const group = node.createGroup();
|
|
395
|
+
const map = group.createMap();
|
|
396
|
+
const [inRx, _inTx] = newStreamPair();
|
|
397
|
+
const [outRx, outTx] = newStreamPair();
|
|
398
|
+
node.syncManager.addPeer({
|
|
399
|
+
id: "test",
|
|
400
|
+
incoming: inRx,
|
|
401
|
+
outgoing: outTx,
|
|
402
|
+
role: "server",
|
|
403
|
+
});
|
|
404
|
+
const reader = outRx.getReader();
|
|
405
|
+
// expect((await reader.read()).value).toMatchObject({
|
|
406
|
+
// action: "load",
|
|
407
|
+
// id: adminID,
|
|
408
|
+
// });
|
|
409
|
+
expect((await reader.read()).value).toMatchObject({
|
|
410
|
+
action: "load",
|
|
411
|
+
id: group.core.id,
|
|
412
|
+
});
|
|
413
|
+
const mapSubscribeMsg = await reader.read();
|
|
414
|
+
expect(mapSubscribeMsg.value).toEqual({
|
|
415
|
+
action: "load",
|
|
416
|
+
id: map.core.id,
|
|
417
|
+
header: true,
|
|
418
|
+
sessions: {},
|
|
419
|
+
});
|
|
420
|
+
map.set("hello", "world", "trusting");
|
|
421
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
422
|
+
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
|
423
|
+
const mapNewContentMsg = await reader.read();
|
|
424
|
+
expect(mapNewContentMsg.value).toEqual({
|
|
425
|
+
action: "content",
|
|
426
|
+
id: map.core.id,
|
|
427
|
+
header: map.core.header,
|
|
428
|
+
new: {
|
|
429
|
+
[node.currentSessionID]: {
|
|
430
|
+
after: 0,
|
|
431
|
+
newTransactions: [
|
|
432
|
+
{
|
|
433
|
+
privacy: "trusting",
|
|
434
|
+
madeAt: map.core.sessionLogs.get(node.currentSessionID).transactions[0].madeAt,
|
|
435
|
+
changes: stableStringify([
|
|
436
|
+
{
|
|
437
|
+
op: "set",
|
|
438
|
+
key: "hello",
|
|
439
|
+
value: "world",
|
|
440
|
+
},
|
|
441
|
+
]),
|
|
442
|
+
},
|
|
443
|
+
],
|
|
444
|
+
lastSignature: map.core.sessionLogs.get(node.currentSessionID).lastSignature,
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
test.skip("If we add a server peer, newly created coValues are auto-subscribed to", async () => {
|
|
450
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
451
|
+
const node = new LocalNode(admin, session, Crypto);
|
|
452
|
+
const group = node.createGroup();
|
|
453
|
+
const [inRx, _inTx] = newStreamPair();
|
|
454
|
+
const [outRx, outTx] = newStreamPair();
|
|
455
|
+
node.syncManager.addPeer({
|
|
456
|
+
id: "test",
|
|
457
|
+
incoming: inRx,
|
|
458
|
+
outgoing: outTx,
|
|
459
|
+
role: "server",
|
|
460
|
+
});
|
|
461
|
+
const reader = outRx.getReader();
|
|
462
|
+
// expect((await reader.read()).value).toMatchObject({
|
|
463
|
+
// action: "load",
|
|
464
|
+
// id: admin.id,
|
|
465
|
+
// });
|
|
466
|
+
expect((await reader.read()).value).toMatchObject({
|
|
467
|
+
action: "load",
|
|
468
|
+
id: group.core.id,
|
|
469
|
+
});
|
|
470
|
+
const map = group.createMap();
|
|
471
|
+
const mapSubscribeMsg = await reader.read();
|
|
472
|
+
expect(mapSubscribeMsg.value).toEqual({
|
|
473
|
+
action: "load",
|
|
474
|
+
...map.core.knownState(),
|
|
475
|
+
});
|
|
476
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(adminID));
|
|
477
|
+
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
|
478
|
+
const mapContentMsg = await reader.read();
|
|
479
|
+
expect(mapContentMsg.value).toEqual({
|
|
480
|
+
action: "content",
|
|
481
|
+
id: map.core.id,
|
|
482
|
+
header: map.core.header,
|
|
483
|
+
new: {},
|
|
484
|
+
});
|
|
485
|
+
});
|
|
486
|
+
test.todo("TODO: when receiving a subscribe response that is behind our optimistic state (due to already sent content), we ignore it");
|
|
487
|
+
test("When we connect a new server peer, we try to sync all existing coValues to it", async () => {
|
|
488
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
489
|
+
const node = new LocalNode(admin, session, Crypto);
|
|
490
|
+
const group = node.createGroup();
|
|
491
|
+
const map = group.createMap();
|
|
492
|
+
const [inRx, _inTx] = newStreamPair();
|
|
493
|
+
const [outRx, outTx] = newStreamPair();
|
|
494
|
+
node.syncManager.addPeer({
|
|
495
|
+
id: "test",
|
|
496
|
+
incoming: inRx,
|
|
497
|
+
outgoing: outTx,
|
|
498
|
+
role: "server",
|
|
499
|
+
});
|
|
500
|
+
const reader = outRx.getReader();
|
|
501
|
+
// const _adminSubscribeMessage = await reader.read();
|
|
502
|
+
const groupSubscribeMessage = await reader.read();
|
|
503
|
+
expect(groupSubscribeMessage.value).toEqual({
|
|
504
|
+
action: "load",
|
|
505
|
+
...group.core.knownState(),
|
|
506
|
+
});
|
|
507
|
+
const secondMessage = await reader.read();
|
|
508
|
+
expect(secondMessage.value).toEqual({
|
|
509
|
+
action: "load",
|
|
510
|
+
...map.core.knownState(),
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
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 () => {
|
|
514
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
515
|
+
const node = new LocalNode(admin, session, Crypto);
|
|
516
|
+
const group = node.createGroup();
|
|
517
|
+
const map = group.createMap();
|
|
518
|
+
const [inRx, inTx] = newStreamPair();
|
|
519
|
+
const [outRx, outTx] = newStreamPair();
|
|
520
|
+
node.syncManager.addPeer({
|
|
521
|
+
id: "test",
|
|
522
|
+
incoming: inRx,
|
|
523
|
+
outgoing: outTx,
|
|
524
|
+
role: "peer",
|
|
525
|
+
});
|
|
526
|
+
const writer = inTx.getWriter();
|
|
527
|
+
await writer.write({
|
|
528
|
+
action: "load",
|
|
529
|
+
id: map.core.id,
|
|
530
|
+
header: true,
|
|
531
|
+
sessions: {
|
|
532
|
+
[node.currentSessionID]: 1,
|
|
533
|
+
},
|
|
534
|
+
});
|
|
535
|
+
const reader = outRx.getReader();
|
|
536
|
+
// expect((await reader.read()).value).toMatchObject(admStateEx(admin.id));
|
|
537
|
+
expect((await reader.read()).value).toMatchObject(groupStateEx(group));
|
|
538
|
+
const mapTellKnownState = await reader.read();
|
|
539
|
+
expect(mapTellKnownState.value).toEqual({
|
|
540
|
+
action: "known",
|
|
541
|
+
...map.core.knownState(),
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
test.skip("When replaying creation and transactions of a coValue as new content, the receiving peer integrates this information", async () => {
|
|
545
|
+
// 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
|
|
546
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
547
|
+
const node1 = new LocalNode(admin, session, Crypto);
|
|
548
|
+
const group = node1.createGroup();
|
|
549
|
+
const [inRx1, inTx1] = newStreamPair();
|
|
550
|
+
const [outRx1, outTx1] = newStreamPair();
|
|
551
|
+
node1.syncManager.addPeer({
|
|
552
|
+
id: "test2",
|
|
553
|
+
incoming: inRx1,
|
|
554
|
+
outgoing: outTx1,
|
|
555
|
+
role: "server",
|
|
556
|
+
});
|
|
557
|
+
const to1 = inTx1.getWriter();
|
|
558
|
+
const from1 = outRx1.getReader();
|
|
559
|
+
const node2 = new LocalNode(admin, newRandomSessionID(admin.id), Crypto);
|
|
560
|
+
const [inRx2, inTx2] = newStreamPair();
|
|
561
|
+
const [outRx2, outTx2] = newStreamPair();
|
|
562
|
+
node2.syncManager.addPeer({
|
|
563
|
+
id: "test1",
|
|
564
|
+
incoming: inRx2,
|
|
565
|
+
outgoing: outTx2,
|
|
566
|
+
role: "client",
|
|
567
|
+
});
|
|
568
|
+
const to2 = inTx2.getWriter();
|
|
569
|
+
const from2 = outRx2.getReader();
|
|
570
|
+
const adminSubscribeMessage = await from1.read();
|
|
571
|
+
expect(adminSubscribeMessage.value).toMatchObject({
|
|
572
|
+
action: "load",
|
|
573
|
+
id: admin.id,
|
|
574
|
+
});
|
|
575
|
+
const groupSubscribeMsg = await from1.read();
|
|
576
|
+
expect(groupSubscribeMsg.value).toMatchObject({
|
|
577
|
+
action: "load",
|
|
578
|
+
id: group.core.id,
|
|
579
|
+
});
|
|
580
|
+
await to2.write(adminSubscribeMessage.value);
|
|
581
|
+
await to2.write(groupSubscribeMsg.value);
|
|
582
|
+
// const adminTellKnownStateMsg = await from2.read();
|
|
583
|
+
// expect(adminTellKnownStateMsg.value).toMatchObject(admStateEx(admin.id));
|
|
584
|
+
const groupTellKnownStateMsg = await from2.read();
|
|
585
|
+
expect(groupTellKnownStateMsg.value).toMatchObject(groupStateEx(group));
|
|
586
|
+
expect(node2.syncManager.peers["test1"].optimisticKnownStates[group.core.id]).toBeDefined();
|
|
587
|
+
// await to1.write(adminTellKnownStateMsg.value!);
|
|
588
|
+
await to1.write(groupTellKnownStateMsg.value);
|
|
589
|
+
// const adminContentMsg = await from1.read();
|
|
590
|
+
// expect(adminContentMsg.value).toMatchObject(admContEx(admin.id));
|
|
591
|
+
const groupContentMsg = await from1.read();
|
|
592
|
+
expect(groupContentMsg.value).toMatchObject(groupContentEx(group));
|
|
593
|
+
// await to2.write(adminContentMsg.value!);
|
|
594
|
+
await to2.write(groupContentMsg.value);
|
|
595
|
+
const map = group.createMap();
|
|
596
|
+
const mapSubscriptionMsg = await from1.read();
|
|
597
|
+
expect(mapSubscriptionMsg.value).toMatchObject({
|
|
598
|
+
action: "load",
|
|
599
|
+
id: map.core.id,
|
|
600
|
+
});
|
|
601
|
+
const mapNewContentMsg = await from1.read();
|
|
602
|
+
expect(mapNewContentMsg.value).toEqual({
|
|
603
|
+
action: "content",
|
|
604
|
+
id: map.core.id,
|
|
605
|
+
header: map.core.header,
|
|
606
|
+
new: {},
|
|
607
|
+
});
|
|
608
|
+
await to2.write(mapSubscriptionMsg.value);
|
|
609
|
+
const mapTellKnownStateMsg = await from2.read();
|
|
610
|
+
expect(mapTellKnownStateMsg.value).toEqual({
|
|
611
|
+
action: "known",
|
|
612
|
+
id: map.core.id,
|
|
613
|
+
header: false,
|
|
614
|
+
sessions: {},
|
|
615
|
+
});
|
|
616
|
+
expect(node2.coValues[map.core.id]?.state).toEqual("loading");
|
|
617
|
+
await to2.write(mapNewContentMsg.value);
|
|
618
|
+
map.set("hello", "world", "trusting");
|
|
619
|
+
const mapEditMsg = await from1.read();
|
|
620
|
+
await to2.write(mapEditMsg.value);
|
|
621
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
622
|
+
expect(expectMap(node2.expectCoValueLoaded(map.core.id).getCurrentContent()).get("hello")).toEqual("world");
|
|
623
|
+
});
|
|
624
|
+
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 () => {
|
|
625
|
+
// 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
|
|
626
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
627
|
+
const node1 = new LocalNode(admin, session, Crypto);
|
|
628
|
+
const group = node1.createGroup();
|
|
629
|
+
const map = group.createMap();
|
|
630
|
+
map.set("hello", "world", "trusting");
|
|
631
|
+
const node2 = new LocalNode(admin, newRandomSessionID(admin.id), Crypto);
|
|
632
|
+
const [node1asPeer, node2asPeer] = connectedPeers("peer1", "peer2");
|
|
633
|
+
node1.syncManager.addPeer(node2asPeer);
|
|
634
|
+
node2.syncManager.addPeer(node1asPeer);
|
|
635
|
+
await node2.loadCoValueCore(map.core.id);
|
|
636
|
+
expect(expectMap(node2.expectCoValueLoaded(map.core.id).getCurrentContent()).get("hello")).toEqual("world");
|
|
637
|
+
});
|
|
638
|
+
test("Can sync a coValue through a server to another client", async () => {
|
|
639
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
640
|
+
const client1 = new LocalNode(admin, session, Crypto);
|
|
641
|
+
const group = client1.createGroup();
|
|
642
|
+
const map = group.createMap();
|
|
643
|
+
map.set("hello", "world", "trusting");
|
|
644
|
+
const [serverUser, serverSession] = randomAnonymousAccountAndSessionID();
|
|
645
|
+
const server = new LocalNode(serverUser, serverSession, Crypto);
|
|
646
|
+
const [serverAsPeer, client1AsPeer] = connectedPeers("server", "client1", {
|
|
647
|
+
peer1role: "server",
|
|
648
|
+
peer2role: "client",
|
|
649
|
+
trace: true,
|
|
650
|
+
});
|
|
651
|
+
client1.syncManager.addPeer(serverAsPeer);
|
|
652
|
+
server.syncManager.addPeer(client1AsPeer);
|
|
653
|
+
const client2 = new LocalNode(admin, newRandomSessionID(admin.id), Crypto);
|
|
654
|
+
const [serverAsOtherPeer, client2AsPeer] = connectedPeers("server", "client2", { peer1role: "server", peer2role: "client", trace: true });
|
|
655
|
+
client2.syncManager.addPeer(serverAsOtherPeer);
|
|
656
|
+
server.syncManager.addPeer(client2AsPeer);
|
|
657
|
+
const mapOnClient2 = await client2.loadCoValueCore(map.core.id);
|
|
658
|
+
if (mapOnClient2 === "unavailable") {
|
|
659
|
+
throw new Error("Map is unavailable");
|
|
660
|
+
}
|
|
661
|
+
expect(expectMap(mapOnClient2.getCurrentContent()).get("hello")).toEqual("world");
|
|
662
|
+
});
|
|
663
|
+
test("Can sync a coValue with private transactions through a server to another client", async () => {
|
|
664
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
665
|
+
const client1 = new LocalNode(admin, session, Crypto);
|
|
666
|
+
const group = client1.createGroup();
|
|
667
|
+
const map = group.createMap();
|
|
668
|
+
map.set("hello", "world", "private");
|
|
669
|
+
const [serverUser, serverSession] = randomAnonymousAccountAndSessionID();
|
|
670
|
+
const server = new LocalNode(serverUser, serverSession, Crypto);
|
|
671
|
+
const [serverAsPeer, client1AsPeer] = connectedPeers("server", "client1", {
|
|
672
|
+
trace: true,
|
|
673
|
+
peer1role: "server",
|
|
674
|
+
peer2role: "client",
|
|
675
|
+
});
|
|
676
|
+
client1.syncManager.addPeer(serverAsPeer);
|
|
677
|
+
server.syncManager.addPeer(client1AsPeer);
|
|
678
|
+
const client2 = new LocalNode(admin, newRandomSessionID(admin.id), Crypto);
|
|
679
|
+
const [serverAsOtherPeer, client2AsPeer] = connectedPeers("server", "client2", { trace: true, peer1role: "server", peer2role: "client" });
|
|
680
|
+
client2.syncManager.addPeer(serverAsOtherPeer);
|
|
681
|
+
server.syncManager.addPeer(client2AsPeer);
|
|
682
|
+
const mapOnClient2 = await client2.loadCoValueCore(map.core.id);
|
|
683
|
+
if (mapOnClient2 === "unavailable") {
|
|
684
|
+
throw new Error("Map is unavailable");
|
|
685
|
+
}
|
|
686
|
+
expect(expectMap(mapOnClient2.getCurrentContent()).get("hello")).toEqual("world");
|
|
687
|
+
});
|
|
688
|
+
test.skip("When a peer's incoming/readable stream closes, we remove the peer", async () => {
|
|
689
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
690
|
+
const node = new LocalNode(admin, session, Crypto);
|
|
691
|
+
const group = node.createGroup();
|
|
692
|
+
const [inRx, inTx] = newStreamPair();
|
|
693
|
+
const [outRx, outTx] = newStreamPair();
|
|
694
|
+
node.syncManager.addPeer({
|
|
695
|
+
id: "test",
|
|
696
|
+
incoming: inRx,
|
|
697
|
+
outgoing: outTx,
|
|
698
|
+
role: "server",
|
|
699
|
+
});
|
|
700
|
+
const reader = outRx.getReader();
|
|
701
|
+
// expect((await reader.read()).value).toMatchObject({
|
|
702
|
+
// action: "load",
|
|
703
|
+
// id: admin.id,
|
|
704
|
+
// });
|
|
705
|
+
expect((await reader.read()).value).toMatchObject({
|
|
706
|
+
action: "load",
|
|
707
|
+
id: group.core.id,
|
|
708
|
+
});
|
|
709
|
+
const map = group.createMap();
|
|
710
|
+
const mapSubscribeMsg = await reader.read();
|
|
711
|
+
expect(mapSubscribeMsg.value).toEqual({
|
|
712
|
+
action: "load",
|
|
713
|
+
...map.core.knownState(),
|
|
714
|
+
});
|
|
715
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
716
|
+
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
|
717
|
+
const mapContentMsg = await reader.read();
|
|
718
|
+
expect(mapContentMsg.value).toEqual({
|
|
719
|
+
action: "content",
|
|
720
|
+
id: map.core.id,
|
|
721
|
+
header: map.core.header,
|
|
722
|
+
new: {},
|
|
723
|
+
});
|
|
724
|
+
await inTx.abort();
|
|
725
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
726
|
+
expect(node.syncManager.peers["test"]).toBeUndefined();
|
|
727
|
+
});
|
|
728
|
+
test.skip("When a peer's outgoing/writable stream closes, we remove the peer", async () => {
|
|
729
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
730
|
+
const node = new LocalNode(admin, session, Crypto);
|
|
731
|
+
const group = node.createGroup();
|
|
732
|
+
const [inRx] = newStreamPair();
|
|
733
|
+
const [outRx, outTx] = newStreamPair();
|
|
734
|
+
node.syncManager.addPeer({
|
|
735
|
+
id: "test",
|
|
736
|
+
incoming: inRx,
|
|
737
|
+
outgoing: outTx,
|
|
738
|
+
role: "server",
|
|
739
|
+
});
|
|
740
|
+
const reader = outRx.getReader();
|
|
741
|
+
// expect((await reader.read()).value).toMatchObject({
|
|
742
|
+
// action: "load",
|
|
743
|
+
// id: admin.id,
|
|
744
|
+
// });
|
|
745
|
+
expect((await reader.read()).value).toMatchObject({
|
|
746
|
+
action: "load",
|
|
747
|
+
id: group.core.id,
|
|
748
|
+
});
|
|
749
|
+
const map = group.createMap();
|
|
750
|
+
const mapSubscribeMsg = await reader.read();
|
|
751
|
+
expect(mapSubscribeMsg.value).toEqual({
|
|
752
|
+
action: "load",
|
|
753
|
+
...map.core.knownState(),
|
|
754
|
+
});
|
|
755
|
+
// expect((await reader.read()).value).toMatchObject(admContEx(admin.id));
|
|
756
|
+
expect((await reader.read()).value).toMatchObject(groupContentEx(group));
|
|
757
|
+
const mapContentMsg = await reader.read();
|
|
758
|
+
expect(mapContentMsg.value).toEqual({
|
|
759
|
+
action: "content",
|
|
760
|
+
id: map.core.id,
|
|
761
|
+
header: map.core.header,
|
|
762
|
+
new: {},
|
|
763
|
+
});
|
|
764
|
+
reader.releaseLock();
|
|
765
|
+
await outRx.cancel();
|
|
766
|
+
map.set("hello", "world", "trusting");
|
|
767
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
768
|
+
expect(node.syncManager.peers["test"]).toBeUndefined();
|
|
769
|
+
});
|
|
770
|
+
test("If we start loading a coValue before connecting to a peer that has it, it will load it once we connect", async () => {
|
|
771
|
+
const [admin, session] = randomAnonymousAccountAndSessionID();
|
|
772
|
+
const node1 = new LocalNode(admin, session, Crypto);
|
|
773
|
+
const group = node1.createGroup();
|
|
774
|
+
const map = group.createMap();
|
|
775
|
+
map.set("hello", "world", "trusting");
|
|
776
|
+
const node2 = new LocalNode(admin, newRandomSessionID(admin.id), Crypto);
|
|
777
|
+
const [node1asPeer, node2asPeer] = connectedPeers("peer1", "peer2", {
|
|
778
|
+
peer1role: "server",
|
|
779
|
+
peer2role: "client",
|
|
780
|
+
trace: true,
|
|
781
|
+
});
|
|
782
|
+
node1.syncManager.addPeer(node2asPeer);
|
|
783
|
+
const mapOnNode2Promise = node2.loadCoValueCore(map.core.id);
|
|
784
|
+
expect(node2.coValues[map.core.id]?.state).toEqual("loading");
|
|
785
|
+
node2.syncManager.addPeer(node1asPeer);
|
|
786
|
+
const mapOnNode2 = await mapOnNode2Promise;
|
|
787
|
+
if (mapOnNode2 === "unavailable") {
|
|
788
|
+
throw new Error("Map is unavailable");
|
|
789
|
+
}
|
|
790
|
+
expect(expectMap(mapOnNode2.getCurrentContent()).get("hello")).toEqual("world");
|
|
791
|
+
});
|
|
792
|
+
function groupContentEx(group) {
|
|
793
|
+
return {
|
|
794
|
+
action: "content",
|
|
795
|
+
id: group.core.id,
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
function _admContEx(adminID) {
|
|
799
|
+
return {
|
|
800
|
+
action: "content",
|
|
801
|
+
id: adminID,
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
function groupStateEx(group) {
|
|
805
|
+
return {
|
|
806
|
+
action: "known",
|
|
807
|
+
id: group.core.id,
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
function _admStateEx(adminID) {
|
|
811
|
+
return {
|
|
812
|
+
action: "known",
|
|
813
|
+
id: adminID,
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
//# sourceMappingURL=sync.test.js.map
|