jazz-tools 0.19.0 → 0.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/.turbo/turbo-build.log +53 -53
  2. package/CHANGELOG.md +21 -0
  3. package/dist/{chunk-P3YLNFN4.js → chunk-NCNM6UDZ.js} +61 -22
  4. package/dist/chunk-NCNM6UDZ.js.map +1 -0
  5. package/dist/index.js +1 -1
  6. package/dist/inspector/{custom-element-QESCMFY7.js → custom-element-ABVPHX53.js} +1118 -465
  7. package/dist/inspector/custom-element-ABVPHX53.js.map +1 -0
  8. package/dist/inspector/index.js +1090 -437
  9. package/dist/inspector/index.js.map +1 -1
  10. package/dist/inspector/register-custom-element.js +1 -1
  11. package/dist/inspector/tests/utils/history.test.d.ts +2 -0
  12. package/dist/inspector/tests/utils/history.test.d.ts.map +1 -0
  13. package/dist/inspector/tests/viewer/co-value-editor.test.d.ts +2 -0
  14. package/dist/inspector/tests/viewer/co-value-editor.test.d.ts.map +1 -0
  15. package/dist/inspector/tests/viewer/comap-view.test.d.ts +2 -0
  16. package/dist/inspector/tests/viewer/comap-view.test.d.ts.map +1 -0
  17. package/dist/inspector/ui/icon.d.ts +6 -0
  18. package/dist/inspector/ui/icon.d.ts.map +1 -1
  19. package/dist/inspector/ui/icons/add-icon.d.ts +2 -0
  20. package/dist/inspector/ui/icons/add-icon.d.ts.map +1 -0
  21. package/dist/inspector/ui/icons/edit-icon.d.ts +2 -0
  22. package/dist/inspector/ui/icons/edit-icon.d.ts.map +1 -0
  23. package/dist/inspector/ui/icons/history.d.ts +2 -0
  24. package/dist/inspector/ui/icons/history.d.ts.map +1 -0
  25. package/dist/inspector/utils/history.d.ts +3 -0
  26. package/dist/inspector/utils/history.d.ts.map +1 -0
  27. package/dist/inspector/utils/transactions-changes.d.ts +38 -0
  28. package/dist/inspector/utils/transactions-changes.d.ts.map +1 -0
  29. package/dist/inspector/viewer/co-map-view.d.ts +9 -0
  30. package/dist/inspector/viewer/co-map-view.d.ts.map +1 -0
  31. package/dist/inspector/viewer/co-value-editor.d.ts +10 -0
  32. package/dist/inspector/viewer/co-value-editor.d.ts.map +1 -0
  33. package/dist/inspector/viewer/grid-view.d.ts +3 -2
  34. package/dist/inspector/viewer/grid-view.d.ts.map +1 -1
  35. package/dist/inspector/viewer/history-view.d.ts.map +1 -1
  36. package/dist/inspector/viewer/page.d.ts.map +1 -1
  37. package/dist/testing.js +1 -1
  38. package/dist/tools/coValues/CoFieldInit.d.ts +2 -1
  39. package/dist/tools/coValues/CoFieldInit.d.ts.map +1 -1
  40. package/dist/tools/coValues/deepLoading.d.ts +8 -7
  41. package/dist/tools/coValues/deepLoading.d.ts.map +1 -1
  42. package/dist/tools/coValues/interfaces.d.ts +3 -3
  43. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  44. package/dist/tools/coValues/schemaUnion.d.ts +6 -9
  45. package/dist/tools/coValues/schemaUnion.d.ts.map +1 -1
  46. package/dist/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.d.ts +18 -7
  47. package/dist/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.d.ts.map +1 -1
  48. package/dist/tools/implementation/zodSchema/typeConverters/CoFieldSchemaInit.d.ts +3 -2
  49. package/dist/tools/implementation/zodSchema/typeConverters/CoFieldSchemaInit.d.ts.map +1 -1
  50. package/dist/tools/implementation/zodSchema/unionUtils.d.ts.map +1 -1
  51. package/dist/tools/subscribe/SubscriptionScope.d.ts +1 -0
  52. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  53. package/package.json +4 -4
  54. package/src/inspector/tests/utils/history.test.ts +401 -0
  55. package/src/inspector/tests/viewer/co-value-editor.test.tsx +903 -0
  56. package/src/inspector/tests/viewer/comap-view.test.tsx +581 -0
  57. package/src/inspector/ui/icon.tsx +6 -0
  58. package/src/inspector/ui/icons/add-icon.tsx +21 -0
  59. package/src/inspector/ui/icons/edit-icon.tsx +17 -0
  60. package/src/inspector/ui/icons/history.tsx +28 -0
  61. package/src/inspector/ui/modal.tsx +3 -3
  62. package/src/inspector/utils/history.ts +49 -0
  63. package/src/inspector/utils/transactions-changes.ts +98 -0
  64. package/src/inspector/viewer/co-map-view.tsx +312 -0
  65. package/src/inspector/viewer/co-value-editor.tsx +164 -0
  66. package/src/inspector/viewer/grid-view.tsx +139 -10
  67. package/src/inspector/viewer/history-view.tsx +16 -118
  68. package/src/inspector/viewer/page.tsx +13 -0
  69. package/src/react-core/tests/usePassPhraseAuth.test.ts +1 -1
  70. package/src/tools/coValues/CoFieldInit.ts +6 -3
  71. package/src/tools/coValues/coList.ts +1 -1
  72. package/src/tools/coValues/deepLoading.ts +85 -71
  73. package/src/tools/coValues/interfaces.ts +3 -3
  74. package/src/tools/coValues/schemaUnion.ts +19 -14
  75. package/src/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.ts +69 -9
  76. package/src/tools/implementation/zodSchema/typeConverters/CoFieldSchemaInit.ts +12 -7
  77. package/src/tools/implementation/zodSchema/unionUtils.ts +35 -4
  78. package/src/tools/subscribe/SubscriptionScope.ts +3 -14
  79. package/src/tools/tests/coDiscriminatedUnion.test.ts +347 -5
  80. package/src/tools/tests/coVector.test.ts +43 -0
  81. package/src/tools/tests/deepLoading.test.ts +55 -59
  82. package/src/tools/tests/schema.resolved.test.ts +70 -1
  83. package/dist/chunk-P3YLNFN4.js.map +0 -1
  84. package/dist/inspector/custom-element-QESCMFY7.js.map +0 -1
