jazz-tools 0.7.0-alpha.3 → 0.7.0-alpha.31

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. package/.turbo/turbo-build.log +78 -8
  2. package/CHANGELOG.md +181 -0
  3. package/dist/coValues/account.js +81 -41
  4. package/dist/coValues/account.js.map +1 -1
  5. package/dist/coValues/coList.js +156 -104
  6. package/dist/coValues/coList.js.map +1 -1
  7. package/dist/coValues/coMap.js +182 -163
  8. package/dist/coValues/coMap.js.map +1 -1
  9. package/dist/coValues/coStream.js +202 -71
  10. package/dist/coValues/coStream.js.map +1 -1
  11. package/dist/coValues/extensions/imageDef.js +13 -8
  12. package/dist/coValues/extensions/imageDef.js.map +1 -1
  13. package/dist/coValues/group.js +46 -38
  14. package/dist/coValues/group.js.map +1 -1
  15. package/dist/coValues/interfaces.js +23 -5
  16. package/dist/coValues/interfaces.js.map +1 -1
  17. package/dist/implementation/refs.js +26 -12
  18. package/dist/implementation/refs.js.map +1 -1
  19. package/dist/implementation/schema.js +38 -1
  20. package/dist/implementation/schema.js.map +1 -1
  21. package/dist/implementation/symbols.js +5 -0
  22. package/dist/implementation/symbols.js.map +1 -0
  23. package/dist/index.js +4 -3
  24. package/dist/index.js.map +1 -1
  25. package/dist/internal.js +1 -0
  26. package/dist/internal.js.map +1 -1
  27. package/dist/tests/coList.test.js +32 -36
  28. package/dist/tests/coList.test.js.map +1 -1
  29. package/dist/tests/coMap.test.js +170 -59
  30. package/dist/tests/coMap.test.js.map +1 -1
  31. package/dist/tests/coStream.test.js +59 -64
  32. package/dist/tests/coStream.test.js.map +1 -1
  33. package/dist/tests/groupsAndAccounts.test.js +88 -0
  34. package/dist/tests/groupsAndAccounts.test.js.map +1 -0
  35. package/package.json +5 -4
  36. package/src/coValues/account.ts +129 -105
  37. package/src/coValues/coList.ts +200 -131
  38. package/src/coValues/coMap.ts +243 -305
  39. package/src/coValues/coStream.ts +300 -127
  40. package/src/coValues/extensions/imageDef.ts +14 -16
  41. package/src/coValues/group.ts +90 -106
  42. package/src/coValues/interfaces.ts +33 -12
  43. package/src/implementation/refs.ts +42 -25
  44. package/src/implementation/schema.ts +69 -46
  45. package/src/implementation/symbols.ts +12 -0
  46. package/src/index.ts +10 -8
  47. package/src/internal.ts +1 -0
  48. package/src/tests/coList.test.ts +35 -39
  49. package/src/tests/coMap.test.ts +176 -81
  50. package/src/tests/coStream.test.ts +76 -81
  51. package/src/tests/groupsAndAccounts.test.ts +100 -0
@@ -4,7 +4,7 @@ import { webcrypto } from "node:crypto";
4
4
  import { connectedPeers } from "cojson/src/streamUtils.js";
5
5
  import { newRandomSessionID } from "cojson/src/coValueCore.js";
6
6
  import { Effect, Queue } from "effect";
7
- import { Account, CoList, jazzReady } from "..";
7
+ import { Account, CoList, co, jazzReady } from "..";
8
8
 
