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