jazz-tools 0.7.0-alpha.36 → 0.7.0-alpha.38

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. package/.eslintrc.cjs +3 -10
  2. package/.prettierrc.js +9 -0
  3. package/.turbo/turbo-build.log +3 -91
  4. package/.turbo/turbo-lint.log +4 -0
  5. package/.turbo/turbo-test.log +140 -0
  6. package/CHANGELOG.md +19 -0
  7. package/README.md +10 -2
  8. package/dist/coValues/account.js +22 -12
  9. package/dist/coValues/account.js.map +1 -1
  10. package/dist/coValues/coList.js +7 -9
  11. package/dist/coValues/coList.js.map +1 -1
  12. package/dist/coValues/coMap.js +11 -3
  13. package/dist/coValues/coMap.js.map +1 -1
  14. package/dist/coValues/coStream.js +15 -8
  15. package/dist/coValues/coStream.js.map +1 -1
  16. package/dist/coValues/deepLoading.js +57 -0
  17. package/dist/coValues/deepLoading.js.map +1 -0
  18. package/dist/coValues/extensions/imageDef.js +1 -1
  19. package/dist/coValues/extensions/imageDef.js.map +1 -1
  20. package/dist/coValues/group.js +1 -0
  21. package/dist/coValues/group.js.map +1 -1
  22. package/dist/coValues/interfaces.js +34 -28
  23. package/dist/coValues/interfaces.js.map +1 -1
  24. package/dist/implementation/devtoolsFormatters.js +1 -0
  25. package/dist/implementation/devtoolsFormatters.js.map +1 -1
  26. package/dist/implementation/refs.js +4 -2
  27. package/dist/implementation/refs.js.map +1 -1
  28. package/dist/implementation/schema.js +5 -1
  29. package/dist/implementation/schema.js.map +1 -1
  30. package/dist/implementation/subscriptionScope.js +9 -4
  31. package/dist/implementation/subscriptionScope.js.map +1 -1
  32. package/dist/implementation/symbols.js.map +1 -1
  33. package/dist/index.js +2 -4
  34. package/dist/index.js.map +1 -1
  35. package/dist/internal.js +2 -1
  36. package/dist/internal.js.map +1 -1
  37. package/dist/tests/coList.test.js +20 -17
  38. package/dist/tests/coList.test.js.map +1 -1
  39. package/dist/tests/coMap.test.js +27 -17
  40. package/dist/tests/coMap.test.js.map +1 -1
  41. package/dist/tests/coStream.test.js +39 -24
  42. package/dist/tests/coStream.test.js.map +1 -1
  43. package/dist/tests/deepLoading.test.js +188 -0
  44. package/dist/tests/deepLoading.test.js.map +1 -0
  45. package/dist/tests/groupsAndAccounts.test.js +6 -11
  46. package/dist/tests/groupsAndAccounts.test.js.map +1 -1
  47. package/package.json +12 -6
  48. package/src/coValues/account.ts +60 -58
  49. package/src/coValues/coList.ts +31 -51
  50. package/src/coValues/coMap.ts +27 -21
  51. package/src/coValues/coStream.ts +44 -32
  52. package/src/coValues/deepLoading.ts +215 -0
  53. package/src/coValues/extensions/imageDef.ts +3 -3
  54. package/src/coValues/group.ts +11 -13
  55. package/src/coValues/interfaces.ts +151 -93
  56. package/src/implementation/devtoolsFormatters.ts +1 -0
  57. package/src/implementation/inspect.ts +1 -1
  58. package/src/implementation/refs.ts +29 -15
  59. package/src/implementation/schema.ts +21 -6
  60. package/src/implementation/subscriptionScope.ts +33 -17
  61. package/src/implementation/symbols.ts +0 -1
  62. package/src/index.ts +5 -3
  63. package/src/internal.ts +3 -2
  64. package/src/tests/coList.test.ts +33 -28
  65. package/src/tests/coMap.test.ts +63 -55
  66. package/src/tests/coStream.test.ts +82 -67
  67. package/src/tests/deepLoading.test.ts +301 -0
  68. package/src/tests/groupsAndAccounts.test.ts +9 -18