@@ -1,13 +1,16 @@
1
1
  import { beforeEach, describe, expect, test, vi } from "vitest";
2
- import { CoPlainText, Loaded, co, z } from "../exports.js";
2
+ import { CoPlainText, Group, Loaded, co, loadCoValue, z } from "../exports.js";
3
3
  import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
4
4
  import { assertLoaded, waitFor } from "./utils.js";
5
+ import type { Account } from "jazz-tools";
5
6
 
6
7
  describe("co.discriminatedUnion", () => {
8
+ let account: Account;
9
+
7
10
  beforeEach(async () => {
8
11
  await setupJazzTestSync();
9
12
 
10
- await createJazzTestAccount({
13
+ account = await createJazzTestAccount({
11
14
  isCurrentActiveAccount: true,
12
15
  creationProps: { name: "Hermes Puggington" },
13
16
  });
@@ -266,7 +269,7 @@ describe("co.discriminatedUnion", () => {
266
269
  expect(referenceItem.children[0]?.type).toEqual("note");
267
270
  });
268
271
 
269
- test("load CoValue instances using the DiscriminatedUnion schema", async () => {
272
+ test("load CoValue instances using the DiscriminatedUnion schema without resolve", async () => {
270
273
  const Dog = co.map({
271
274
  type: z.literal("dog"),
272
275
  });
@@ -281,7 +284,40 @@ describe("co.discriminatedUnion", () => {
281
284
  expect(loadedPet.type).toEqual("dog");
282
285
  });
283
286
 
284
- test("subscribe to CoValue instances using the DiscriminatedUnion schema", async () => {
287
+ test("load CoValue instances using the DiscriminatedUnion schema with deep resolve", async () => {
288
+ const Person = co.map({
289
+ name: z.string(),
290
+ });
291
+ const Dog = co.map({
292
+ type: z.literal("dog"),
293
+ owner: Person,
294
+ });
295
+ const Cat = co.map({
296
+ type: z.literal("cat"),
297
+ owner: Person,
298
+ });
299
+ const Pet = co.discriminatedUnion("type", [Dog, Cat]);
300
+
301
+ const dog = Dog.create({
302
+ type: "dog",
303
+ owner: Person.create({
304
+ name: "John Doe",
305
+ }),
306
+ });
307
+
308
+ const loadedPet = await Pet.load(dog.$jazz.id, {
309
+ resolve: {
310
+ owner: true,
311
+ },
312
+ });
313
+
314
+ assertLoaded(loadedPet);
315
+
316
+ expect(loadedPet?.type).toEqual("dog");
317
+ expect(loadedPet?.owner.name).toEqual("John Doe");
318
+ });
319
+
320
+ test("subscribe to CoValue instances using the DiscriminatedUnion schema without resolve", async () => {
285
321
  const Person = co.map({
286
322
  name: z.string(),
287
323
  });
@@ -319,7 +355,41 @@ describe("co.discriminatedUnion", () => {
319
355
  expect(updates[0]?.name).toEqual("Rex");
320
356
  });
321
357
 
322
- test("should work when one of the options has a dicriminated union field", async () => {
358
+ test("subscribe to CoValue instances using the DiscriminatedUnion schema with deep resolve", async () => {
359
+ const Person = co.map({
360
+ name: z.string(),
361
+ });
362
+ const Dog = co.map({
363
+ type: z.literal("dog"),
364
+ owner: Person,
365
+ });
366
+ const Cat = co.map({
367
+ type: z.literal("cat"),
368
+ owner: Person,
369
+ });
370
+ const Pet = co.discriminatedUnion("type", [Dog, Cat]);
371
+
372
+ const dog = Dog.create({
373
+ type: "dog",
374
+ owner: Person.create({
375
+ name: "John Doe",
376
+ }),
377
+ });
378
+
379
+ const spy = vi.fn();
380
+ Pet.subscribe(dog.$jazz.id, { resolve: { owner: true } }, (pet) => {
381
+ expect(pet.owner.name).toEqual("John Doe");
382
+ spy(pet);
383
+ });
384
+
385
+ expect(spy).not.toHaveBeenCalled();
386
+
387
+ await waitFor(() => expect(spy).toHaveBeenCalled());
388
+
389
+ expect(spy).toHaveBeenCalledTimes(1);
390
+ });
391
+
392
+ test("should work when one of the options has a discriminated union field", async () => {
323
393
  const Collie = co.map({
324
394
  type: z.literal("collie"),
325
395
  });
@@ -371,4 +441,276 @@ describe("co.discriminatedUnion", () => {
371
441
  assertLoaded(loadedAnimal);
372
442
  expect(loadedAnimal.type).toEqual("collie");
373
443
  });
444
+
445
+ test("load co.discriminatedUnion with deep resolve using loadCoValue", async () => {
446
+ const Person = co.map({
447
+ name: z.string(),
448
+ });
449
+ const Dog = co.map({
450
+ type: z.literal("dog"),
451
+ owner: Person,
452
+ });
453
+ const Cat = co.map({
454
+ type: z.literal("cat"),
455
+ });
456
+
457
+ const Pet = co.discriminatedUnion("type", [Dog, Cat]);
458
+
459
+ const dog = Dog.create({
460
+ type: "dog",
461
+ owner: Person.create({ name: "John Doe" }),
462
+ });
463
+
464
+ const loadedPet = await loadCoValue(Pet.getCoValueClass(), dog.$jazz.id, {
465
+ resolve: { owner: true },
466
+ loadAs: account,
467
+ });
468
+
469
+ assertLoaded(loadedPet);
470
+
471
+ if (loadedPet.type === "dog") {
472
+ expect(loadedPet.owner.name).toEqual("John Doe");
473
+ }
474
+ });
475
+
476
+ test("load co.discriminatedUnion with non-matching deep resolve", async () => {
477
+ const Person = co.map({
478
+ name: z.string(),
479
+ });
480
+ const Dog = co.map({
481
+ type: z.literal("dog"),
482
+ owner: Person,
483
+ });
484
+ const Cat = co.map({
485
+ type: z.literal("cat"),
486
+ });
487
+ const Pet = co.discriminatedUnion("type", [Dog, Cat]);
488
+
489
+ const cat = Cat.create({
490
+ type: "cat",
491
+ });
492
+
493
+ const loadedPet = await Pet.load(cat.$jazz.id, {
494
+ resolve: { owner: true },
495
+ });
496
+
497
+ assertLoaded(loadedPet);
498
+
499
+ expect(loadedPet.type).toEqual("cat");
500
+ // @ts-expect-error - no owner on Cat
501
+ expect(loadedPet.owner).toBeUndefined();
502
+ });
503
+
504
+ test("load co.discriminatedUnion list with different schemas on deep resolved fields", async () => {
505
+ // Schema without nested CoValues
506
+ const Bird = co.map({
507
+ type: z.literal("bird"),
508
+ species: z.string(),
509
+ });
510
+ const Person = co.map({
511
+ name: z.string(),
512
+ bird: Bird,
513
+ });
514
+ // Schema with a nested CoValue
515
+ const Dog = co.map({
516
+ type: z.literal("dog"),
517
+ friend: Person,
518
+ });
519
+ // Same attribute (friend) with a completely different schema (list)
520
+ const Cat = co.map({
521
+ type: z.literal("cat"),
522
+ get friend() {
523
+ return co.list(Cat);
524
+ },
525
+ });
526
+ const Pet = co.discriminatedUnion("type", [Dog, Cat, Bird]);
527
+ const Pets = co.list(Pet);
528
+
529
+ const bird = Bird.create({
530
+ type: "bird",
531
+ species: "Parrot",
532
+ });
533
+
534
+ const dog = Dog.create({
535
+ type: "dog",
536
+ friend: Person.create({ name: "John Doe", bird }),
537
+ });
538
+
539
+ const cat = Cat.create({
540
+ type: "cat",
541
+ friend: [{ type: "cat", friend: [{ type: "cat", friend: [] }] }],
542
+ });
543
+
544
+ const pets = Pets.create([dog, cat, bird]);
545
+
546
+ const loadedPets = await Pets.load(pets.$jazz.id, {
547
+ resolve: { $each: { friend: { $each: { friend: true }, bird: true } } },
548
+ });
549
+
550
+ assertLoaded(loadedPets);
551
+
552
+ for (const pet of loadedPets) {
553
+ if (pet.type === "dog") {
554
+ expect(pet.friend.name).toEqual("John Doe");
555
+ expect(pet.friend.bird.species).toEqual("Parrot");
556
+ // @ts-expect-error - no species on Person
557
+ expect(pet.friend.species).toBeUndefined();
558
+ } else if (pet.type === "cat") {
559
+ expect(pet.friend[0]?.type).toEqual("cat");
560
+ // @ts-expect-error - no name on Bird
561
+ expect(pet.friend.name).toBeUndefined();
562
+ }
563
+ }
564
+ });
565
+
566
+ test("ensureLoaded on co.discriminatedUnion members", async () => {
567
+ const Person = co.map({
568
+ name: z.string(),
569
+ });
570
+ const Dog = co.map({
571
+ type: z.literal("dog"),
572
+ owner: Person,
573
+ });
574
+ const Cat = co.map({
575
+ type: z.literal("cat"),
576
+ friend: Person,
577
+ });
578
+ const Pet = co.discriminatedUnion("type", [Dog, Cat]);
579
+
580
+ const dog = Dog.create({
581
+ type: "dog",
582
+ owner: Person.create({ name: "John Doe" }),
583
+ });
584
+
585
+ const cat = Cat.create({
586
+ type: "cat",
587
+ friend: Person.create({ name: "Jane Doe" }),
588
+ });
589
+
590
+ const pet = await Pet.load(dog.$jazz.id);
591
+
592
+ assertLoaded(pet);
593
+
594
+ // @ts-expect-error - can't use ensureLoaded before narrowing
595
+ pet.$jazz.ensureLoaded({
596
+ resolve: { owner: true },
597
+ });
598
+
599
+ if (pet.type === "dog") {
600
+ const loadedPet = await pet.$jazz.ensureLoaded({
601
+ resolve: { owner: true },
602
+ });
603
+
604
+ expect(loadedPet.owner.name).toEqual("John Doe");
605
+ }
606
+ });
607
+
608
+ describe("Deep loading mutually exclusive nested CoMaps", async () => {
609
+ const Breed = co.map({
610
+ type: z.enum(["collie", "border-collie"]),
611
+ });
612
+ const Dog = co.map({
613
+ type: z.literal("dog"),
614
+ breed: Breed,
615
+ });
616
+
617
+ const Ocean = co.map({
618
+ name: z.enum(["atlantic", "pacific"]),
619
+ });
620
+ const Shark = co.map({
621
+ type: z.literal("shark"),
622
+ ocean: Ocean,
623
+ });
624
+
625
+ const Animal = co.discriminatedUnion("type", [Dog, Shark]);
626
+ const Species = co.list(Animal);
627
+
628
+ let species: Loaded<typeof Species>;
629
+
630
+ beforeEach(async () => {
631
+ const group = Group.create();
632
+ group.makePublic();
633
+
634
+ species = Species.create(
635
+ [
636
+ {
637
+ type: "dog",
638
+ breed: {
639
+ type: "collie",
640
+ },
641
+ },
642
+ {
643
+ type: "shark",
644
+ ocean: {
645
+ name: "atlantic",
646
+ },
647
+ },
648
+ ],
649
+ group,
650
+ );
651
+ });
652
+
653
+ test("co.discriminatedUnion should load with deeply resolved mutually exclusive nested CoMaps", async () => {
654
+ const loadedSpecies = await Species.load(species.$jazz.id, {
655
+ resolve: {
656
+ $each: {
657
+ breed: true,
658
+ ocean: true,
659
+ },
660
+ },
661
+ });
662
+
663
+ assertLoaded(loadedSpecies);
664
+
665
+ // @ts-expect-error - type needs to be narrowed
666
+ expect(loadedSpecies[0]?.breed.type).toEqual("collie");
667
+ // @ts-expect-error - type needs to be narrowed
668
+ expect(loadedSpecies[1]?.ocean.name).toEqual("atlantic");
669
+
670
+ for (const animal of loadedSpecies) {
671
+ if (animal.type === "dog") {
672
+ expect(animal.breed.type).toBeDefined();
673
+ // @ts-expect-error - no ocean property on Dog
674
+ expect(animal.ocean).toBeUndefined();
675
+ } else if (animal.type === "shark") {
676
+ expect(animal.ocean.name).toBeDefined();
677
+ // @ts-expect-error - no breed property on Shark
678
+ expect(animal.breed).toBeUndefined();
679
+ }
680
+ }
681
+ });
682
+
683
+ test("co.discriminatedUnion should load with deeply resolved nested CoMaps with another account as owner", async () => {
684
+ const alice = await createJazzTestAccount({
685
+ creationProps: { name: "Alice" },
686
+ isCurrentActiveAccount: false,
687
+ });
688
+
689
+ const loadedSpecies = await Species.load(species.$jazz.id, {
690
+ loadAs: alice,
691
+ resolve: {
692
+ $each: {
693
+ breed: true,
694
+ ocean: true,
695
+ },
696
+ },
697
+ });
698
+
699
+ console.log(loadedSpecies.$isLoaded);
700
+
701
+ assertLoaded(loadedSpecies);
702
+
703
+ for (const animal of loadedSpecies) {
704
+ if (animal.type === "dog") {
705
+ expect(animal.breed.type).toBeDefined();
706
+ // @ts-expect-error - no ocean on Dog
707
+ expect(animal.ocean).toBeUndefined();
708
+ } else if (animal.type === "shark") {
709
+ expect(animal.ocean.name).toBeDefined();
710
+ // @ts-expect-error - no breed on Shark
711
+ expect(animal.breed).toBeUndefined();
712
+ }
713
+ }
714
+ });
715
+ });
374
716
  });
@@ -112,6 +112,49 @@ describe("Creating a CoVector", async () => {
112
112
 
113
113
  expect(embedding.$jazz.owner).toEqual(group);
114
114
  });
115
+
116
+ describe("nested inside a container CoValue", async () => {
117
+ test("from an array of numbers", () => {
118
+ const VectorMap = co.map({
119
+ embedding: EmbeddingSchema,
120
+ });
121
+
122
+ const container = VectorMap.create({
123
+ embedding: [1, 2, 3],
124
+ });
125
+
126
+ expect(container.embedding).toBeInstanceOf(CoVector);
127
+ expect(Array.from(container.embedding)).toEqual([1, 2, 3]);
128
+ const vectorOwner = container.embedding.$jazz.owner;
129
+ expect(
130
+ vectorOwner.getParentGroups().map((group) => group.$jazz.id),
131
+ ).toContain(container.$jazz.owner.$jazz.id);
132
+
133
+ container.$jazz.set("embedding", [4, 5, 6]);
134
+ expect(Array.from(container.embedding)).toEqual([4, 5, 6]);
135
+ });
136
+
137
+ test("from a Float32Array", () => {
138
+ const VectorList = co.list(EmbeddingSchema);
139
+
140
+ const list = VectorList.create([new Float32Array([1, 2, 3])]);
141
+
142
+ const vector = list[0];
143
+ assert(vector);
144
+ expect(vector).toBeInstanceOf(CoVector);
145
+ expect(Array.from(vector)).toEqual([1, 2, 3]);
146
+ const vectorOwner = vector.$jazz.owner;
147
+ expect(
148
+ vectorOwner.getParentGroups().map((group) => group.$jazz.id),
149
+ ).toContain(list.$jazz.owner.$jazz.id);
150
+
151
+ list.$jazz.push(new Float32Array([4, 5, 6]));
152
+
153
+ const vector2 = list[1];
154
+ assert(vector2);
155
+ expect(Array.from(vector2)).toEqual([4, 5, 6]);
156
+ });
157
+ });
115
158
  });
116
159
 
117
160
  describe("CoVector structure", async () => {
@@ -361,55 +361,49 @@ test("Deep loading a record-like coMap", async () => {
361
361
  expect(recordLoaded.key2?.list).toBeTruthy();
362
362
  });
363
363
 
364
- test("The resolve type doesn't accept extra keys", async () => {
365
- expect.assertions(1);
366
-
364
+ test("The resolve type doesn't accept extra keys, but the load resolves anyway", async () => {
367
365
  const me = await CustomAccount.create({
368
366
  creationProps: { name: "Hermes Puggington" },
369
367
  crypto: Crypto,
370
368
  });
371
369
 
372
- try {
373
- const meLoaded = await me.$jazz.ensureLoaded({
374
- resolve: {
375
- // @ts-expect-error
376
- profile: { stream: true, extraKey: true },
377
- // @ts-expect-error
378
- root: { list: true, extraKey: true },
379
- },
380
- });
370
+ const meLoaded = await me.$jazz.ensureLoaded({
371
+ resolve: {
372
+ // @ts-expect-error
373
+ profile: { stream: true, extraKey: true },
374
+ // @ts-expect-error
375
+ root: { list: true, extraKey: true },
376
+ },
377
+ });
381
378
 
382
- await me.$jazz.ensureLoaded({
383
- resolve: {
384
- // @ts-expect-error
385
- root: { list: { $each: true, extraKey: true } },
386
- },
387
- });
379
+ await me.$jazz.ensureLoaded({
380
+ resolve: {
381
+ // @ts-expect-error
382
+ root: { list: { $each: true, extraKey: true } },
383
+ },
384
+ });
388
385
 
389
- await me.$jazz.ensureLoaded({
390
- resolve: {
391
- root: { list: true },
392
- // @ts-expect-error
393
- extraKey: true,
394
- },
395
- });
386
+ await me.$jazz.ensureLoaded({
387
+ resolve: {
388
+ root: { list: true },
389
+ // @ts-expect-error
390
+ extraKey: true,
391
+ },
392
+ });
396
393
 
397
- // using assignment to check type compatibility
398
- const _T:
399
- | (Loaded<typeof CustomAccount> & {
400
- profile: Loaded<typeof CustomProfile> & {
401
- stream: Loaded<typeof TestFeed>;
402
- extraKey: never;
403
- };
404
- root: Loaded<typeof TestMap> & {
405
- list: Loaded<typeof TestList>;
406
- extraKey: never;
407
- };
408
- })
409
- | null = meLoaded;
410
- } catch (e) {
411
- expect(e).toBeInstanceOf(Error);
412
- }
394
+ // using assignment to check type compatibility
395
+ const _T:
396
+ | (Loaded<typeof CustomAccount> & {
397
+ profile: Loaded<typeof CustomProfile> & {
398
+ stream: Loaded<typeof TestFeed>;
399
+ extraKey: never;
400
+ };
401
+ root: Loaded<typeof TestMap> & {
402
+ list: Loaded<typeof TestList>;
403
+ extraKey: never;
404
+ };
405
+ })
406
+ | null = meLoaded;
413
407
  });
414
408
 
415
409
  test("The resolve type accepts keys from optional fields", async () => {
@@ -435,7 +429,7 @@ test("The resolve type accepts keys from optional fields", async () => {
435
429
  expect(pets[0]?.owner?.name).toEqual("Rex");
436
430
  });
437
431
 
438
- test("The resolve type doesn't accept keys from discriminated unions", async () => {
432
+ test("The resolve type accepts keys from discriminated unions", async () => {
439
433
  const Person = co.map({
440
434
  name: z.string(),
441
435
  });
@@ -449,24 +443,25 @@ test("The resolve type doesn't accept keys from discriminated unions", async ()
449
443
  const Pet = co.discriminatedUnion("type", [Dog, Cat]);
450
444
  const Pets = co.list(Pet);
451
445
 
452
- const pets = await Pets.create([
446
+ const pets = Pets.create([
453
447
  Dog.create({ type: "dog", owner: Person.create({ name: "Rex" }) }),
448
+ Cat.create({ type: "cat" }),
454
449
  ]);
455
450
 
456
451
  await pets.$jazz.ensureLoaded({
457
- resolve: {
458
- $each: true,
459
- },
460
- });
461
-
462
- await pets.$jazz.ensureLoaded({
463
- // @ts-expect-error cannot resolve owner
464
452
  resolve: { $each: { owner: true } },
465
453
  });
466
454
 
467
455
  expect(pets).toBeTruthy();
468
- if (pets?.[0]?.type === "dog") {
469
- expect(pets[0].owner?.name).toEqual("Rex");
456
+
457
+ for (const pet of pets) {
458
+ if (pet.type === "dog") {
459
+ expect(pet.owner?.name).toEqual("Rex");
460
+ } else {
461
+ expect("owner" in pet).toEqual(false);
462
+ // @ts-expect-error - this should still not appear in the types
463
+ expect(pet.owner).toBeUndefined();
464
+ }
470
465
  }
471
466
  });
472
467
 
@@ -1110,7 +1105,7 @@ test("throw when calling ensureLoaded on a ref that's required but missing", asy
1110
1105
  ).rejects.toThrow("Failed to deeply load CoValue " + root.$jazz.id);
1111
1106
  });
1112
1107
 
1113
- test("throw when calling ensureLoaded on a ref that is not defined in the schema", async () => {
1108
+ test("returns the value when calling ensureLoaded on a ref that is not defined in the schema", async () => {
1114
1109
  const JazzRoot = co.map({});
1115
1110
 
1116
1111
  const me = await Account.create({
@@ -1120,12 +1115,13 @@ test("throw when calling ensureLoaded on a ref that is not defined in the schema
1120
1115
 
1121
1116
  const root = JazzRoot.create({}, { owner: me });
1122
1117
 
1123
- await expect(
1124
- root.$jazz.ensureLoaded({
1125
- // @ts-expect-error missing required ref
1126
- resolve: { profile: true },
1127
- }),
1128
- ).rejects.toThrow("Failed to deeply load CoValue " + root.$jazz.id);
1118
+ const loadedRoot = await JazzRoot.load(root.$jazz.id, {
1119
+ // @ts-expect-error missing required ref
1120
+ resolve: { profile: true },
1121
+ loadAs: me,
1122
+ });
1123
+
1124
+ expect(loadedRoot.$jazz.loadingState).toBe(CoValueLoadingState.LOADED);
1129
1125
  });
1130
1126
 
1131
1127
  test("should not throw when calling ensureLoaded a record with a deleted ref", async () => {
@@ -1,4 +1,11 @@
1
- import { beforeAll, describe, expect, expectTypeOf, test } from "vitest";
1
+ import {
2
+ assert,
3
+ beforeAll,
4
+ describe,
5
+ expect,
6
+ expectTypeOf,
7
+ test,
8
+ } from "vitest";
2
9
  import {
3
10
  Account,
4
11
  co,
@@ -104,6 +111,27 @@ describe("Schema.resolved()", () => {
104
111
  profile: true,
105
112
  });
106
113
  });
114
+
115
+ test("to a DiscriminatedUnion schema", () => {
116
+ const Pet = co.discriminatedUnion("type", [
117
+ co.map({
118
+ type: z.literal("dog"),
119
+ name: co.plainText(),
120
+ }),
121
+ co.map({
122
+ type: z.literal("cat"),
123
+ name: co.plainText(),
124
+ }),
125
+ ]);
126
+
127
+ const PetWithName = Pet.resolved({
128
+ name: true,
129
+ });
130
+
131
+ expect(PetWithName.resolveQuery).toEqual({
132
+ name: true,
133
+ });
134
+ });
107
135
  });
108
136
 
109
137
  describe("the schema's resolve query is used when loading CoValues", () => {
@@ -234,6 +262,47 @@ describe("Schema.resolved()", () => {
234
262
  expect(loadedAccount.profile.$isLoaded).toBe(true);
235
263
  expect(loadedAccount.profile.name).toBe("Hermes Puggington");
236
264
  });
265
+
266
+ test("for DiscriminatedUnion", async () => {
267
+ const Person = co.map({
268
+ name: co.plainText(),
269
+ });
270
+ const Dog = co.map({
271
+ type: z.literal("dog"),
272
+ name: co.plainText(),
273
+ owner: Person,
274
+ });
275
+ const Cat = co.map({
276
+ type: z.literal("cat"),
277
+ name: co.plainText(),
278
+ });
279
+ const Pet = co.discriminatedUnion("type", [Dog, Cat]).resolved({
280
+ name: true,
281
+ owner: {
282
+ name: true,
283
+ },
284
+ });
285
+
286
+ const dog = Dog.create(
287
+ {
288
+ type: "dog",
289
+ name: "Rex",
290
+ owner: { name: "Lewis" },
291
+ },
292
+ publicGroup,
293
+ );
294
+
295
+ const loadedDiscriminatedUnion = await Pet.load(dog.$jazz.id, {
296
+ loadAs: clientAccount,
297
+ });
298
+
299
+ assertLoaded(loadedDiscriminatedUnion);
300
+ expect(loadedDiscriminatedUnion.name.$isLoaded).toBe(true);
301
+ expect(loadedDiscriminatedUnion.name.toUpperCase()).toBe("REX");
302
+
303
+ assert(loadedDiscriminatedUnion.type === "dog");
304
+ expect(loadedDiscriminatedUnion.owner.name.toUpperCase()).toBe("LEWIS");
305
+ });
237
306
  });
238
307
 
239
308
  describe("on subscribe()", () => {