jazz-tools 0.7.0-alpha.4 → 0.7.0-alpha.42

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. package/.eslintrc.cjs +3 -10
  2. package/.prettierrc.js +9 -0
  3. package/.turbo/turbo-build.log +3 -19
  4. package/.turbo/turbo-lint.log +4 -0
  5. package/.turbo/turbo-test.log +140 -0
  6. package/CHANGELOG.md +252 -0
  7. package/README.md +10 -2
  8. package/dist/coValues/account.js +104 -50
  9. package/dist/coValues/account.js.map +1 -1
  10. package/dist/coValues/coList.js +165 -112
  11. package/dist/coValues/coList.js.map +1 -1
  12. package/dist/coValues/coMap.js +243 -163
  13. package/dist/coValues/coMap.js.map +1 -1
  14. package/dist/coValues/coStream.js +256 -73
  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 +14 -8
  19. package/dist/coValues/extensions/imageDef.js.map +1 -1
  20. package/dist/coValues/group.js +49 -38
  21. package/dist/coValues/group.js.map +1 -1
  22. package/dist/coValues/interfaces.js +66 -26
  23. package/dist/coValues/interfaces.js.map +1 -1
  24. package/dist/implementation/devtoolsFormatters.js +114 -0
  25. package/dist/implementation/devtoolsFormatters.js.map +1 -0
  26. package/dist/implementation/refs.js +60 -19
  27. package/dist/implementation/refs.js.map +1 -1
  28. package/dist/implementation/schema.js +44 -1
  29. package/dist/implementation/schema.js.map +1 -1
  30. package/dist/implementation/subscriptionScope.js +19 -1
  31. package/dist/implementation/subscriptionScope.js.map +1 -1
  32. package/dist/implementation/symbols.js +5 -0
  33. package/dist/implementation/symbols.js.map +1 -0
  34. package/dist/index.js +4 -5
  35. package/dist/index.js.map +1 -1
  36. package/dist/internal.js +4 -1
  37. package/dist/internal.js.map +1 -1
  38. package/dist/tests/coList.test.js +51 -52
  39. package/dist/tests/coList.test.js.map +1 -1
  40. package/dist/tests/coMap.test.js +196 -75
  41. package/dist/tests/coMap.test.js.map +1 -1
  42. package/dist/tests/coStream.test.js +95 -85
  43. package/dist/tests/coStream.test.js.map +1 -1
  44. package/dist/tests/deepLoading.test.js +188 -0
  45. package/dist/tests/deepLoading.test.js.map +1 -0
  46. package/dist/tests/groupsAndAccounts.test.js +83 -0
  47. package/dist/tests/groupsAndAccounts.test.js.map +1 -0
  48. package/package.json +17 -9
  49. package/src/coValues/account.ts +184 -153
  50. package/src/coValues/coList.ts +220 -173
  51. package/src/coValues/coMap.ts +322 -312
  52. package/src/coValues/coStream.ts +397 -135
  53. package/src/coValues/deepLoading.ts +215 -0
  54. package/src/coValues/extensions/imageDef.ts +16 -17
  55. package/src/coValues/group.ts +95 -111
  56. package/src/coValues/interfaces.ts +217 -115
  57. package/src/implementation/devtoolsFormatters.ts +110 -0
  58. package/src/implementation/inspect.ts +1 -1
  59. package/src/implementation/refs.ts +91 -38
  60. package/src/implementation/schema.ts +87 -46
  61. package/src/implementation/subscriptionScope.ts +44 -12
  62. package/src/implementation/symbols.ts +11 -0
  63. package/src/index.ts +13 -9
  64. package/src/internal.ts +6 -2
  65. package/src/tests/coList.test.ts +67 -66
  66. package/src/tests/coMap.test.ts +226 -123
  67. package/src/tests/coStream.test.ts +141 -131
  68. package/src/tests/deepLoading.test.ts +301 -0
  69. package/src/tests/groupsAndAccounts.test.ts +91 -0
@@ -1,65 +1,52 @@
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 { Account, jazzReady, Encoders, indexSignature, CoMap } from "..";
8
-
9
- if (!("crypto" in globalThis)) {
10
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
- (globalThis as any).crypto = webcrypto;
12
- }
5
+ import { Account, Encoders, CoMap, co, WasmCrypto, isControlledAccount } from "../index.js";
13
6
 