9
9
  if (!("crypto" in globalThis)) {
10
10
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -17,13 +17,12 @@ beforeEach(async () => {
17
17
 
18
18
  describe("Simple CoList operations", async () => {
19
19
  const me = await Account.create({
20
- name: "Hermes Puggington",
20
+ creationProps: { name: "Hermes Puggington" },
21
21
  });
22
22
 
23
- class TestList extends CoList<string> {}
24
- TestList.encoding({ _item: "json" });
23
+ class TestList extends CoList.Of(co.string) {}
25
24
 
26
- const list = new TestList(["bread", "butter", "onion"], { owner: me });
25
+ const list = TestList.create(["bread", "butter", "onion"], { owner: me });
27
26
 
28
27
  test("Construction", () => {
29
28
  expect(list[0]).toBe("bread");
@@ -40,7 +39,7 @@ describe("Simple CoList operations", async () => {
40
39
 
41
40
  describe("Mutation", () => {
42
41
  test("assignment", () => {
43
- const list = new TestList(["bread", "butter", "onion"], {
42
+ const list = TestList.create(["bread", "butter", "onion"], {
44
43
  owner: me,
45
44
  });
46
45
  list[1] = "margarine";
@@ -53,7 +52,7 @@ describe("Simple CoList operations", async () => {
53
52
  });
54
53
 
55
54
  test("push", () => {
56
- const list = new TestList(["bread", "butter", "onion"], {
55
+ const list = TestList.create(["bread", "butter", "onion"], {
57
56
  owner: me,
58
57
  });
59
58
  list.push("cheese");
@@ -67,7 +66,7 @@ describe("Simple CoList operations", async () => {
67
66
  });
68
67
 
69
68
  test("unshift", () => {
70
- const list = new TestList(["bread", "butter", "onion"], {
69
+ const list = TestList.create(["bread", "butter", "onion"], {
71
70
  owner: me,
72
71
  });
73
72
  list.unshift("lettuce");
@@ -81,7 +80,7 @@ describe("Simple CoList operations", async () => {
81
80
  });
82
81
 
83
82
  test("pop", () => {
84
- const list = new TestList(["bread", "butter", "onion"], {
83
+ const list = TestList.create(["bread", "butter", "onion"], {
85
84
  owner: me,
86
85
  });
87
86
  expect(list.pop()).toBe("onion");
@@ -90,7 +89,7 @@ describe("Simple CoList operations", async () => {
90
89
  });
91
90
 
92
91
  test("shift", () => {
93
- const list = new TestList(["bread", "butter", "onion"], {
92
+ const list = TestList.create(["bread", "butter", "onion"], {
94
93
  owner: me,
95
94
  });
96
95
  expect(list.shift()).toBe("bread");
@@ -98,49 +97,46 @@ describe("Simple CoList operations", async () => {
98
97
  expect(list._raw.asArray()).toEqual(["butter", "onion"]);
99
98
  });
100
99
 
101
- // test("splice", () => {
102
- // const list = new TestList(["bread", "butter", "onion"], {
103
- // owner: me,
104
- // });
105
- // list.splice(1, 1, "salt", "pepper");
106
- // expect(list.length).toBe(4);
107
- // expect(list._raw.asArray()).toEqual([
108
- // "bread",
109
- // "salt",
110
- // "pepper",
111
- // "onion",
112
- // ]);
113
- // });
100
+ test("splice", () => {
101
+ const list = TestList.create(["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
+ });
114
113
  });
115
114
  });
116
115
 
117
116
  describe("CoList resolution", async () => {
118
- class TwiceNestedList extends CoList<string> {
117
+ class TwiceNestedList extends CoList.Of(co.string) {
119
118
  joined() {
120
119
  return this.join(",");
121
120
  }
122
121
  }
123
- TwiceNestedList.encoding({ _item: "json" });
124
122
 
125
- class NestedList extends CoList<TwiceNestedList | null> {}
126
- NestedList.encoding({ _item: { ref: () => TwiceNestedList } });
123
+ class NestedList extends CoList.Of(co.ref(TwiceNestedList)) {}
127
124
 
128
- class TestList extends CoList<NestedList | null> {}
129
- TestList.encoding({ _item: { ref: () => NestedList } });
125
+ class TestList extends CoList.Of(co.ref(NestedList)) {}
130
126
 
131
127
  const initNodeAndList = async () => {
132
128
  const me = await Account.create({
133
- name: "Hermes Puggington",
129
+ creationProps: { name: "Hermes Puggington" },
134
130
  });
135
131
 
136
- const list = new TestList(
132
+ const list = TestList.create(
137
133
  [
138
- new NestedList(
139
- [new TwiceNestedList(["a", "b"], { owner: me })],
134
+ NestedList.create(
135
+ [TwiceNestedList.create(["a", "b"], { owner: me })],
140
136
  { owner: me }
141
137
  ),
142
- new NestedList(
143
- [new TwiceNestedList(["c", "d"], { owner: me })],
138
+ NestedList.create(
139
+ [TwiceNestedList.create(["c", "d"], { owner: me })],
144
140
  { owner: me }
145
141
  ),
146
142
  ],
@@ -200,8 +196,8 @@ describe("CoList resolution", async () => {
200
196
  expect(loadedList?.[0]?._refs[0]?.id).toEqual(list[0]?.[0]?.id);
201
197
  expect(loadedList?.[0]?._refs[0]?.value).toEqual(loadedTwiceNestedList);
202
198
 
203
- const otherNestedList = new NestedList(
204
- [new TwiceNestedList(["e", "f"], { owner: meOnSecondPeer })],
199
+ const otherNestedList = NestedList.create(
200
+ [TwiceNestedList.create(["e", "f"], { owner: meOnSecondPeer })],
205
201
  { owner: meOnSecondPeer }
206
202
  );
207
203
 
@@ -261,11 +257,11 @@ describe("CoList resolution", async () => {
261
257
 
262
258
  // When assigning a new nested value, we get an update
263
259
 
264
- const newTwiceNestedList = new TwiceNestedList(["y", "z"], {
260
+ const newTwiceNestedList = TwiceNestedList.create(["y", "z"], {
265
261
  owner: meOnSecondPeer,
266
262
  });
267
263
 
268
- const newNestedList = new NestedList([newTwiceNestedList], {
264
+ const newNestedList = NestedList.create([newTwiceNestedList], {
269
265
  owner: meOnSecondPeer,
270
266
  });
271
267
 
@@ -4,7 +4,7 @@ import { webcrypto } from "node:crypto";
4
4
  import { connectedPeers } from "cojson/src/streamUtils.js";
5
5
  import { newRandomSessionID } from "cojson/src/coValueCore.js";
6
6
  import { Effect, Queue } from "effect";
7
- import { Account, jazzReady, Encoders, indexSignature, CoMap } from "..";
7
+ import { Account, jazzReady, Encoders, CoMap, co } from "..";
8
8
 
9
9
  if (!("crypto" in globalThis)) {
10
10
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -17,34 +17,28 @@ beforeEach(async () => {
17
17
 
18
18
  describe("Simple CoMap operations", async () => {
19
19
  const me = await Account.create({
20
- name: "Hermes Puggington",
20
+ creationProps: { name: "Hermes Puggington" },
21
21
  });
22
22
 
23
- class TestMap extends CoMap<TestMap> {
24
- declare color: string;
25
- declare height: number;
26
- declare birthday: Date;
27
- declare name?: string;
23
+ class TestMap extends CoMap {
24
+ color = co.string;
25
+ _height = co.number;
26
+ birthday = co.encoded(Encoders.Date);
27
+ name? = co.string;
28
28
 
29
- get _roughColor() {
29
+ get roughColor() {
30
30
  return this.color + "ish";
31
31
  }
32
32
  }
33
- TestMap.encoding({
34
- color: "json",
35
- height: "json",
36
- birthday: { encoded: Encoders.Date },
37
- name: "json",
38
- });
39
33
 
40
- console.log("TestMap schema", TestMap.prototype._encoding);
34
+ console.log("TestMap schema", TestMap.prototype._schema);
41
35
 
42
36
  const birthday = new Date();
43
37
 
44
- const map = new TestMap(
38
+ const map = TestMap.create(
45
39
  {
46
40
  color: "red",
47
- height: 10,
41
+ _height: 10,
48
42
  birthday: birthday,
49
43
  },
50
44
  { owner: me }
@@ -52,14 +46,15 @@ describe("Simple CoMap operations", async () => {
52
46
 
53
47
  test("Construction", () => {
54
48
  expect(map.color).toEqual("red");
55
- expect(map._roughColor).toEqual("redish");
56
- expect(map.height).toEqual(10);
49
+ expect(map.roughColor).toEqual("redish");
50
+ expect(map._height).toEqual(10);
57
51
  expect(map.birthday).toEqual(birthday);
58
52
  expect(map._raw.get("birthday")).toEqual(birthday.toISOString());
53
+ expect(Object.keys(map)).toEqual(["color", "_height", "birthday"]);
59
54
  });
60
55
 
61
56
  describe("Mutation", () => {
62
- test("assignment", () => {
57
+ test("assignment & deletion", () => {
63
58
  map.color = "blue";
64
59
  expect(map.color).toEqual("blue");
65
60
  expect(map._raw.get("color")).toEqual("blue");
@@ -68,35 +63,35 @@ describe("Simple CoMap operations", async () => {
68
63
  expect(map.birthday).toEqual(newBirthday);
69
64
  expect(map._raw.get("birthday")).toEqual(newBirthday.toISOString());
70
65
 
71
- Object.assign(map, { color: "green", height: 20 });
66
+ Object.assign(map, { color: "green", _height: 20 });
72
67
  expect(map.color).toEqual("green");
73
68
  expect(map._raw.get("color")).toEqual("green");
74
- expect(map.height).toEqual(20);
75
- expect(map._raw.get("height")).toEqual(20);
69
+ expect(map._height).toEqual(20);
70
+ expect(map._raw.get("_height")).toEqual(20);
76
71
 
77
72
  map.name = "Secret name";
78
73
  expect(map.name).toEqual("Secret name");
79
74
  map.name = undefined;
80
75
  expect(map.name).toEqual(undefined);
76
+ expect(Object.keys(map)).toContain("name");
77
+ delete map.name;
78
+ expect(map.name).toEqual(undefined);
79
+ expect(Object.keys(map)).not.toContain("name");
81
80
  });
82
81
  });
83
82
 
84
- class RecursiveMap extends CoMap<RecursiveMap> {
85
- declare name: string;
86
- declare next: RecursiveMap | null;
83
+ class RecursiveMap extends CoMap {
84
+ name = co.string;
85
+ next: co<RecursiveMap | null> = co.ref(RecursiveMap);
87
86
  }
88
- RecursiveMap.encoding({
89
- name: "json",
90
- next: { ref: () => RecursiveMap },
91
- });
92
87
 
93
- const recursiveMap = new RecursiveMap(
88
+ const recursiveMap = RecursiveMap.create(
94
89
  {
95
90
  name: "first",
96
- next: new RecursiveMap(
91
+ next: RecursiveMap.create(
97
92
  {
98
93
  name: "second",
99
- next: new RecursiveMap(
94
+ next: RecursiveMap.create(
100
95
  {
101
96
  name: "third",
102
97
  },
@@ -116,57 +111,104 @@ describe("Simple CoMap operations", async () => {
116
111
  expect(recursiveMap.next?.next?.name).toEqual("third");
117
112
  });
118
113
  });
114
+
115
+ class MapWithEnumOfMaps extends CoMap {
116
+ name = co.string;
117
+ child = co.ref<typeof ChildA | typeof ChildB>((raw) =>
118
+ raw.get("type") === "a" ? ChildA : ChildB
119
+ );
120
+ }
121
+
122
+ class ChildA extends CoMap {
123
+ type = co.literal("a");
124
+ value = co.number;
125
+ }
126
+
127
+ class ChildB extends CoMap {
128
+ type = co.literal("b");
129
+ value = co.string;
130
+ }
131
+
132
+ const mapWithEnum = MapWithEnumOfMaps.create(
133
+ {
134
+ name: "enum",
135
+ child: ChildA.create(
136
+ {
137
+ type: "a",
138
+ value: 5,
139
+ },
140
+ { owner: me }
141
+ ),
142
+ },
143
+ { owner: me }
144
+ );
145
+
146
+ test("Enum of maps", () => {
147
+ expect(mapWithEnum.name).toEqual("enum");
148
+ expect(mapWithEnum.child?.type).toEqual("a");
149
+ expect(mapWithEnum.child?.value).toEqual(5);
150
+ expect(mapWithEnum.child?.id).toBeDefined();
151
+ });
152
+
153
+ class SuperClassMap extends CoMap {
154
+ name = co.string;
155
+ }
156
+
157
+ class SubClassMap extends SuperClassMap {
158
+ name = co.literal("specificString");
159
+ value = co.number;
160
+ extra = co.ref(TestMap);
161
+ }
162
+ interface SubClassMap extends CoMap {}
163
+
164
+ class GenericMapWithLoose<
165
+ out T extends string = string,
166
+ > extends CoMap {
167
+ name = co.json<T>();
168
+ }
169
+
170
+ const loose: GenericMapWithLoose<string> = {} as GenericMapWithLoose<
171
+ "a" | "b"
172
+ >;
119
173
  });
120
174
 
121
175
  describe("CoMap resolution", async () => {
122
- class TwiceNestedMap extends CoMap<TwiceNestedMap> {
123
- taste!: string;
176
+ class TwiceNestedMap extends CoMap {
177
+ taste = co.string;
124
178
  }
125
- TwiceNestedMap.encoding({
126
- taste: "json",
127
- });
128
179
 
129
- class NestedMap extends CoMap<NestedMap> {
130
- name!: string;
131
- twiceNested!: TwiceNestedMap | null;
180
+ class NestedMap extends CoMap {
181
+ name = co.string;
182
+ twiceNested = co.ref(TwiceNestedMap);
132
183
 
133
184
  get _fancyName() {
134
185
  return "Sir " + this.name;
135
186
  }
136
187
  }
137
- NestedMap.encoding({
138
- name: "json",
139
- twiceNested: { ref: () => TwiceNestedMap },
140
- });
141
188
 
142
- class TestMap extends CoMap<TestMap> {
143
- declare color: string;
144
- declare height: number;
145
- declare nested: NestedMap | null;
189
+ class TestMap extends CoMap {
190
+ color = co.string;
191
+ height = co.number;
192
+ nested = co.ref(NestedMap);
146
193
 
147
194
  get _roughColor() {
148
195
  return this.color + "ish";
149
196
  }
150
197
  }
151
- TestMap.encoding({
152
- color: "json",
153
- height: "json",
154
- nested: { ref: () => NestedMap },
155
- });
156
198
 
157
199
  const initNodeAndMap = async () => {
158
200
  const me = await Account.create({
159
- name: "Hermes Puggington",
201
+ creationProps: { name: "Hermes Puggington" },
160
202
  });
161
203
 
162
- const map = new TestMap(
204
+ const map = TestMap.create(
163
205
  {
164
206
  color: "red",
165
207
  height: 10,
166
- nested: new NestedMap(
208
+ nested: NestedMap.create(
167
209
  {
168
210
  name: "nested",
169
- twiceNested: new TwiceNestedMap(
211
+ twiceNested: TwiceNestedMap.create(
170
212
  { taste: "sour" },
171
213
  { owner: me }
172
214
  ),
@@ -236,10 +278,10 @@ describe("CoMap resolution", async () => {
236
278
  loadedTwiceNestedMap
237
279
  );
238
280
 
239
- const otherNestedMap = new NestedMap(
281
+ const otherNestedMap = NestedMap.create(
240
282
  {
241
283
  name: "otherNested",
242
- twiceNested: new TwiceNestedMap(
284
+ twiceNested: TwiceNestedMap.create(
243
285
  { taste: "sweet" },
244
286
  { owner: meOnSecondPeer }
245
287
  ),
@@ -305,14 +347,14 @@ describe("CoMap resolution", async () => {
305
347
  expect(oldTwiceNested?.taste).toEqual("sour");
306
348
 
307
349
  // When assigning a new nested value, we get an update
308
- const newTwiceNested = new TwiceNestedMap(
350
+ const newTwiceNested = TwiceNestedMap.create(
309
351
  {
310
352
  taste: "sweet",
311
353
  },
312
354
  { owner: meOnSecondPeer }
313
355
  );
314
356
 
315
- const newNested = new NestedMap(
357
+ const newNested = NestedMap.create(
316
358
  {
317
359
  name: "newNested",
318
360
  twiceNested: newTwiceNested,
@@ -341,21 +383,17 @@ describe("CoMap resolution", async () => {
341
383
  );
342
384
  });
343
385
 
344
- class TestMapWithOptionalRef extends CoMap<TestMapWithOptionalRef> {
345
- declare color: string;
346
- declare nested?: NestedMap | null;
386
+ class TestMapWithOptionalRef extends CoMap {
387
+ color = co.string;
388
+ nested? = co.ref(NestedMap);
347
389
  }
348
- TestMapWithOptionalRef.encoding({
349
- color: "json",
350
- nested: { ref: () => NestedMap },
351
- });
352
390
 
353
391
  test("Construction with optional", async () => {
354
392
  const me = await Account.create({
355
- name: "Hermes Puggington",
393
+ creationProps: { name: "Hermes Puggington" },
356
394
  });
357
395
 
358
- const mapWithout = new TestMapWithOptionalRef(
396
+ const mapWithout = TestMapWithOptionalRef.create(
359
397
  {
360
398
  color: "red",
361
399
  },
@@ -365,13 +403,13 @@ describe("CoMap resolution", async () => {
365
403
  expect(mapWithout.color).toEqual("red");
366
404
  expect(mapWithout.nested).toEqual(undefined);
367
405
 
368
- const mapWith = new TestMapWithOptionalRef(
406
+ const mapWith = TestMapWithOptionalRef.create(
369
407
  {
370
408
  color: "red",
371
- nested: new NestedMap(
409
+ nested: NestedMap.create(
372
410
  {
373
411
  name: "wow!",
374
- twiceNested: new TwiceNestedMap(
412
+ twiceNested: TwiceNestedMap.create(
375
413
  { taste: "sour" },
376
414
  { owner: me }
377
415
  ),
@@ -388,20 +426,17 @@ describe("CoMap resolution", async () => {
388
426
  expect(mapWith.nested?._raw).toBeDefined();
389
427
  });
390
428
 
391
- class TestRecord extends CoMap<TestRecord> {
392
- declare [indexSignature]: number;
429
+ class TestRecord extends CoMap {
430
+ [co.items] = co.number;
393
431
  }
394
432
  interface TestRecord extends Record<string, number> {}
395
- TestRecord.encoding({
396
- [indexSignature]: "json",
397
- });
398
433
 
399
434
  test("Construction with index signature", async () => {
400
435
  const me = await Account.create({
401
- name: "Hermes Puggington",
436
+ creationProps: { name: "Hermes Puggington" },
402
437
  });
403
438
 
404
- const record = new TestRecord(
439
+ const record = TestRecord.create(
405
440
  {
406
441
  height: 5,
407
442
  other: 3,
@@ -414,5 +449,65 @@ describe("CoMap resolution", async () => {
414
449
  expect(record.other).toEqual(3);
415
450
  expect(record._raw.get("other")).toEqual(3);
416
451
  expect(Object.keys(record)).toEqual(["height", "other"]);
452
+ expect(record.toJSON()).toMatchObject({
453
+ _type: "CoMap",
454
+ height: 5,
455
+ id: expect.any(String),
456
+ other: 3,
457
+ });
458
+ });
459
+
460
+ class TestRecord2 extends CoMap.Record(co.number) {}
461
+
462
+ test("Construction with index signature (shorthand)", async () => {
463
+ const me = await Account.create({
464
+ creationProps: { name: "Hermes Puggington" },
465
+ });
466
+
467
+ const record = TestRecord2.create(
468
+ {
469
+ height: 5,
470
+ other: 3,
471
+ },
472
+ { owner: me }
473
+ );
474
+
475
+ expect(record.height).toEqual(5);
476
+ expect(record._raw.get("height")).toEqual(5);
477
+ expect(record.other).toEqual(3);
478
+ expect(record._raw.get("other")).toEqual(3);
479
+ expect(Object.keys(record)).toEqual(["height", "other"]);
480
+ });
481
+
482
+ class TestRecordRef extends CoMap.Record(co.ref(TwiceNestedMap)) {}
483
+
484
+ test("Construction with index signature ref", async () => {
485
+ const me = await Account.create({
486
+ creationProps: { name: "Hermes Puggington" },
487
+ });
488
+
489
+ const record = TestRecordRef.create(
490
+ {
491
+ firstNested: TwiceNestedMap.create(
492
+ { taste: "sour" },
493
+ { owner: me }
494
+ ),
495
+ secondNested: TwiceNestedMap.create(
496
+ { taste: "sweet" },
497
+ { owner: me }
498
+ ),
499
+ },
500
+ { owner: me }
501
+ );
502
+
503
+ expect(record.firstNested?.taste).toEqual("sour");
504
+ expect(record.firstNested?.id).toBeDefined();
505
+ expect(record.secondNested?.taste).toEqual("sweet");
506
+ expect(record.secondNested?.id).toBeDefined();
507
+ expect(Object.keys(record)).toEqual(["firstNested", "secondNested"]);
508
+ expect(Object.keys(record._refs)).toEqual([
509
+ "firstNested",
510
+ "secondNested",
511
+ ]);
417
512
  });
418
513
  });