jazz-tools 0.7.0-alpha.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.eslintrc.cjs +24 -0
- package/.turbo/turbo-build.log +24 -0
- package/CHANGELOG.md +42 -0
- package/LICENSE.txt +19 -0
- package/README.md +3 -0
- package/dist/coValueInterfaces.js +8 -0
- package/dist/coValueInterfaces.js.map +1 -0
- package/dist/coValues/account/account.js +11 -0
- package/dist/coValues/account/account.js.map +1 -0
- package/dist/coValues/account/accountOf.js +150 -0
- package/dist/coValues/account/accountOf.js.map +1 -0
- package/dist/coValues/account/migration.js +4 -0
- package/dist/coValues/account/migration.js.map +1 -0
- package/dist/coValues/coList/coList.js +2 -0
- package/dist/coValues/coList/coList.js.map +1 -0
- package/dist/coValues/coList/coListOf.js +235 -0
- package/dist/coValues/coList/coListOf.js.map +1 -0
- package/dist/coValues/coList/internalDocs.js +2 -0
- package/dist/coValues/coList/internalDocs.js.map +1 -0
- package/dist/coValues/coMap/coMap.js +2 -0
- package/dist/coValues/coMap/coMap.js.map +1 -0
- package/dist/coValues/coMap/coMapOf.js +262 -0
- package/dist/coValues/coMap/coMapOf.js.map +1 -0
- package/dist/coValues/coMap/internalDocs.js +2 -0
- package/dist/coValues/coMap/internalDocs.js.map +1 -0
- package/dist/coValues/coStream/coStream.js +2 -0
- package/dist/coValues/coStream/coStream.js.map +1 -0
- package/dist/coValues/coStream/coStreamOf.js +244 -0
- package/dist/coValues/coStream/coStreamOf.js.map +1 -0
- package/dist/coValues/construction.js +34 -0
- package/dist/coValues/construction.js.map +1 -0
- package/dist/coValues/extensions/imageDef.js +36 -0
- package/dist/coValues/extensions/imageDef.js.map +1 -0
- package/dist/coValues/group/group.js +2 -0
- package/dist/coValues/group/group.js.map +1 -0
- package/dist/coValues/group/groupOf.js +109 -0
- package/dist/coValues/group/groupOf.js.map +1 -0
- package/dist/coValues/resolution.js +66 -0
- package/dist/coValues/resolution.js.map +1 -0
- package/dist/errors.js +2 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/refs.js +95 -0
- package/dist/refs.js.map +1 -0
- package/dist/schemaHelpers.js +14 -0
- package/dist/schemaHelpers.js.map +1 -0
- package/dist/subscriptionScope.js +81 -0
- package/dist/subscriptionScope.js.map +1 -0
- package/dist/tests/coList.test.js +207 -0
- package/dist/tests/coList.test.js.map +1 -0
- package/dist/tests/coMap.test.js +238 -0
- package/dist/tests/coMap.test.js.map +1 -0
- package/dist/tests/coStream.test.js +263 -0
- package/dist/tests/coStream.test.js.map +1 -0
- package/dist/tests/types.test.js +33 -0
- package/dist/tests/types.test.js.map +1 -0
- package/package.json +23 -0
- package/src/coValueInterfaces.ts +105 -0
- package/src/coValues/account/account.ts +106 -0
- package/src/coValues/account/accountOf.ts +284 -0
- package/src/coValues/account/migration.ts +12 -0
- package/src/coValues/coList/coList.ts +57 -0
- package/src/coValues/coList/coListOf.ts +377 -0
- package/src/coValues/coList/internalDocs.ts +1 -0
- package/src/coValues/coMap/coMap.ts +110 -0
- package/src/coValues/coMap/coMapOf.ts +451 -0
- package/src/coValues/coMap/internalDocs.ts +1 -0
- package/src/coValues/coStream/coStream.ts +63 -0
- package/src/coValues/coStream/coStreamOf.ts +404 -0
- package/src/coValues/construction.ts +110 -0
- package/src/coValues/extensions/imageDef.ts +51 -0
- package/src/coValues/group/group.ts +27 -0
- package/src/coValues/group/groupOf.ts +183 -0
- package/src/coValues/resolution.ts +111 -0
- package/src/errors.ts +1 -0
- package/src/index.ts +68 -0
- package/src/refs.ts +128 -0
- package/src/schemaHelpers.ts +72 -0
- package/src/subscriptionScope.ts +118 -0
- package/src/tests/coList.test.ts +283 -0
- package/src/tests/coMap.test.ts +357 -0
- package/src/tests/coStream.test.ts +415 -0
- package/src/tests/types.test.ts +37 -0
- package/tsconfig.json +15 -0
@@ -0,0 +1,283 @@
|
|
1
|
+
import { expect, describe, test, beforeEach } from "vitest";
|
2
|
+
|
3
|
+
import { webcrypto } from "node:crypto";
|
4
|
+
import { connectedPeers } from "cojson/src/streamUtils.js";
|
5
|
+
import { newRandomSessionID } from "cojson/src/coValueCore.js";
|
6
|
+
import { Effect, Queue } from "effect";
|
7
|
+
import { Co, S, Account, jazzReady } from "..";
|
8
|
+
|
9
|
+
if (!("crypto" in globalThis)) {
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
11
|
+
(globalThis as any).crypto = webcrypto;
|
12
|
+
}
|
13
|
+
|
14
|
+
beforeEach(async () => {
|
15
|
+
await jazzReady;
|
16
|
+
});
|
17
|
+
|
18
|
+
describe("Simple CoList operations", async () => {
|
19
|
+
const me = await Account.create({
|
20
|
+
name: "Hermes Puggington",
|
21
|
+
});
|
22
|
+
|
23
|
+
class TestList extends Co.list(S.string).as<TestList>() {}
|
24
|
+
|
25
|
+
const list = new TestList(["bread", "butter", "onion"], { owner: me });
|
26
|
+
|
27
|
+
test("Construction", () => {
|
28
|
+
expect(list[0]).toBe("bread");
|
29
|
+
expect(list[1]).toBe("butter");
|
30
|
+
expect(list[2]).toBe("onion");
|
31
|
+
expect(list._raw.asArray()).toEqual(["bread", "butter", "onion"]);
|
32
|
+
expect(list.length).toBe(3);
|
33
|
+
expect(list.map((item) => item.toUpperCase())).toEqual([
|
34
|
+
"BREAD",
|
35
|
+
"BUTTER",
|
36
|
+
"ONION",
|
37
|
+
]);
|
38
|
+
});
|
39
|
+
|
40
|
+
describe("Mutation", () => {
|
41
|
+
test("assignment", () => {
|
42
|
+
const list = new TestList(["bread", "butter", "onion"], {
|
43
|
+
owner: me,
|
44
|
+
});
|
45
|
+
list[1] = "margarine";
|
46
|
+
expect(list._raw.asArray()).toEqual([
|
47
|
+
"bread",
|
48
|
+
"margarine",
|
49
|
+
"onion",
|
50
|
+
]);
|
51
|
+
expect(list[1]).toBe("margarine");
|
52
|
+
});
|
53
|
+
|
54
|
+
test("push", () => {
|
55
|
+
const list = new TestList(["bread", "butter", "onion"], {
|
56
|
+
owner: me,
|
57
|
+
});
|
58
|
+
list.push("cheese");
|
59
|
+
expect(list[3]).toBe("cheese");
|
60
|
+
expect(list._raw.asArray()).toEqual([
|
61
|
+
"bread",
|
62
|
+
"butter",
|
63
|
+
"onion",
|
64
|
+
"cheese",
|
65
|
+
]);
|
66
|
+
});
|
67
|
+
|
68
|
+
test("unshift", () => {
|
69
|
+
const list = new TestList(["bread", "butter", "onion"], {
|
70
|
+
owner: me,
|
71
|
+
});
|
72
|
+
list.unshift("lettuce");
|
73
|
+
expect(list[0]).toBe("lettuce");
|
74
|
+
expect(list._raw.asArray()).toEqual([
|
75
|
+
"lettuce",
|
76
|
+
"bread",
|
77
|
+
"butter",
|
78
|
+
"onion",
|
79
|
+
]);
|
80
|
+
});
|
81
|
+
|
82
|
+
test("pop", () => {
|
83
|
+
const list = new TestList(["bread", "butter", "onion"], {
|
84
|
+
owner: me,
|
85
|
+
});
|
86
|
+
expect(list.pop()).toBe("onion");
|
87
|
+
expect(list.length).toBe(2);
|
88
|
+
expect(list._raw.asArray()).toEqual(["bread", "butter"]);
|
89
|
+
});
|
90
|
+
|
91
|
+
test("shift", () => {
|
92
|
+
const list = new TestList(["bread", "butter", "onion"], {
|
93
|
+
owner: me,
|
94
|
+
});
|
95
|
+
expect(list.shift()).toBe("bread");
|
96
|
+
expect(list.length).toBe(2);
|
97
|
+
expect(list._raw.asArray()).toEqual(["butter", "onion"]);
|
98
|
+
});
|
99
|
+
|
100
|
+
test("splice", () => {
|
101
|
+
const list = new TestList(["bread", "butter", "onion"], {
|
102
|
+
owner: me,
|
103
|
+
});
|
104
|
+
list.splice(1, 1, "salt", "pepper");
|
105
|
+
expect(list.length).toBe(4);
|
106
|
+
expect(list._raw.asArray()).toEqual([
|
107
|
+
"bread",
|
108
|
+
"salt",
|
109
|
+
"pepper",
|
110
|
+
"onion",
|
111
|
+
]);
|
112
|
+
});
|
113
|
+
});
|
114
|
+
});
|
115
|
+
|
116
|
+
describe("CoList resolution", async () => {
|
117
|
+
class TwiceNestedList extends Co.list(S.string).as<TwiceNestedList>() {
|
118
|
+
joined() {
|
119
|
+
return this.join(",");
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
class NestedList extends Co.list(TwiceNestedList).as<NestedList>() {}
|
124
|
+
|
125
|
+
class TestList extends Co.list(NestedList).as<TestList>() {}
|
126
|
+
|
127
|
+
const initNodeAndList = async () => {
|
128
|
+
const me = await Account.create({
|
129
|
+
name: "Hermes Puggington",
|
130
|
+
});
|
131
|
+
|
132
|
+
const list = new TestList(
|
133
|
+
[
|
134
|
+
new NestedList(
|
135
|
+
[new TwiceNestedList(["a", "b"], { owner: me })],
|
136
|
+
{ owner: me }
|
137
|
+
),
|
138
|
+
new NestedList(
|
139
|
+
[new TwiceNestedList(["c", "d"], { owner: me })],
|
140
|
+
{ owner: me }
|
141
|
+
),
|
142
|
+
],
|
143
|
+
{ owner: me }
|
144
|
+
);
|
145
|
+
|
146
|
+
return { me, list };
|
147
|
+
};
|
148
|
+
|
149
|
+
test("Construction", async () => {
|
150
|
+
const { list } = await initNodeAndList();
|
151
|
+
|
152
|
+
expect(list[0]?.[0]?.[0]).toBe("a");
|
153
|
+
expect(list[0]?.[0]?.joined()).toBe("a,b");
|
154
|
+
expect(list[0]?.[0]?.id).toBeDefined();
|
155
|
+
expect(list[1]?.[0]?.[0]).toBe("c");
|
156
|
+
});
|
157
|
+
|
158
|
+
test("Loading and availability", async () => {
|
159
|
+
const { me, list } = await initNodeAndList();
|
160
|
+
|
161
|
+
const [initialAsPeer, secondPeer] = connectedPeers(
|
162
|
+
"initial",
|
163
|
+
"second",
|
164
|
+
{ peer1role: "server", peer2role: "client" }
|
165
|
+
);
|
166
|
+
me._raw.core.node.syncManager.addPeer(secondPeer);
|
167
|
+
const meOnSecondPeer = await Account.become({
|
168
|
+
accountID: me.id,
|
169
|
+
accountSecret: me._raw.agentSecret,
|
170
|
+
peersToLoadFrom: [initialAsPeer],
|
171
|
+
sessionID: newRandomSessionID(me.id as any),
|
172
|
+
});
|
173
|
+
|
174
|
+
const loadedList = await TestList.load(list.id, { as: meOnSecondPeer });
|
175
|
+
|
176
|
+
expect(loadedList?.[0]).toBe(undefined);
|
177
|
+
expect(loadedList?._refs[0]?.id).toEqual(list[0]!.id);
|
178
|
+
|
179
|
+
const loadedNestedList = await NestedList.load(list[0]!.id, {
|
180
|
+
as: meOnSecondPeer,
|
181
|
+
});
|
182
|
+
|
183
|
+
expect(loadedList?.[0]).toBeDefined();
|
184
|
+
expect(loadedList?.[0]?.[0]).toBeUndefined();
|
185
|
+
expect(loadedList?.[0]?._refs[0]?.id).toEqual(list[0]![0]!.id);
|
186
|
+
expect(loadedList?._refs[0]?.value).toEqual(loadedNestedList);
|
187
|
+
|
188
|
+
const loadedTwiceNestedList = await TwiceNestedList.load(
|
189
|
+
list[0]![0]!.id,
|
190
|
+
{ as: meOnSecondPeer }
|
191
|
+
);
|
192
|
+
|
193
|
+
expect(loadedList?.[0]?.[0]).toBeDefined();
|
194
|
+
expect(loadedList?.[0]?.[0]?.[0]).toBe("a");
|
195
|
+
expect(loadedList?.[0]?.[0]?.joined()).toBe("a,b");
|
196
|
+
expect(loadedList?.[0]?._refs[0]?.id).toEqual(list[0]?.[0]?.id);
|
197
|
+
expect(loadedList?.[0]?._refs[0]?.value).toEqual(
|
198
|
+
loadedTwiceNestedList
|
199
|
+
);
|
200
|
+
|
201
|
+
const otherNestedList = new NestedList(
|
202
|
+
[new TwiceNestedList(["e", "f"], { owner: meOnSecondPeer })],
|
203
|
+
{ owner: meOnSecondPeer }
|
204
|
+
);
|
205
|
+
|
206
|
+
loadedList![0] = otherNestedList;
|
207
|
+
expect(loadedList?.[0]).toEqual(otherNestedList);
|
208
|
+
expect(loadedList?._refs[0]?.id).toEqual(otherNestedList.id);
|
209
|
+
});
|
210
|
+
|
211
|
+
test("Subscription & auto-resolution", async () => {
|
212
|
+
const { me, list } = await initNodeAndList();
|
213
|
+
|
214
|
+
const [initialAsPeer, secondPeer] = connectedPeers(
|
215
|
+
"initial",
|
216
|
+
"second",
|
217
|
+
{ peer1role: "server", peer2role: "client" }
|
218
|
+
);
|
219
|
+
me._raw.core.node.syncManager.addPeer(secondPeer);
|
220
|
+
const meOnSecondPeer = await Account.become({
|
221
|
+
accountID: me.id,
|
222
|
+
accountSecret: me._raw.agentSecret,
|
223
|
+
peersToLoadFrom: [initialAsPeer],
|
224
|
+
sessionID: newRandomSessionID(me.id as any),
|
225
|
+
});
|
226
|
+
|
227
|
+
await Effect.runPromise(
|
228
|
+
Effect.gen(function* ($) {
|
229
|
+
const queue = yield* $(Queue.unbounded<TestList>());
|
230
|
+
|
231
|
+
TestList.subscribe(
|
232
|
+
list.id,
|
233
|
+
{ as: meOnSecondPeer },
|
234
|
+
(subscribedList) => {
|
235
|
+
console.log(
|
236
|
+
"subscribedList?.[0]?.[0]?.[0]",
|
237
|
+
subscribedList?.[0]?.[0]?.[0]
|
238
|
+
);
|
239
|
+
Effect.runPromise(Queue.offer(queue, subscribedList));
|
240
|
+
}
|
241
|
+
);
|
242
|
+
|
243
|
+
const update1 = yield* $(Queue.take(queue));
|
244
|
+
expect(update1?.[0]).toEqual(undefined);
|
245
|
+
|
246
|
+
const update2 = yield* $(Queue.take(queue));
|
247
|
+
expect(update2?.[0]).toBeDefined();
|
248
|
+
expect(update2?.[0]?.[0]).toBeUndefined();
|
249
|
+
|
250
|
+
const update3 = yield* $(Queue.take(queue));
|
251
|
+
expect(update3?.[0]?.[0]).toBeDefined();
|
252
|
+
expect(update3?.[0]?.[0]?.[0]).toBe("a");
|
253
|
+
expect(update3?.[0]?.[0]?.joined()).toBe("a,b");
|
254
|
+
|
255
|
+
update3[0]![0]![0] = "x";
|
256
|
+
|
257
|
+
const update4 = yield* $(Queue.take(queue));
|
258
|
+
expect(update4?.[0]?.[0]?.[0]).toBe("x");
|
259
|
+
|
260
|
+
// When assigning a new nested value, we get an update
|
261
|
+
|
262
|
+
const newTwiceNestedList = new TwiceNestedList(["y", "z"], {
|
263
|
+
owner: meOnSecondPeer,
|
264
|
+
});
|
265
|
+
|
266
|
+
const newNestedList = new NestedList([newTwiceNestedList], {
|
267
|
+
owner: meOnSecondPeer,
|
268
|
+
});
|
269
|
+
|
270
|
+
update4[0] = newNestedList;
|
271
|
+
|
272
|
+
const update5 = yield* $(Queue.take(queue));
|
273
|
+
expect(update5?.[0]?.[0]?.[0]).toBe("y");
|
274
|
+
expect(update5?.[0]?.[0]?.joined()).toBe("y,z");
|
275
|
+
|
276
|
+
// we get updates when the new nested value changes
|
277
|
+
newTwiceNestedList[0] = "w";
|
278
|
+
const update6 = yield* $(Queue.take(queue));
|
279
|
+
expect(update6?.[0]?.[0]?.[0]).toBe("w");
|
280
|
+
})
|
281
|
+
);
|
282
|
+
});
|
283
|
+
});
|
@@ -0,0 +1,357 @@
|
|
1
|
+
import { expect, describe, test, beforeEach } from "vitest";
|
2
|
+
|
3
|
+
import { webcrypto } from "node:crypto";
|
4
|
+
import { connectedPeers } from "cojson/src/streamUtils.js";
|
5
|
+
import { newRandomSessionID } from "cojson/src/coValueCore.js";
|
6
|
+
import { Effect, Queue } from "effect";
|
7
|
+
import { Co, S, Account, jazzReady } from "..";
|
8
|
+
|
9
|
+
if (!("crypto" in globalThis)) {
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
11
|
+
(globalThis as any).crypto = webcrypto;
|
12
|
+
}
|
13
|
+
|
14
|
+
beforeEach(async () => {
|
15
|
+
await jazzReady;
|
16
|
+
});
|
17
|
+
|
18
|
+
describe("Simple CoMap operations", async () => {
|
19
|
+
const me = await Account.create({
|
20
|
+
name: "Hermes Puggington",
|
21
|
+
});
|
22
|
+
|
23
|
+
class TestMap extends Co.map({
|
24
|
+
color: S.string,
|
25
|
+
height: S.number,
|
26
|
+
birthday: S.Date,
|
27
|
+
name: S.optional(S.string),
|
28
|
+
}).as<TestMap>() {
|
29
|
+
get roughColor() {
|
30
|
+
return this.color + "ish";
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
const birthday = new Date();
|
35
|
+
|
36
|
+
const map = new TestMap(
|
37
|
+
{
|
38
|
+
color: "red",
|
39
|
+
height: 10,
|
40
|
+
birthday: birthday,
|
41
|
+
},
|
42
|
+
{ owner: me }
|
43
|
+
);
|
44
|
+
|
45
|
+
test("Construction", () => {
|
46
|
+
expect(map.color).toEqual("red");
|
47
|
+
expect(map.roughColor).toEqual("redish");
|
48
|
+
expect(map.height).toEqual(10);
|
49
|
+
expect(map.birthday).toEqual(birthday);
|
50
|
+
expect(map._raw.get("birthday")).toEqual(birthday.toISOString());
|
51
|
+
});
|
52
|
+
|
53
|
+
describe("Mutation", () => {
|
54
|
+
test("assignment", () => {
|
55
|
+
map.color = "blue";
|
56
|
+
expect(map.color).toEqual("blue");
|
57
|
+
expect(map._raw.get("color")).toEqual("blue");
|
58
|
+
const newBirthday = new Date();
|
59
|
+
map.birthday = newBirthday;
|
60
|
+
expect(map.birthday).toEqual(newBirthday);
|
61
|
+
expect(map._raw.get("birthday")).toEqual(
|
62
|
+
newBirthday.toISOString()
|
63
|
+
);
|
64
|
+
|
65
|
+
Object.assign(map, { color: "green", height: 20 });
|
66
|
+
expect(map.color).toEqual("green");
|
67
|
+
expect(map._raw.get("color")).toEqual("green");
|
68
|
+
expect(map.height).toEqual(20);
|
69
|
+
expect(map._raw.get("height")).toEqual(20);
|
70
|
+
|
71
|
+
map.name = "Secret name";
|
72
|
+
expect(map.name).toEqual("Secret name");
|
73
|
+
delete map.name;
|
74
|
+
expect(map.name).toEqual(undefined);
|
75
|
+
});
|
76
|
+
});
|
77
|
+
});
|
78
|
+
|
79
|
+
describe("CoMap resolution", async () => {
|
80
|
+
class TwiceNestedMap extends Co.map({
|
81
|
+
taste: S.string,
|
82
|
+
}).as<TwiceNestedMap>() {}
|
83
|
+
|
84
|
+
class NestedMap extends Co.map({
|
85
|
+
name: S.string,
|
86
|
+
twiceNested: TwiceNestedMap,
|
87
|
+
}).as<NestedMap>() {
|
88
|
+
get fancyName() {
|
89
|
+
return "Sir " + this.name;
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
class TestMap extends Co.map({
|
94
|
+
color: S.string,
|
95
|
+
height: S.number,
|
96
|
+
nested: NestedMap,
|
97
|
+
}).as<TestMap>() {
|
98
|
+
get roughColor() {
|
99
|
+
return this.color + "ish";
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
const initNodeAndMap = async () => {
|
104
|
+
const me = await Account.create({
|
105
|
+
name: "Hermes Puggington",
|
106
|
+
});
|
107
|
+
|
108
|
+
const map = new TestMap(
|
109
|
+
{
|
110
|
+
color: "red",
|
111
|
+
height: 10,
|
112
|
+
nested: new NestedMap(
|
113
|
+
{
|
114
|
+
name: "nested",
|
115
|
+
twiceNested: new TwiceNestedMap(
|
116
|
+
{ taste: "sour" },
|
117
|
+
{ owner: me }
|
118
|
+
),
|
119
|
+
},
|
120
|
+
{ owner: me }
|
121
|
+
),
|
122
|
+
},
|
123
|
+
{ owner: me }
|
124
|
+
);
|
125
|
+
|
126
|
+
return { me, map };
|
127
|
+
};
|
128
|
+
|
129
|
+
test("Construction", async () => {
|
130
|
+
const { map } = await initNodeAndMap();
|
131
|
+
|
132
|
+
// const test: Schema.Schema.To<typeof NestedMap>
|
133
|
+
|
134
|
+
expect(map.color).toEqual("red");
|
135
|
+
expect(map.roughColor).toEqual("redish");
|
136
|
+
expect(map.height).toEqual(10);
|
137
|
+
expect(map.nested?.name).toEqual("nested");
|
138
|
+
expect(map.nested?.fancyName).toEqual("Sir nested");
|
139
|
+
expect(map.nested?.id).toBeDefined();
|
140
|
+
expect(map.nested?.twiceNested?.taste).toEqual("sour");
|
141
|
+
});
|
142
|
+
|
143
|
+
test("Loading and availability", async () => {
|
144
|
+
const { me, map } = await initNodeAndMap();
|
145
|
+
const [initialAsPeer, secondPeer] = connectedPeers(
|
146
|
+
"initial",
|
147
|
+
"second",
|
148
|
+
{ peer1role: "server", peer2role: "client" }
|
149
|
+
);
|
150
|
+
me._raw.core.node.syncManager.addPeer(secondPeer);
|
151
|
+
const meOnSecondPeer = await Account.become({
|
152
|
+
accountID: me.id,
|
153
|
+
accountSecret: me._raw.agentSecret,
|
154
|
+
peersToLoadFrom: [initialAsPeer],
|
155
|
+
sessionID: newRandomSessionID(me.id as any),
|
156
|
+
});
|
157
|
+
|
158
|
+
const loadedMap = await TestMap.load(map.id, { as: meOnSecondPeer });
|
159
|
+
|
160
|
+
expect(loadedMap?.color).toEqual("red");
|
161
|
+
expect(loadedMap?.height).toEqual(10);
|
162
|
+
expect(loadedMap?.nested).toEqual(undefined);
|
163
|
+
expect(loadedMap?._refs.nested?.id).toEqual(map.nested?.id);
|
164
|
+
expect(loadedMap?._refs.nested?.value).toEqual(undefined);
|
165
|
+
|
166
|
+
const loadedNestedMap = await NestedMap.load(map.nested!.id, {
|
167
|
+
as: meOnSecondPeer,
|
168
|
+
});
|
169
|
+
|
170
|
+
expect(loadedMap?.nested?.name).toEqual("nested");
|
171
|
+
expect(loadedMap?.nested.fancyName).toEqual("Sir nested");
|
172
|
+
expect(loadedMap?._refs.nested?.value).toEqual(loadedNestedMap);
|
173
|
+
expect(loadedMap?.nested?.twiceNested?.taste).toEqual(undefined);
|
174
|
+
|
175
|
+
const loadedTwiceNestedMap = await TwiceNestedMap.load(
|
176
|
+
map.nested!.twiceNested!.id,
|
177
|
+
{ as: meOnSecondPeer }
|
178
|
+
);
|
179
|
+
|
180
|
+
expect(loadedMap?.nested?.twiceNested?.taste).toEqual("sour");
|
181
|
+
expect(loadedMap?.nested?._refs.twiceNested?.value).toEqual(
|
182
|
+
loadedTwiceNestedMap
|
183
|
+
);
|
184
|
+
|
185
|
+
const otherNestedMap = new NestedMap(
|
186
|
+
{
|
187
|
+
name: "otherNested",
|
188
|
+
twiceNested: new TwiceNestedMap(
|
189
|
+
{ taste: "sweet" },
|
190
|
+
{ owner: meOnSecondPeer }
|
191
|
+
),
|
192
|
+
},
|
193
|
+
{ owner: meOnSecondPeer }
|
194
|
+
);
|
195
|
+
|
196
|
+
loadedMap!.nested = otherNestedMap;
|
197
|
+
expect(loadedMap?.nested?.name).toEqual("otherNested");
|
198
|
+
expect(loadedMap?._refs.nested?.id).toEqual(otherNestedMap.id);
|
199
|
+
expect(loadedMap?._refs.nested?.value).toEqual(otherNestedMap);
|
200
|
+
expect(loadedMap?.nested?.twiceNested?.taste).toEqual("sweet");
|
201
|
+
expect(loadedMap?.nested?._refs.twiceNested?.value).toBeDefined();
|
202
|
+
});
|
203
|
+
|
204
|
+
test("Subscription & auto-resolution", async () => {
|
205
|
+
const { me, map } = await initNodeAndMap();
|
206
|
+
|
207
|
+
const [initialAsPeer, secondAsPeer] = connectedPeers(
|
208
|
+
"initial",
|
209
|
+
"second",
|
210
|
+
{ peer1role: "server", peer2role: "client" }
|
211
|
+
);
|
212
|
+
|
213
|
+
me._raw.core.node.syncManager.addPeer(secondAsPeer);
|
214
|
+
|
215
|
+
const meOnSecondPeer = await Account.become({
|
216
|
+
accountID: me.id,
|
217
|
+
accountSecret: me._raw.agentSecret,
|
218
|
+
peersToLoadFrom: [initialAsPeer],
|
219
|
+
sessionID: newRandomSessionID(me.id as any),
|
220
|
+
});
|
221
|
+
|
222
|
+
await Effect.runPromise(
|
223
|
+
Effect.gen(function* ($) {
|
224
|
+
const queue = yield* $(Queue.unbounded<TestMap>());
|
225
|
+
|
226
|
+
TestMap.subscribe(
|
227
|
+
map.id,
|
228
|
+
{ as: meOnSecondPeer },
|
229
|
+
(subscribedMap) => {
|
230
|
+
console.log(
|
231
|
+
"subscribedMap.nested?.twiceNested?.taste",
|
232
|
+
subscribedMap.nested?.twiceNested?.taste
|
233
|
+
);
|
234
|
+
Effect.runPromise(Queue.offer(queue, subscribedMap));
|
235
|
+
}
|
236
|
+
);
|
237
|
+
|
238
|
+
const update1 = yield* $(Queue.take(queue));
|
239
|
+
expect(update1.nested).toEqual(undefined);
|
240
|
+
|
241
|
+
const update2 = yield* $(Queue.take(queue));
|
242
|
+
expect(update2.nested?.name).toEqual("nested");
|
243
|
+
|
244
|
+
map.nested!.name = "nestedUpdated";
|
245
|
+
|
246
|
+
const _ = yield* $(Queue.take(queue));
|
247
|
+
const update3 = yield* $(Queue.take(queue));
|
248
|
+
expect(update3.nested?.name).toEqual("nestedUpdated");
|
249
|
+
|
250
|
+
const oldTwiceNested = update3.nested!.twiceNested;
|
251
|
+
expect(oldTwiceNested?.taste).toEqual("sour");
|
252
|
+
|
253
|
+
// When assigning a new nested value, we get an update
|
254
|
+
const newTwiceNested = new TwiceNestedMap(
|
255
|
+
{
|
256
|
+
taste: "sweet",
|
257
|
+
},
|
258
|
+
{ owner: meOnSecondPeer }
|
259
|
+
);
|
260
|
+
|
261
|
+
const newNested = new NestedMap(
|
262
|
+
{
|
263
|
+
name: "newNested",
|
264
|
+
twiceNested: newTwiceNested,
|
265
|
+
},
|
266
|
+
{ owner: meOnSecondPeer }
|
267
|
+
);
|
268
|
+
|
269
|
+
update3.nested = newNested;
|
270
|
+
|
271
|
+
yield* $(Queue.take(queue));
|
272
|
+
// const update4 = yield* $(Queue.take(queue));
|
273
|
+
const update4b = yield* $(Queue.take(queue));
|
274
|
+
|
275
|
+
expect(update4b.nested?.name).toEqual("newNested");
|
276
|
+
expect(update4b.nested?.twiceNested?.taste).toEqual("sweet");
|
277
|
+
|
278
|
+
// we get updates when the new nested value changes
|
279
|
+
newTwiceNested.taste = "salty";
|
280
|
+
const update5 = yield* $(Queue.take(queue));
|
281
|
+
expect(update5.nested?.twiceNested?.taste).toEqual("salty");
|
282
|
+
|
283
|
+
newTwiceNested.taste = "umami";
|
284
|
+
const update6 = yield* $(Queue.take(queue));
|
285
|
+
expect(update6.nested?.twiceNested?.taste).toEqual("umami");
|
286
|
+
})
|
287
|
+
);
|
288
|
+
});
|
289
|
+
|
290
|
+
class TestMapWithOptionalRef extends Co.map({
|
291
|
+
color: S.string,
|
292
|
+
nested: S.optional(NestedMap),
|
293
|
+
}).as<TestMapWithOptionalRef>() {}
|
294
|
+
|
295
|
+
test("Construction with optional", async () => {
|
296
|
+
const me = await Account.create({
|
297
|
+
name: "Hermes Puggington",
|
298
|
+
});
|
299
|
+
|
300
|
+
const mapWithout = new TestMapWithOptionalRef(
|
301
|
+
{
|
302
|
+
color: "red",
|
303
|
+
},
|
304
|
+
{ owner: me }
|
305
|
+
);
|
306
|
+
|
307
|
+
expect(mapWithout.color).toEqual("red");
|
308
|
+
expect(mapWithout.nested).toEqual(undefined);
|
309
|
+
|
310
|
+
const mapWith = new TestMapWithOptionalRef(
|
311
|
+
{
|
312
|
+
color: "red",
|
313
|
+
nested: new NestedMap(
|
314
|
+
{
|
315
|
+
name: "wow!",
|
316
|
+
twiceNested: new TwiceNestedMap(
|
317
|
+
{ taste: "sour" },
|
318
|
+
{ owner: me }
|
319
|
+
),
|
320
|
+
},
|
321
|
+
{ owner: me }
|
322
|
+
),
|
323
|
+
},
|
324
|
+
{ owner: me }
|
325
|
+
);
|
326
|
+
|
327
|
+
expect(mapWith.color).toEqual("red");
|
328
|
+
expect(mapWith.nested?.name).toEqual("wow!");
|
329
|
+
expect(mapWith.nested?.fancyName).toEqual("Sir wow!");
|
330
|
+
expect(mapWith.nested?._raw).toBeDefined();
|
331
|
+
});
|
332
|
+
|
333
|
+
class TestRecord extends Co.map(
|
334
|
+
{ color: S.string },
|
335
|
+
{ key: S.string, value: S.string }
|
336
|
+
).as<TestRecord>() {}
|
337
|
+
|
338
|
+
test("Construction with index signature", async () => {
|
339
|
+
const me = await Account.create({
|
340
|
+
name: "Hermes Puggington",
|
341
|
+
});
|
342
|
+
|
343
|
+
const record = new TestRecord(
|
344
|
+
{
|
345
|
+
color: "red",
|
346
|
+
other: "wild",
|
347
|
+
},
|
348
|
+
{ owner: me }
|
349
|
+
);
|
350
|
+
|
351
|
+
expect(record.color).toEqual("red");
|
352
|
+
expect(record._raw.get("color")).toEqual("red");
|
353
|
+
expect(record.other).toEqual("wild");
|
354
|
+
expect(record._raw.get("other")).toEqual("wild");
|
355
|
+
expect(Object.keys(record)).toEqual(["color", "other"]);
|
356
|
+
});
|
357
|
+
});
|