@@ -1,26 +1,25 @@
1
- import { expect, describe, test, beforeEach } from "vitest";
2
-
3
- import { webcrypto } from "node:crypto";
1
+ import { expect, describe, test } from "vitest";
4
2
  import { connectedPeers } from "cojson/src/streamUtils.js";
5
3
  import { newRandomSessionID } from "cojson/src/coValueCore.js";
6
4
  import { Effect, Queue } from "effect";
7
- import { BinaryCoStream, ID, Account, jazzReady, CoStream, co } from "..";
8
- import { Simplify } from "effect/Types";
9
-
10
- if (!("crypto" in globalThis)) {
11
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
- (globalThis as any).crypto = webcrypto;
13
- }
14
-
15
- beforeEach(async () => {
16
- await jazzReady;
17
- });
5
+ import {
6
+ BinaryCoStream,
7
+ ID,
8
+ Account,
9
+ CoStream,
10
+ co,
11
+ WasmCrypto,
12
+ isControlledAccount,
13
+ } from "../index.js";
14
+
15
+ const Crypto = await WasmCrypto.create();
18
16
 
19
17
  describe("Simple CoStream operations", async () => {
20
18
  const me = await Account.create({
21
19
  creationProps: { name: "Hermes Puggington" },
20
+ crypto: Crypto,
22
21
  });
23
-
22
+ if (!isControlledAccount(me)) { throw("me is not a controlled account") }
24
23
  class TestStream extends CoStream.Of(co.string) {}
25
24
 
26
25
  const stream = TestStream.create(["milk"], { owner: me });
@@ -57,16 +56,17 @@ describe("CoStream resolution", async () => {
57
56
  const initNodeAndStream = async () => {
58
57
  const me = await Account.create({
59
58
  creationProps: { name: "Hermes Puggington" },
59
+ crypto: Crypto,
60
60
  });
61
61
 
62
62
  const stream = TestStream.create(
63
63
  [
64
64
  NestedStream.create(
65
65
  [TwiceNestedStream.create(["milk"], { owner: me })],
66
- { owner: me }
66
+ { owner: me },
67
67
  ),
68
68
  ],
69
- { owner: me }
69
+ { owner: me },
70
70
  );
71
71
 
72
72
  return { me, stream };
@@ -75,7 +75,7 @@ describe("CoStream resolution", async () => {
75
75
  test("Construction", async () => {
76
76
  const { me, stream } = await initNodeAndStream();
77
77
  expect(stream[me.id]?.value?.[me.id]?.value?.[me.id]?.value).toEqual(
78
- "milk"
78
+ "milk",
79
79
  );
80
80
  });
81
81
 
@@ -84,80 +84,87 @@ describe("CoStream resolution", async () => {
84
84
  const [initialAsPeer, secondPeer] = connectedPeers(
85
85
  "initial",
86
86
  "second",
87
- { peer1role: "server", peer2role: "client" }
87
+ { peer1role: "server", peer2role: "client" },
88
88
  );
89
+ if (!isControlledAccount(me)) { throw("me is not a controlled account") }
89
90
  me._raw.core.node.syncManager.addPeer(secondPeer);
90
91
  const meOnSecondPeer = await Account.become({
91
92
  accountID: me.id,
92
93
  accountSecret: me._raw.agentSecret,
93
94
  peersToLoadFrom: [initialAsPeer],
95
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
94
96
  sessionID: newRandomSessionID(me.id as any),
97
+ crypto: Crypto,
95
98
  });
96
99
 
97
- const loadedStream = await TestStream.load(stream.id, {
98
- as: meOnSecondPeer,
99
- });
100
+ const loadedStream = await TestStream.load(
101
+ stream.id,
102
+ meOnSecondPeer,
103
+ []
104
+ );
100
105
 
101
106
  expect(loadedStream?.[me.id]?.value).toEqual(null);
102
107
  expect(loadedStream?.[me.id]?.ref?.id).toEqual(
103
- stream[me.id]?.value?.id
108
+ stream[me.id]?.value?.id,
104
109
  );
105
110
 
106
111
  const loadedNestedStream = await NestedStream.load(
107
112
  stream[me.id]!.value!.id,
108
- { as: meOnSecondPeer }
113
+ meOnSecondPeer,
114
+ []
109
115
  );
110
116
 
111
117
  // expect(loadedStream?.[me.id]?.value).toEqual(loadedNestedStream);
112
118
  expect(loadedStream?.[me.id]?.value?.id).toEqual(
113
- loadedNestedStream?.id
119
+ loadedNestedStream?.id,
114
120
  );
115
121
  expect(loadedStream?.[me.id]?.value?.[me.id]?.value).toEqual(null);
116
122
  // expect(loadedStream?.[me.id]?.ref?.value).toEqual(loadedNestedStream);
117
123
  expect(loadedStream?.[me.id]?.ref?.value?.id).toEqual(
118
- loadedNestedStream?.id
124
+ loadedNestedStream?.id,
119
125
  );
120
126
  expect(loadedStream?.[me.id]?.value?.[me.id]?.ref?.id).toEqual(
121
- stream[me.id]?.value?.[me.id]?.value?.id
127
+ stream[me.id]?.value?.[me.id]?.value?.id,
122
128
  );
123
129
 
124
130
  const loadedTwiceNestedStream = await TwiceNestedStream.load(
125
131
  stream[me.id]!.value![me.id]!.value!.id,
126
- { as: meOnSecondPeer }
132
+ meOnSecondPeer,
133
+ []
127
134
  );
128
135
 
129
136
  // expect(loadedStream?.[me.id]?.value?.[me.id]?.value).toEqual(
130
137
  // loadedTwiceNestedStream
131
138
  // );
132
139
  expect(loadedStream?.[me.id]?.value?.[me.id]?.value?.id).toEqual(
133
- loadedTwiceNestedStream?.id
140
+ loadedTwiceNestedStream?.id,
134
141
  );
135
142
  expect(
136
- loadedStream?.[me.id]?.value?.[me.id]?.value?.fancyValueOf(me.id)
143
+ loadedStream?.[me.id]?.value?.[me.id]?.value?.fancyValueOf(me.id),
137
144
  ).toEqual("Sir milk");
138
145
  // expect(loadedStream?.[me.id]?.ref?.value).toEqual(loadedNestedStream);
139
146
  expect(loadedStream?.[me.id]?.ref?.value?.id).toEqual(
140
- loadedNestedStream?.id
147
+ loadedNestedStream?.id,
141
148
  );
142
149
  expect(loadedStream?.[me.id]?.value?.[me.id]?.ref?.value?.id).toEqual(
143
- loadedTwiceNestedStream?.id
150
+ loadedTwiceNestedStream?.id,
144
151
  );
145
152
 
146
153
  const otherNestedStream = NestedStream.create(
147
154
  [TwiceNestedStream.create(["butter"], { owner: meOnSecondPeer })],
148
- { owner: meOnSecondPeer }
155
+ { owner: meOnSecondPeer },
149
156
  );
150
157
  loadedStream?.push(otherNestedStream);
151
158
  // expect(loadedStream?.[me.id]?.value).toEqual(otherNestedStream);
152
159
  expect(loadedStream?.[me.id]?.value?.id).toEqual(otherNestedStream?.id);
153
160
  expect(loadedStream?.[me.id]?.ref?.value?.id).toEqual(
154
- otherNestedStream?.id
161
+ otherNestedStream?.id,
155
162
  );
156
163
  expect(loadedStream?.[me.id]?.value?.[me.id]?.value?.id).toEqual(
157
- otherNestedStream[me.id]?.value?.id
164
+ otherNestedStream[me.id]?.value?.id,
158
165
  );
159
166
  expect(
160
- loadedStream?.[me.id]?.value?.[me.id]?.value?.fancyValueOf(me.id)
167
+ loadedStream?.[me.id]?.value?.[me.id]?.value?.fancyValueOf(me.id),
161
168
  ).toEqual("Sir butter");
162
169
  });
163
170
 
@@ -167,16 +174,17 @@ describe("CoStream resolution", async () => {
167
174
  const [initialAsPeer, secondAsPeer] = connectedPeers(
168
175
  "initial",
169
176
  "second",
170
- { peer1role: "server", peer2role: "client" }
177
+ { peer1role: "server", peer2role: "client" },
171
178
  );
172
-
173
179
  me._raw.core.node.syncManager.addPeer(secondAsPeer);
174
-
180
+ if (!isControlledAccount(me)) { throw("me is not a controlled account") }
175
181
  const meOnSecondPeer = await Account.become({
176
182
  accountID: me.id,
177
183
  accountSecret: me._raw.agentSecret,
178
184
  peersToLoadFrom: [initialAsPeer],
185
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
186
  sessionID: newRandomSessionID(me.id as any),
187
+ crypto: Crypto,
180
188
  });
181
189
 
182
190
  await Effect.runPromise(
@@ -184,30 +192,28 @@ describe("CoStream resolution", async () => {
184
192
  const queue = yield* $(Queue.unbounded<TestStream>());
185
193
 
186
194
  TestStream.subscribe(
187
- stream.id,
188
- { as: meOnSecondPeer },
195
+ stream.id, meOnSecondPeer , [],
189
196
  (subscribedStream) => {
190
197
  console.log(
191
198
  "subscribedStream[me.id]",
192
- subscribedStream[me.id]
199
+ subscribedStream[me.id],
193
200
  );
194
201
  console.log(
195
202
  "subscribedStream[me.id]?.value?.[me.id]?.value",
196
- subscribedStream[me.id]?.value?.[me.id]?.value
203
+ subscribedStream[me.id]?.value?.[me.id]?.value,
197
204
  );
198
205
  console.log(
199
206
  "subscribedStream[me.id]?.value?.[me.id]?.value?.[me.id]?.value",
200
207
  subscribedStream[me.id]?.value?.[me.id]?.value?.[
201
208
  me.id
202
- ]?.value
209
+ ]?.value,
203
210
  );
204
- Effect.runPromise(Queue.offer(queue, subscribedStream));
205
- }
211
+ void Effect.runPromise(
212
+ Queue.offer(queue, subscribedStream),
213
+ );
214
+ },
206
215
  );
207
216
 
208
- type T = Simplify<TestStream>;
209
- const te: T = stream;
210
-
211
217
  const update1 = yield* $(Queue.take(queue));
212
218
  expect(update1[me.id]?.value).toEqual(null);
213
219
 
@@ -218,14 +224,14 @@ describe("CoStream resolution", async () => {
218
224
  const update3 = yield* $(Queue.take(queue));
219
225
  expect(update3[me.id]?.value?.[me.id]?.value).toBeDefined();
220
226
  expect(
221
- update3[me.id]?.value?.[me.id]?.value?.[me.id]?.value
227
+ update3[me.id]?.value?.[me.id]?.value?.[me.id]?.value,
222
228
  ).toBe("milk");
223
229
 
224
230
  update3[me.id]!.value![me.id]!.value!.push("bread");
225
231
 
226
232
  const update4 = yield* $(Queue.take(queue));
227
233
  expect(
228
- update4[me.id]?.value?.[me.id]?.value?.[me.id]?.value
234
+ update4[me.id]?.value?.[me.id]?.value?.[me.id]?.value,
229
235
  ).toBe("bread");
230
236
 
231
237
  // When assigning a new nested stream, we get an update
@@ -241,16 +247,16 @@ describe("CoStream resolution", async () => {
241
247
 
242
248
  const update5 = yield* $(Queue.take(queue));
243
249
  expect(
244
- update5[me.id]?.value?.[me.id]?.value?.[me.id]?.value
250
+ update5[me.id]?.value?.[me.id]?.value?.[me.id]?.value,
245
251
  ).toBe("butter");
246
252
 
247
253
  // we get updates when the new nested stream changes
248
254
  newTwiceNested.push("jam");
249
255
  const update6 = yield* $(Queue.take(queue));
250
256
  expect(
251
- update6[me.id]?.value?.[me.id]?.value?.[me.id]?.value
257
+ update6[me.id]?.value?.[me.id]?.value?.[me.id]?.value,
252
258
  ).toBe("jam");
253
- })
259
+ }),
254
260
  );
255
261
  });
256
262
  });
@@ -258,6 +264,7 @@ describe("CoStream resolution", async () => {
258
264
  describe("Simple BinaryCoStream operations", async () => {
259
265
  const me = await Account.create({
260
266
  creationProps: { name: "Hermes Puggington" },
267
+ crypto: Crypto,
261
268
  });
262
269
 
263
270
  const stream = BinaryCoStream.create({ owner: me });
@@ -286,6 +293,7 @@ describe("BinaryCoStream loading & Subscription", async () => {
286
293
  const initNodeAndStream = async () => {
287
294
  const me = await Account.create({
288
295
  creationProps: { name: "Hermes Puggington" },
296
+ crypto: Crypto,
289
297
  });
290
298
 
291
299
  const stream = BinaryCoStream.create({ owner: me });
@@ -299,7 +307,7 @@ describe("BinaryCoStream loading & Subscription", async () => {
299
307
  };
300
308
 
301
309
  test("Construction", async () => {
302
- const { me, stream } = await initNodeAndStream();
310
+ const { stream } = await initNodeAndStream();
303
311
  expect(stream.getChunks()).toEqual({
304
312
  mimeType: "text/plain",
305
313
  chunks: [new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])],
@@ -312,19 +320,24 @@ describe("BinaryCoStream loading & Subscription", async () => {
312
320
  const [initialAsPeer, secondAsPeer] = connectedPeers(
313
321
  "initial",
314
322
  "second",
315
- { peer1role: "server", peer2role: "client" }
323
+ { peer1role: "server", peer2role: "client" },
316
324
  );
325
+ if (!isControlledAccount(me)) { throw("me is not a controlled account") }
317
326
  me._raw.core.node.syncManager.addPeer(secondAsPeer);
318
327
  const meOnSecondPeer = await Account.become({
319
328
  accountID: me.id,
320
329
  accountSecret: me._raw.agentSecret,
321
330
  peersToLoadFrom: [initialAsPeer],
331
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
322
332
  sessionID: newRandomSessionID(me.id as any),
333
+ crypto: Crypto,
323
334
  });
324
335
 
325
- const loadedStream = await BinaryCoStream.load(stream.id, {
326
- as: meOnSecondPeer,
327
- });
336
+ const loadedStream = await BinaryCoStream.load(
337
+ stream.id,
338
+ meOnSecondPeer,
339
+ []
340
+ );
328
341
 
329
342
  expect(loadedStream?.getChunks()).toEqual({
330
343
  mimeType: "text/plain",
@@ -341,16 +354,17 @@ describe("BinaryCoStream loading & Subscription", async () => {
341
354
  const [initialAsPeer, secondAsPeer] = connectedPeers(
342
355
  "initial",
343
356
  "second",
344
- { peer1role: "server", peer2role: "client" }
357
+ { peer1role: "server", peer2role: "client" },
345
358
  );
346
-
347
359
  me._raw.core.node.syncManager.addPeer(secondAsPeer);
348
-
360
+ if (!isControlledAccount(me)) { throw("me is not a controlled account") }
349
361
  const meOnSecondPeer = await Account.become({
350
362
  accountID: me.id,
351
363
  accountSecret: me._raw.agentSecret,
352
364
  peersToLoadFrom: [initialAsPeer],
365
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
353
366
  sessionID: newRandomSessionID(me.id as any),
367
+ crypto: Crypto,
354
368
  });
355
369
 
356
370
  await Effect.runPromise(
@@ -358,11 +372,12 @@ describe("BinaryCoStream loading & Subscription", async () => {
358
372
  const queue = yield* $(Queue.unbounded<BinaryCoStream>());
359
373
 
360
374
  BinaryCoStream.subscribe(
361
- stream.id,
362
- { as: meOnSecondPeer },
375
+ stream.id, meOnSecondPeer, [],
363
376
  (subscribedStream) => {
364
- Effect.runPromise(Queue.offer(queue, subscribedStream));
365
- }
377
+ void Effect.runPromise(
378
+ Queue.offer(queue, subscribedStream),
379
+ );
380
+ },
366
381
  );
367
382
 
368
383
  const update1 = yield* $(Queue.take(queue));
@@ -417,7 +432,7 @@ describe("BinaryCoStream loading & Subscription", async () => {
417
432
  totalSizeBytes: undefined,
418
433
  finished: true,
419
434
  });
420
- })
435
+ }),
421
436
  );
422
437
  });
423
438
  });
@@ -0,0 +1,301 @@
1
+ const Crypto = await WasmCrypto.create();
2
+ import { expect, describe, test, expectTypeOf } from "vitest";
3
+ import { connectedPeers } from "cojson/src/streamUtils.js";
4
+ import {
5
+ Account,
6
+ CoList,
7
+ CoMap,
8
+ CoStream,
9
+ SessionID,
10
+ WasmCrypto,
11
+ co,
12
+ Profile,
13
+ isControlledAccount,
14
+ ID,
15
+ } from "../index.js";
16
+ import { newRandomSessionID } from "cojson/src/coValueCore.js";
17
+
18
+ class TestMap extends CoMap {
19
+ list = co.ref(TestList);
20
+ optionalRef? = co.ref(InnermostMap);
21
+ }
22
+
23
+ class TestList extends CoList.Of(co.ref(() => InnerMap)) {}
24
+
25
+ class InnerMap extends CoMap {
26
+ stream = co.ref(TestStream);
27
+ }
28
+
29
+ class TestStream extends CoStream.Of(co.ref(() => InnermostMap)) {}
30
+
31
+ class InnermostMap extends CoMap {
32
+ value = co.string;
33
+ }
34
+
35
+ describe("Deep loading with depth arg", async () => {
36
+ const me = await Account.create({
37
+ creationProps: { name: "Hermes Puggington" },
38
+ crypto: Crypto,
39
+ });
40
+
41
+ const [initialAsPeer, secondPeer] = connectedPeers("initial", "second", {
42
+ peer1role: "server",
43
+ peer2role: "client",
44
+ });
45
+ if (!isControlledAccount(me)) {
46
+ throw "me is not a controlled account";
47
+ }
48
+ me._raw.core.node.syncManager.addPeer(secondPeer);
49
+ const meOnSecondPeer = await Account.become({
50
+ accountID: me.id,
51
+ accountSecret: me._raw.agentSecret,
52
+ peersToLoadFrom: [initialAsPeer],
53
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ sessionID: newRandomSessionID(me.id as any),
55
+ crypto: Crypto,
56
+ });
57
+
58
+ test("loading a deeply nested object will wait until all required refs are loaded", async () => {
59
+ const map = TestMap.create(
60
+ {
61
+ list: TestList.create(
62
+ [
63
+ InnerMap.create(
64
+ {
65
+ stream: TestStream.create(
66
+ [
67
+ InnermostMap.create(
68
+ { value: "hello" },
69
+ { owner: me },
70
+ ),
71
+ ],
72
+ { owner: me },
73
+ ),
74
+ },
75
+ { owner: me },
76
+ ),
77
+ ],
78
+ { owner: me },
79
+ ),
80
+ },
81
+ { owner: me },
82
+ );
83
+
84
+ const map1 = await TestMap.load(map.id, meOnSecondPeer, {});
85
+ expectTypeOf(map1).toEqualTypeOf<TestMap | undefined>();
86
+ if (map1 === undefined) {
87
+ throw new Error("map1 is undefined");
88
+ }
89
+ expect(map1.list).toBe(null);
90
+
91
+ const map2 = await TestMap.load(map.id, meOnSecondPeer, { list: [] });
92
+ expectTypeOf(map2).toEqualTypeOf<
93
+ | (TestMap & {
94
+ list: TestList;
95
+ })
96
+ | undefined
97
+ >();
98
+ if (map2 === undefined) {
99
+ throw new Error("map2 is undefined");
100
+ }
101
+ expect(map2.list).not.toBe(null);
102
+ expect(map2.list[0]).toBe(null);
103
+
104
+ const map3 = await TestMap.load(map.id, meOnSecondPeer, { list: [{}] });
105
+ expectTypeOf(map3).toEqualTypeOf<
106
+ | (TestMap & {
107
+ list: TestList & InnerMap[];
108
+ })
109
+ | undefined
110
+ >();
111
+ if (map3 === undefined) {
112
+ throw new Error("map3 is undefined");
113
+ }
114
+ expect(map3.list[0]).not.toBe(null);
115
+ expect(map3.list[0]?.stream).toBe(null);
116
+
117
+ const map3a = await TestMap.load(map.id, meOnSecondPeer, {
118
+ optionalRef: {},
119
+ });
120
+ expectTypeOf(map3a).toEqualTypeOf<
121
+ | (TestMap & {
122
+ optionalRef: InnermostMap | undefined;
123
+ })
124
+ | undefined
125
+ >();
126
+
127
+ const map4 = await TestMap.load(map.id, meOnSecondPeer, {
128
+ list: [{ stream: [] }],
129
+ });
130
+ expectTypeOf(map4).toEqualTypeOf<
131
+ | (TestMap & {
132
+ list: TestList & (InnerMap & { stream: TestStream })[];
133
+ })
134
+ | undefined
135
+ >();
136
+ if (map4 === undefined) {
137
+ throw new Error("map4 is undefined");
138
+ }
139
+ expect(map4.list[0]?.stream).not.toBe(null);
140
+ // TODO: we should expect null here, but apparently we don't even have the id/ref?
141
+ expect(map4.list[0]?.stream?.[me.id]?.value).not.toBeDefined();
142
+ expect(map4.list[0]?.stream?.byMe?.value).not.toBeDefined();
143
+
144
+ const map5 = await TestMap.load(map.id, meOnSecondPeer, {
145
+ list: [{ stream: [{}] }],
146
+ });
147
+ type ExpectedMap5 =
148
+ | (TestMap & {
149
+ list: TestList &
150
+ (InnerMap & {
151
+ stream: TestStream & {
152
+ byMe?: { value: InnermostMap };
153
+ inCurrentSession?: { value: InnermostMap };
154
+ perSession: {
155
+ [sessionID: SessionID]: {
156
+ value: InnermostMap;
157
+ };
158
+ };
159
+ } & {
160
+ [key: ID<Account>]: { value: InnermostMap };
161
+ };
162
+ })[];
163
+ })
164
+ | undefined;
165
+
166
+ expectTypeOf(map5).toEqualTypeOf<ExpectedMap5>();
167
+ if (map5 === undefined) {
168
+ throw new Error("map5 is undefined");
169
+ }
170
+ expect(map5.list[0]?.stream?.[me.id]?.value).not.toBe(null);
171
+ expect(map5.list[0]?.stream?.byMe?.value).not.toBe(null);
172
+ });
173
+ });
174
+
175
+ class CustomProfile extends Profile {
176
+ stream = co.ref(TestStream);
177
+ }
178
+
179
+ class CustomAccount extends Account {
180
+ profile = co.ref(CustomProfile);
181
+ root = co.ref(TestMap);
182
+
183
+ async migrate(creationProps?: { name: string } | undefined) {
184
+ if (creationProps) {
185
+ this.profile = CustomProfile.create(
186
+ {
187
+ name: creationProps.name,
188
+ stream: TestStream.create([], { owner: this }),
189
+ },
190
+ { owner: this },
191
+ );
192
+ this.root = TestMap.create(
193
+ { list: TestList.create([], { owner: this }) },
194
+ { owner: this },
195
+ );
196
+ }
197
+
198
+ const thisLoaded = await CustomAccount.load(this, {
199
+ profile: { stream: [] },
200
+ root: { list: [] },
201
+ });
202
+ expectTypeOf(thisLoaded).toEqualTypeOf<
203
+ | (CustomAccount & {
204
+ profile: CustomProfile & {
205
+ stream: TestStream;
206
+ };
207
+ root: TestMap & {
208
+ list: TestList;
209
+ };
210
+ })
211
+ | undefined
212
+ >();
213
+ }
214
+ }
215
+
216
+ test("Deep loading within account", async () => {
217
+ const me = await CustomAccount.create({
218
+ creationProps: { name: "Hermes Puggington" },
219
+ crypto: Crypto,
220
+ });
221
+
222
+ const meLoaded = await CustomAccount.load(me, {
223
+ profile: { stream: [] },
224
+ root: { list: [] },
225
+ });
226
+ expectTypeOf(meLoaded).toEqualTypeOf<
227
+ | (CustomAccount & {
228
+ profile: CustomProfile & {
229
+ stream: TestStream;
230
+ };
231
+ root: TestMap & {
232
+ list: TestList;
233
+ };
234
+ })
235
+ | undefined
236
+ >();
237
+ if (meLoaded === undefined) {
238
+ throw new Error("meLoaded is undefined");
239
+ }
240
+ expect(meLoaded.profile.stream).not.toBe(null);
241
+ expect(meLoaded.root.list).not.toBe(null);
242
+ });
243
+
244
+ class RecordLike extends CoMap.Record(co.ref(TestMap)) {}
245
+
246
+ test("Deep loading a record-like coMap", async () => {
247
+ const me = await Account.create({
248
+ creationProps: { name: "Hermes Puggington" },
249
+ crypto: Crypto,
250
+ });
251
+
252
+ const [initialAsPeer, secondPeer] = connectedPeers("initial", "second", {
253
+ peer1role: "server",
254
+ peer2role: "client",
255
+ });
256
+ if (!isControlledAccount(me)) {
257
+ throw "me is not a controlled account";
258
+ }
259
+ me._raw.core.node.syncManager.addPeer(secondPeer);
260
+ const meOnSecondPeer = await Account.become({
261
+ accountID: me.id,
262
+ accountSecret: me._raw.agentSecret,
263
+ peersToLoadFrom: [initialAsPeer],
264
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
265
+ sessionID: newRandomSessionID(me.id as any),
266
+ crypto: Crypto,
267
+ });
268
+
269
+ const record = RecordLike.create(
270
+ {
271
+ key1: TestMap.create(
272
+ { list: TestList.create([], { owner: me }) },
273
+ { owner: me },
274
+ ),
275
+ key2: TestMap.create(
276
+ { list: TestList.create([], { owner: me }) },
277
+ { owner: me },
278
+ ),
279
+ },
280
+ { owner: me },
281
+ );
282
+
283
+ const recordLoaded = await RecordLike.load(record.id, meOnSecondPeer, [
284
+ { list: [{}] },
285
+ ]);
286
+ expectTypeOf(recordLoaded).toEqualTypeOf<
287
+ | (RecordLike & {
288
+ [key: string]: TestMap & {
289
+ list: TestList & InnerMap[];
290
+ };
291
+ })
292
+ | undefined
293
+ >();
294
+ if (recordLoaded === undefined) {
295
+ throw new Error("recordLoaded is undefined");
296
+ }
297
+ expect(recordLoaded.key1?.list).not.toBe(null);
298
+ expect(recordLoaded.key1?.list).not.toBe(undefined);
299
+ expect(recordLoaded.key2?.list).not.toBe(null);
300
+ expect(recordLoaded.key2?.list).not.toBe(undefined);
301
+ });