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