14
- beforeEach(async () => {
15
- await jazzReady;
16
- });
7
+ const Crypto = await WasmCrypto.create();
17
8
 
18
9
  describe("Simple CoMap operations", async () => {
19
10
  const me = await Account.create({
20
- name: "Hermes Puggington",
11
+ creationProps: { name: "Hermes Puggington" },
12
+ crypto: Crypto,
21
13
  });
22
14
 
23
- class TestMap extends CoMap<TestMap> {
24
- declare color: string;
25
- declare height: number;
26
- declare birthday: Date;
27
- declare name?: string;
15
+ class TestMap extends CoMap {
16
+ color = co.string;
17
+ _height = co.number;
18
+ birthday = co.encoded(Encoders.Date);
19
+ name? = co.string;
28
20
 
29
- get _roughColor() {
21
+ get roughColor() {
30
22
  return this.color + "ish";
31
23
  }
32
24
  }
33
- TestMap.encoding({
34
- color: "json",
35
- height: "json",
36
- birthday: { encoded: Encoders.Date },
37
- name: "json",
38
- });
39
25
 
40
- console.log("TestMap schema", TestMap.prototype._encoding);
26
+ console.log("TestMap schema", TestMap.prototype._schema);
41
27
 
42
28
  const birthday = new Date();
43
29
 
44
- const map = new TestMap(
30
+ const map = TestMap.create(
45
31
  {
46
32
  color: "red",
47
- height: 10,
33
+ _height: 10,
48
34
  birthday: birthday,
49
35
  },
50
- { owner: me }
36
+ { owner: me },
51
37
  );
52
38
 
53
39
  test("Construction", () => {
54
40
  expect(map.color).toEqual("red");
55
- expect(map._roughColor).toEqual("redish");
56
- expect(map.height).toEqual(10);
41
+ expect(map.roughColor).toEqual("redish");
42
+ expect(map._height).toEqual(10);
57
43
  expect(map.birthday).toEqual(birthday);
58
44
  expect(map._raw.get("birthday")).toEqual(birthday.toISOString());
45
+ expect(Object.keys(map)).toEqual(["color", "_height", "birthday"]);
59
46
  });
60
47
 
61
48
  describe("Mutation", () => {
62
- test("assignment", () => {
49
+ test("assignment & deletion", () => {
63
50
  map.color = "blue";
64
51
  expect(map.color).toEqual("blue");
65
52
  expect(map._raw.get("color")).toEqual("blue");
@@ -68,45 +55,45 @@ describe("Simple CoMap operations", async () => {
68
55
  expect(map.birthday).toEqual(newBirthday);
69
56
  expect(map._raw.get("birthday")).toEqual(newBirthday.toISOString());
70
57
 
71
- Object.assign(map, { color: "green", height: 20 });
58
+ Object.assign(map, { color: "green", _height: 20 });
72
59
  expect(map.color).toEqual("green");
73
60
  expect(map._raw.get("color")).toEqual("green");
74
- expect(map.height).toEqual(20);
75
- expect(map._raw.get("height")).toEqual(20);
61
+ expect(map._height).toEqual(20);
62
+ expect(map._raw.get("_height")).toEqual(20);
76
63
 
77
64
  map.name = "Secret name";
78
65
  expect(map.name).toEqual("Secret name");
79
66
  map.name = undefined;
80
67
  expect(map.name).toEqual(undefined);
68
+ expect(Object.keys(map)).toContain("name");
69
+ delete map.name;
70
+ expect(map.name).toEqual(undefined);
71
+ expect(Object.keys(map)).not.toContain("name");
81
72
  });
82
73
  });
83
74
 
84
- class RecursiveMap extends CoMap<RecursiveMap> {
85
- declare name: string;
86
- declare next: RecursiveMap | null;
75
+ class RecursiveMap extends CoMap {
76
+ name = co.string;
77
+ next?: co<RecursiveMap | null> = co.ref(RecursiveMap);
87
78
  }
88
- RecursiveMap.encoding({
89
- name: "json",
90
- next: { ref: () => RecursiveMap },
91
- });
92
79
 
93
- const recursiveMap = new RecursiveMap(
80
+ const recursiveMap = RecursiveMap.create(
94
81
  {
95
82
  name: "first",
96
- next: new RecursiveMap(
83
+ next: RecursiveMap.create(
97
84
  {
98
85
  name: "second",
99
- next: new RecursiveMap(
86
+ next: RecursiveMap.create(
100
87
  {
101
88
  name: "third",
102
89
  },
103
- { owner: me }
90
+ { owner: me },
104
91
  ),
105
92
  },
106
- { owner: me }
93
+ { owner: me },
107
94
  ),
108
95
  },
109
- { owner: me }
96
+ { owner: me },
110
97
  );
111
98
 
112
99
  describe("Recursive CoMap", () => {
@@ -116,65 +103,112 @@ describe("Simple CoMap operations", async () => {
116
103
  expect(recursiveMap.next?.next?.name).toEqual("third");
117
104
  });
118
105
  });
106
+
107
+ class MapWithEnumOfMaps extends CoMap {
108
+ name = co.string;
109
+ child = co.ref<typeof ChildA | typeof ChildB>((raw) =>
110
+ raw.get("type") === "a" ? ChildA : ChildB,
111
+ );
112
+ }
113
+
114
+ class ChildA extends CoMap {
115
+ type = co.literal("a");
116
+ value = co.number;
117
+ }
118
+
119
+ class ChildB extends CoMap {
120
+ type = co.literal("b");
121
+ value = co.string;
122
+ }
123
+
124
+ const mapWithEnum = MapWithEnumOfMaps.create(
125
+ {
126
+ name: "enum",
127
+ child: ChildA.create(
128
+ {
129
+ type: "a",
130
+ value: 5,
131
+ },
132
+ { owner: me },
133
+ ),
134
+ },
135
+ { owner: me },
136
+ );
137
+
138
+ test("Enum of maps", () => {
139
+ expect(mapWithEnum.name).toEqual("enum");
140
+ expect(mapWithEnum.child?.type).toEqual("a");
141
+ expect(mapWithEnum.child?.value).toEqual(5);
142
+ expect(mapWithEnum.child?.id).toBeDefined();
143
+ });
144
+
145
+ class SuperClassMap extends CoMap {
146
+ name = co.string;
147
+ }
148
+
149
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
150
+ class SubClassMap extends SuperClassMap {
151
+ name = co.literal("specificString");
152
+ value = co.number;
153
+ extra = co.ref(TestMap);
154
+ }
155
+
156
+ class GenericMapWithLoose<out T extends string = string> extends CoMap {
157
+ name = co.json<T>();
158
+ }
159
+
160
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
161
+ const loose: GenericMapWithLoose<string> = {} as GenericMapWithLoose<
162
+ "a" | "b"
163
+ >;
119
164
  });
120
165
 
121
166
  describe("CoMap resolution", async () => {
122
- class TwiceNestedMap extends CoMap<TwiceNestedMap> {
123
- taste!: string;
167
+ class TwiceNestedMap extends CoMap {
168
+ taste = co.string;
124
169
  }
125
- TwiceNestedMap.encoding({
126
- taste: "json",
127
- });
128
170
 
129
- class NestedMap extends CoMap<NestedMap> {
130
- name!: string;
131
- twiceNested!: TwiceNestedMap | null;
171
+ class NestedMap extends CoMap {
172
+ name = co.string;
173
+ twiceNested = co.ref(TwiceNestedMap);
132
174
 
133
175
  get _fancyName() {
134
176
  return "Sir " + this.name;
135
177
  }
136
178
  }
137
- NestedMap.encoding({
138
- name: "json",
139
- twiceNested: { ref: () => TwiceNestedMap },
140
- });
141
179
 
142
- class TestMap extends CoMap<TestMap> {
143
- declare color: string;
144
- declare height: number;
145
- declare nested: NestedMap | null;
180
+ class TestMap extends CoMap {
181
+ color = co.string;
182
+ height = co.number;
183
+ nested = co.ref(NestedMap);
146
184
 
147
185
  get _roughColor() {
148
186
  return this.color + "ish";
149
187
  }
150
188
  }
151
- TestMap.encoding({
152
- color: "json",
153
- height: "json",
154
- nested: { ref: () => NestedMap },
155
- });
156
189
 
157
190
  const initNodeAndMap = async () => {
158
191
  const me = await Account.create({
159
- name: "Hermes Puggington",
192
+ creationProps: { name: "Hermes Puggington" },
193
+ crypto: Crypto,
160
194
  });
161
195
 
162
- const map = new TestMap(
196
+ const map = TestMap.create(
163
197
  {
164
198
  color: "red",
165
199
  height: 10,
166
- nested: new NestedMap(
200
+ nested: NestedMap.create(
167
201
  {
168
202
  name: "nested",
169
- twiceNested: new TwiceNestedMap(
203
+ twiceNested: TwiceNestedMap.create(
170
204
  { taste: "sour" },
171
- { owner: me }
205
+ { owner: me },
172
206
  ),
173
207
  },
174
- { owner: me }
208
+ { owner: me },
175
209
  ),
176
210
  },
177
- { owner: me }
211
+ { owner: me },
178
212
  );
179
213
 
180
214
  return { me, map };
@@ -199,17 +233,20 @@ describe("CoMap resolution", async () => {
199
233
  const [initialAsPeer, secondPeer] = connectedPeers(
200
234
  "initial",
201
235
  "second",
202
- { peer1role: "server", peer2role: "client" }
236
+ { peer1role: "server", peer2role: "client" },
203
237
  );
238
+ if (!isControlledAccount(me)) { throw("me is not a controlled account") }
204
239
  me._raw.core.node.syncManager.addPeer(secondPeer);
205
240
  const meOnSecondPeer = await Account.become({
206
241
  accountID: me.id,
207
242
  accountSecret: me._raw.agentSecret,
208
243
  peersToLoadFrom: [initialAsPeer],
244
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
209
245
  sessionID: newRandomSessionID(me.id as any),
246
+ crypto: Crypto,
210
247
  });
211
248
 
212
- const loadedMap = await TestMap.load(map.id, { as: meOnSecondPeer });
249
+ const loadedMap = await TestMap.load(map.id, meOnSecondPeer, {});
213
250
 
214
251
  expect(loadedMap?.color).toEqual("red");
215
252
  expect(loadedMap?.height).toEqual(10);
@@ -217,9 +254,11 @@ describe("CoMap resolution", async () => {
217
254
  expect(loadedMap?._refs.nested?.id).toEqual(map.nested?.id);
218
255
  expect(loadedMap?._refs.nested?.value).toEqual(null);
219
256
 
220
- const loadedNestedMap = await NestedMap.load(map.nested!.id, {
221
- as: meOnSecondPeer,
222
- });
257
+ const loadedNestedMap = await NestedMap.load(
258
+ map.nested!.id,
259
+ meOnSecondPeer,
260
+ {},
261
+ );
223
262
 
224
263
  expect(loadedMap?.nested?.name).toEqual("nested");
225
264
  expect(loadedMap?.nested?._fancyName).toEqual("Sir nested");
@@ -228,23 +267,24 @@ describe("CoMap resolution", async () => {
228
267
 
229
268
  const loadedTwiceNestedMap = await TwiceNestedMap.load(
230
269
  map.nested!.twiceNested!.id,
231
- { as: meOnSecondPeer }
270
+ meOnSecondPeer,
271
+ {},
232
272
  );
233
273
 
234
274
  expect(loadedMap?.nested?.twiceNested?.taste).toEqual("sour");
235
275
  expect(loadedMap?.nested?._refs.twiceNested?.value).toEqual(
236
- loadedTwiceNestedMap
276
+ loadedTwiceNestedMap,
237
277
  );
238
278
 
239
- const otherNestedMap = new NestedMap(
279
+ const otherNestedMap = NestedMap.create(
240
280
  {
241
281
  name: "otherNested",
242
- twiceNested: new TwiceNestedMap(
282
+ twiceNested: TwiceNestedMap.create(
243
283
  { taste: "sweet" },
244
- { owner: meOnSecondPeer }
284
+ { owner: meOnSecondPeer },
245
285
  ),
246
286
  },
247
- { owner: meOnSecondPeer }
287
+ { owner: meOnSecondPeer },
248
288
  );
249
289
 
250
290
  loadedMap!.nested = otherNestedMap;
@@ -261,16 +301,17 @@ describe("CoMap resolution", async () => {
261
301
  const [initialAsPeer, secondAsPeer] = connectedPeers(
262
302
  "initial",
263
303
  "second",
264
- { peer1role: "server", peer2role: "client" }
304
+ { peer1role: "server", peer2role: "client" },
265
305
  );
266
-
306
+ if (!isControlledAccount(me)) { throw("me is not a controlled account") }
267
307
  me._raw.core.node.syncManager.addPeer(secondAsPeer);
268
-
269
308
  const meOnSecondPeer = await Account.become({
270
309
  accountID: me.id,
271
310
  accountSecret: me._raw.agentSecret,
272
311
  peersToLoadFrom: [initialAsPeer],
312
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
273
313
  sessionID: newRandomSessionID(me.id as any),
314
+ crypto: Crypto,
274
315
  });
275
316
 
276
317
  await Effect.runPromise(
@@ -279,14 +320,17 @@ describe("CoMap resolution", async () => {
279
320
 
280
321
  TestMap.subscribe(
281
322
  map.id,
282
- { as: meOnSecondPeer },
323
+ meOnSecondPeer,
324
+ {},
283
325
  (subscribedMap) => {
284
326
  console.log(
285
327
  "subscribedMap.nested?.twiceNested?.taste",
286
- subscribedMap.nested?.twiceNested?.taste
328
+ subscribedMap.nested?.twiceNested?.taste,
329
+ );
330
+ void Effect.runPromise(
331
+ Queue.offer(queue, subscribedMap),
287
332
  );
288
- Effect.runPromise(Queue.offer(queue, subscribedMap));
289
- }
333
+ },
290
334
  );
291
335
 
292
336
  const update1 = yield* $(Queue.take(queue));
@@ -305,19 +349,19 @@ describe("CoMap resolution", async () => {
305
349
  expect(oldTwiceNested?.taste).toEqual("sour");
306
350
 
307
351
  // When assigning a new nested value, we get an update
308
- const newTwiceNested = new TwiceNestedMap(
352
+ const newTwiceNested = TwiceNestedMap.create(
309
353
  {
310
354
  taste: "sweet",
311
355
  },
312
- { owner: meOnSecondPeer }
356
+ { owner: meOnSecondPeer },
313
357
  );
314
358
 
315
- const newNested = new NestedMap(
359
+ const newNested = NestedMap.create(
316
360
  {
317
361
  name: "newNested",
318
362
  twiceNested: newTwiceNested,
319
363
  },
320
- { owner: meOnSecondPeer }
364
+ { owner: meOnSecondPeer },
321
365
  );
322
366
 
323
367
  update3.nested = newNested;
@@ -337,49 +381,46 @@ describe("CoMap resolution", async () => {
337
381
  newTwiceNested.taste = "umami";
338
382
  const update6 = yield* $(Queue.take(queue));
339
383
  expect(update6.nested?.twiceNested?.taste).toEqual("umami");
340
- })
384
+ }),
341
385
  );
342
386
  });
343
387
 
344
- class TestMapWithOptionalRef extends CoMap<TestMapWithOptionalRef> {
345
- declare color: string;
346
- declare nested?: NestedMap | null;
388
+ class TestMapWithOptionalRef extends CoMap {
389
+ color = co.string;
390
+ nested? = co.ref(NestedMap);
347
391
  }
348
- TestMapWithOptionalRef.encoding({
349
- color: "json",
350
- nested: { ref: () => NestedMap },
351
- });
352
392
 
353
393
  test("Construction with optional", async () => {
354
394
  const me = await Account.create({
355
- name: "Hermes Puggington",
395
+ creationProps: { name: "Hermes Puggington" },
396
+ crypto: Crypto,
356
397
  });
357
398
 
358
- const mapWithout = new TestMapWithOptionalRef(
399
+ const mapWithout = TestMapWithOptionalRef.create(
359
400
  {
360
401
  color: "red",
361
402
  },
362
- { owner: me }
403
+ { owner: me },
363
404
  );
364
405
 
365
406
  expect(mapWithout.color).toEqual("red");
366
407
  expect(mapWithout.nested).toEqual(undefined);
367
408
 
368
- const mapWith = new TestMapWithOptionalRef(
409
+ const mapWith = TestMapWithOptionalRef.create(
369
410
  {
370
411
  color: "red",
371
- nested: new NestedMap(
412
+ nested: NestedMap.create(
372
413
  {
373
414
  name: "wow!",
374
- twiceNested: new TwiceNestedMap(
415
+ twiceNested: TwiceNestedMap.create(
375
416
  { taste: "sour" },
376
- { owner: me }
417
+ { owner: me },
377
418
  ),
378
419
  },
379
- { owner: me }
420
+ { owner: me },
380
421
  ),
381
422
  },
382
- { owner: me }
423
+ { owner: me },
383
424
  );
384
425
 
385
426
  expect(mapWith.color).toEqual("red");
@@ -388,25 +429,25 @@ describe("CoMap resolution", async () => {
388
429
  expect(mapWith.nested?._raw).toBeDefined();
389
430
  });
390
431
 
391
- class TestRecord extends CoMap<TestRecord> {
392
- declare [indexSignature]: number;
432
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
433
+ class TestRecord extends CoMap {
434
+ [co.items] = co.number;
393
435
  }
436
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
394
437
  interface TestRecord extends Record<string, number> {}
395
- TestRecord.encoding({
396
- [indexSignature]: "json",
397
- });
398
438
 
399
439
  test("Construction with index signature", async () => {
400
440
  const me = await Account.create({
401
- name: "Hermes Puggington",
441
+ creationProps: { name: "Hermes Puggington" },
442
+ crypto: Crypto,
402
443
  });
403
444
 
404
- const record = new TestRecord(
445
+ const record = TestRecord.create(
405
446
  {
406
447
  height: 5,
407
448
  other: 3,
408
449
  },
409
- { owner: me }
450
+ { owner: me },
410
451
  );
411
452
 
412
453
  expect(record.height).toEqual(5);
@@ -414,5 +455,67 @@ describe("CoMap resolution", async () => {
414
455
  expect(record.other).toEqual(3);
415
456
  expect(record._raw.get("other")).toEqual(3);
416
457
  expect(Object.keys(record)).toEqual(["height", "other"]);
458
+ expect(record.toJSON()).toMatchObject({
459
+ _type: "CoMap",
460
+ height: 5,
461
+ id: expect.any(String),
462
+ other: 3,
463
+ });
464
+ });
465
+
466
+ class TestRecord2 extends CoMap.Record(co.number) {}
467
+
468
+ test("Construction with index signature (shorthand)", async () => {
469
+ const me = await Account.create({
470
+ creationProps: { name: "Hermes Puggington" },
471
+ crypto: Crypto,
472
+ });
473
+
474
+ const record = TestRecord2.create(
475
+ {
476
+ height: 5,
477
+ other: 3,
478
+ },
479
+ { owner: me },
480
+ );
481
+
482
+ expect(record.height).toEqual(5);
483
+ expect(record._raw.get("height")).toEqual(5);
484
+ expect(record.other).toEqual(3);
485
+ expect(record._raw.get("other")).toEqual(3);
486
+ expect(Object.keys(record)).toEqual(["height", "other"]);
487
+ });
488
+
489
+ class TestRecordRef extends CoMap.Record(co.ref(TwiceNestedMap)) {}
490
+
491
+ test("Construction with index signature ref", async () => {
492
+ const me = await Account.create({
493
+ creationProps: { name: "Hermes Puggington" },
494
+ crypto: Crypto,
495
+ });
496
+
497
+ const record = TestRecordRef.create(
498
+ {
499
+ firstNested: TwiceNestedMap.create(
500
+ { taste: "sour" },
501
+ { owner: me },
502
+ ),
503
+ secondNested: TwiceNestedMap.create(
504
+ { taste: "sweet" },
505
+ { owner: me },
506
+ ),
507
+ },
508
+ { owner: me },
509
+ );
510
+
511
+ expect(record.firstNested?.taste).toEqual("sour");
512
+ expect(record.firstNested?.id).toBeDefined();
513
+ expect(record.secondNested?.taste).toEqual("sweet");
514
+ expect(record.secondNested?.id).toBeDefined();
515
+ expect(Object.keys(record)).toEqual(["firstNested", "secondNested"]);
516
+ expect(Object.keys(record._refs)).toEqual([
517
+ "firstNested",
518
+ "secondNested",
519
+ ]);
417
520
  });
418
521
  });