jazz-tools 0.18.28 → 0.18.30

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 (121) hide show
  1. package/.svelte-kit/__package__/media/image.svelte +7 -4
  2. package/.svelte-kit/__package__/media/image.svelte.d.ts.map +1 -1
  3. package/.svelte-kit/__package__/media/image.types.d.ts +1 -0
  4. package/.svelte-kit/__package__/media/image.types.d.ts.map +1 -1
  5. package/.svelte-kit/__package__/tests/media/image.svelte.test.js +63 -0
  6. package/.turbo/turbo-build.log +60 -60
  7. package/CHANGELOG.md +27 -0
  8. package/dist/better-auth/auth/client.d.ts +1 -1
  9. package/dist/better-auth/auth/server.d.ts +1 -1
  10. package/dist/better-auth/auth/server.d.ts.map +1 -1
  11. package/dist/better-auth/auth/server.js.map +1 -1
  12. package/dist/better-auth/database-adapter/index.d.ts +3 -3
  13. package/dist/better-auth/database-adapter/index.d.ts.map +1 -1
  14. package/dist/better-auth/database-adapter/index.js +6 -2
  15. package/dist/better-auth/database-adapter/index.js.map +1 -1
  16. package/dist/better-auth/database-adapter/utils.d.ts.map +1 -1
  17. package/dist/browser/index.d.ts +2 -1
  18. package/dist/browser/index.d.ts.map +1 -1
  19. package/dist/browser/index.js.map +1 -1
  20. package/dist/{chunk-YOL3XDDW.js → chunk-6BIYT3KH.js} +84 -50
  21. package/dist/chunk-6BIYT3KH.js.map +1 -0
  22. package/dist/index.js +3 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/inspector/{custom-element-A7UAELEG.js → custom-element-RQTLPAPJ.js} +2137 -1848
  25. package/dist/inspector/custom-element-RQTLPAPJ.js.map +1 -0
  26. package/dist/inspector/custom-element.d.ts.map +1 -1
  27. package/dist/inspector/index.d.ts +1 -1
  28. package/dist/inspector/index.d.ts.map +1 -1
  29. package/dist/inspector/index.js +570 -339
  30. package/dist/inspector/index.js.map +1 -1
  31. package/dist/inspector/register-custom-element.js +1 -1
  32. package/dist/inspector/ui/index.d.ts +6 -0
  33. package/dist/inspector/ui/index.d.ts.map +1 -0
  34. package/dist/inspector/viewer/group-view.d.ts +3 -2
  35. package/dist/inspector/viewer/group-view.d.ts.map +1 -1
  36. package/dist/inspector/viewer/page.d.ts.map +1 -1
  37. package/dist/react/index.js +2 -2
  38. package/dist/react/index.js.map +1 -1
  39. package/dist/react/media/image.d.ts +8 -0
  40. package/dist/react/media/image.d.ts.map +1 -1
  41. package/dist/react-native-core/index.js +3 -3
  42. package/dist/react-native-core/index.js.map +1 -1
  43. package/dist/react-native-core/media/image.d.ts +15 -0
  44. package/dist/react-native-core/media/image.d.ts.map +1 -1
  45. package/dist/svelte/media/image.svelte +7 -4
  46. package/dist/svelte/media/image.svelte.d.ts.map +1 -1
  47. package/dist/svelte/media/image.types.d.ts +1 -0
  48. package/dist/svelte/media/image.types.d.ts.map +1 -1
  49. package/dist/svelte/tests/media/image.svelte.test.js +63 -0
  50. package/dist/testing.js +8 -1
  51. package/dist/testing.js.map +1 -1
  52. package/dist/tools/coValues/account.d.ts +1 -0
  53. package/dist/tools/coValues/account.d.ts.map +1 -1
  54. package/dist/tools/coValues/group.d.ts +3 -3
  55. package/dist/tools/coValues/group.d.ts.map +1 -1
  56. package/dist/tools/coValues/interfaces.d.ts +9 -2
  57. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  58. package/dist/tools/exports.d.ts +2 -2
  59. package/dist/tools/exports.d.ts.map +1 -1
  60. package/dist/tools/implementation/invites.d.ts +2 -2
  61. package/dist/tools/implementation/invites.d.ts.map +1 -1
  62. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts +1 -1
  63. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts.map +1 -1
  64. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -1
  65. package/dist/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.d.ts +3 -1
  66. package/dist/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.d.ts.map +1 -1
  67. package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
  68. package/dist/tools/implementation/zodSchema/zodReExport.d.ts +1 -1
  69. package/dist/tools/implementation/zodSchema/zodReExport.d.ts.map +1 -1
  70. package/dist/tools/subscribe/CoValueCoreSubscription.d.ts.map +1 -1
  71. package/dist/tools/subscribe/SubscriptionScope.d.ts +0 -2
  72. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  73. package/dist/tools/testing.d.ts +1 -0
  74. package/dist/tools/testing.d.ts.map +1 -1
  75. package/dist/tools/tests/CoValueCoreSubscription.test.d.ts.map +1 -0
  76. package/package.json +5 -5
  77. package/src/better-auth/auth/server.ts +7 -2
  78. package/src/better-auth/auth/tests/server.test.ts +39 -17
  79. package/src/better-auth/database-adapter/index.ts +8 -5
  80. package/src/better-auth/database-adapter/utils.ts +4 -0
  81. package/src/browser/index.ts +2 -1
  82. package/src/inspector/custom-element.tsx +4 -0
  83. package/src/inspector/index.tsx +0 -2
  84. package/src/inspector/ui/index.ts +5 -0
  85. package/src/inspector/viewer/group-view.tsx +304 -20
  86. package/src/inspector/viewer/new-app.tsx +4 -4
  87. package/src/inspector/viewer/page.tsx +16 -2
  88. package/src/react/media/image.tsx +11 -2
  89. package/src/react/tests/media/image.test.tsx +94 -0
  90. package/src/react-native-core/media/image.tsx +11 -3
  91. package/src/svelte/media/image.svelte +7 -4
  92. package/src/svelte/media/image.types.ts +1 -0
  93. package/src/svelte/tests/media/image.svelte.test.ts +85 -0
  94. package/src/tools/coValues/account.ts +30 -5
  95. package/src/tools/coValues/coList.ts +3 -3
  96. package/src/tools/coValues/coMap.ts +3 -3
  97. package/src/tools/coValues/group.ts +13 -12
  98. package/src/tools/coValues/inbox.ts +5 -5
  99. package/src/tools/coValues/interfaces.ts +49 -18
  100. package/src/tools/exports.ts +1 -1
  101. package/src/tools/implementation/invites.ts +3 -8
  102. package/src/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.ts +5 -1
  103. package/src/tools/implementation/zodSchema/schemaTypes/AccountSchema.ts +2 -0
  104. package/src/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.ts +63 -50
  105. package/src/tools/implementation/zodSchema/zodReExport.ts +2 -2
  106. package/src/tools/subscribe/CoValueCoreSubscription.ts +17 -0
  107. package/src/tools/subscribe/SubscriptionScope.ts +1 -27
  108. package/src/tools/testing.ts +7 -0
  109. package/src/tools/{subscribe → tests}/CoValueCoreSubscription.test.ts +233 -3
  110. package/src/tools/tests/coFeed.branch.test.ts +14 -5
  111. package/src/tools/tests/coMap.test.ts +139 -42
  112. package/src/tools/tests/coMap.unique.test.ts +106 -1
  113. package/src/tools/tests/coOptional.test.ts +9 -1
  114. package/src/tools/tests/groupsAndAccounts.test.ts +156 -1
  115. package/src/tools/tests/load.test.ts +198 -1
  116. package/src/tools/tests/zod.test-d.ts +0 -2
  117. package/src/tools/tests/zod.test.ts +43 -40
  118. package/dist/chunk-YOL3XDDW.js.map +0 -1
  119. package/dist/inspector/custom-element-A7UAELEG.js.map +0 -1
  120. package/dist/tools/subscribe/CoValueCoreSubscription.test.d.ts.map +0 -1
  121. /package/dist/tools/{subscribe → tests}/CoValueCoreSubscription.test.d.ts +0 -0
@@ -37,7 +37,7 @@ describe("co.optional", () => {
37
37
  });
38
38
  });
39
39
 
40
- test("can use schem.optional() on all CoValue schemas but co.optional()", () => {
40
+ test("can use schema.optional() on all CoValue schemas but co.optional()", async () => {
41
41
  const Option1 = co.map({ type: z.literal("1") });
42
42
  const Option2 = co.map({ type: z.literal("2") });
43
43
  const Schema = co.map({
@@ -49,6 +49,7 @@ describe("co.optional", () => {
49
49
  map: co.map({ field: z.string() }).optional(),
50
50
  list: co.list(z.string()).optional(),
51
51
  feed: co.feed(z.string()).optional(),
52
+ account: co.account().optional(),
52
53
  union: co.discriminatedUnion("type", [Option1, Option2]).optional(),
53
54
  });
54
55
 
@@ -62,6 +63,7 @@ describe("co.optional", () => {
62
63
  expect(schema.map).toBeUndefined();
63
64
  expect(schema.list).toBeUndefined();
64
65
  expect(schema.feed).toBeUndefined();
66
+ expect(schema.account).toBeUndefined();
65
67
  expect(schema.union).toBeUndefined();
66
68
 
67
69
  schema.$jazz.set(
@@ -91,6 +93,11 @@ describe("co.optional", () => {
91
93
  );
92
94
  schema.$jazz.set("list", Schema.shape.list.innerType.create([]));
93
95
  schema.$jazz.set("feed", Schema.shape.feed.innerType.create([]));
96
+ const loadedAccount = await co
97
+ .account()
98
+ .getMe()
99
+ .$jazz.ensureLoaded({ resolve: { profile: true, root: true } });
100
+ schema.$jazz.set("account", loadedAccount);
94
101
  schema.$jazz.set("union", Option1.create({ type: "1" }));
95
102
 
96
103
  expect(schema.plainText?.toString()).toEqual("Hello");
@@ -101,6 +108,7 @@ describe("co.optional", () => {
101
108
  expect(schema.map?.field).toEqual("hello");
102
109
  expect(schema.list).toEqual([]);
103
110
  expect(schema.feed).not.toBeUndefined();
111
+ expect(schema.account?.profile.name).toEqual("Hermes Puggington");
104
112
  expect(schema.union?.type).toEqual("1");
105
113
  });
106
114
 
@@ -481,6 +481,9 @@ describe("Account permissions", () => {
481
481
  const group = Group.create({ owner: admin });
482
482
  const testObject = CoMap.create({}, { owner: group });
483
483
 
484
+ const manager = await co.account().createAs(admin, {
485
+ creationProps: { name: "Manager" },
486
+ });
484
487
  const writer = await co.account().createAs(admin, {
485
488
  creationProps: { name: "Writer" },
486
489
  });
@@ -492,11 +495,13 @@ describe("Account permissions", () => {
492
495
  });
493
496
 
494
497
  // Set up roles
498
+ group.addMember(manager, "manager");
495
499
  group.addMember(writer, "writer");
496
500
  group.addMember(reader, "reader");
497
501
  group.addMember(writeOnly, "writeOnly");
498
502
 
499
503
  // Test canRead permissions
504
+ expect(manager.canRead(testObject)).toBe(true);
500
505
  expect(admin.canRead(testObject)).toBe(true);
501
506
  expect(writer.canRead(testObject)).toBe(true);
502
507
  expect(reader.canRead(testObject)).toBe(true);
@@ -513,6 +518,9 @@ describe("Account permissions", () => {
513
518
  const group = Group.create({ owner: admin });
514
519
  const testObject = CoMap.create({}, { owner: group });
515
520
 
521
+ const manager = await co.account().createAs(admin, {
522
+ creationProps: { name: "Manager" },
523
+ });
516
524
  const writer = await co.account().createAs(admin, {
517
525
  creationProps: { name: "Writer" },
518
526
  });
@@ -524,11 +532,13 @@ describe("Account permissions", () => {
524
532
  });
525
533
 
526
534
  // Set up roles
535
+ group.addMember(manager, "manager");
527
536
  group.addMember(writer, "writer");
528
537
  group.addMember(reader, "reader");
529
538
  group.addMember(writeOnly, "writeOnly");
530
539
 
531
540
  // Test canWrite permissions
541
+ expect(manager.canWrite(testObject)).toBe(true);
532
542
  expect(admin.canWrite(testObject)).toBe(true);
533
543
  expect(writer.canWrite(testObject)).toBe(true);
534
544
  expect(reader.canWrite(testObject)).toBe(false);
@@ -538,13 +548,17 @@ describe("Account permissions", () => {
538
548
  test("canAdmin permissions for different roles", async () => {
539
549
  // Create test accounts
540
550
  const admin = await co.account().create({
541
- creationProps: { name: "Admin" },
551
+ creationProps: { name: "Super Admin" },
542
552
  crypto: Crypto,
543
553
  });
544
554
 
545
555
  const group = Group.create({ owner: admin });
546
556
  const testObject = CoMap.create({}, { owner: group });
547
557
 
558
+ const manager = await co.account().createAs(admin, {
559
+ creationProps: { name: "Admin" },
560
+ });
561
+
548
562
  const writer = await co.account().createAs(admin, {
549
563
  creationProps: { name: "Writer" },
550
564
  });
@@ -556,17 +570,57 @@ describe("Account permissions", () => {
556
570
  });
557
571
 
558
572
  // Set up roles
573
+ group.addMember(manager, "manager");
559
574
  group.addMember(writer, "writer");
560
575
  group.addMember(reader, "reader");
561
576
  group.addMember(writeOnly, "writeOnly");
562
577
 
563
578
  // Test canAdmin permissions
564
579
  expect(admin.canAdmin(testObject)).toBe(true);
580
+ expect(manager.canAdmin(testObject)).toBe(false);
565
581
  expect(writer.canAdmin(testObject)).toBe(false);
566
582
  expect(reader.canAdmin(testObject)).toBe(false);
567
583
  expect(writeOnly.canAdmin(testObject)).toBe(false);
568
584
  });
569
585
 
586
+ test("canManage permissions for different roles", async () => {
587
+ // Create test accounts
588
+ const admin = await co.account().create({
589
+ creationProps: { name: "Super Admin" },
590
+ crypto: Crypto,
591
+ });
592
+
593
+ const group = Group.create({ owner: admin });
594
+ const testObject = CoMap.create({}, { owner: group });
595
+
596
+ const manager = await co.account().createAs(admin, {
597
+ creationProps: { name: "Admin" },
598
+ });
599
+
600
+ const writer = await co.account().createAs(admin, {
601
+ creationProps: { name: "Writer" },
602
+ });
603
+ const reader = await co.account().createAs(admin, {
604
+ creationProps: { name: "Reader" },
605
+ });
606
+ const writeOnly = await co.account().createAs(admin, {
607
+ creationProps: { name: "WriteOnly" },
608
+ });
609
+
610
+ // Set up roles
611
+ group.addMember(manager, "manager");
612
+ group.addMember(writer, "writer");
613
+ group.addMember(reader, "reader");
614
+ group.addMember(writeOnly, "writeOnly");
615
+
616
+ // Test canManage permissions
617
+ expect(admin.canManage(testObject)).toBe(true);
618
+ expect(manager.canManage(testObject)).toBe(true);
619
+ expect(writer.canManage(testObject)).toBe(false);
620
+ expect(reader.canManage(testObject)).toBe(false);
621
+ expect(writeOnly.canManage(testObject)).toBe(false);
622
+ });
623
+
570
624
  test("permissions for non-members", async () => {
571
625
  const admin = await co.account().create({
572
626
  creationProps: { name: "Admin" },
@@ -584,6 +638,7 @@ describe("Account permissions", () => {
584
638
  expect(nonMember.canRead(testObject)).toBe(false);
585
639
  expect(nonMember.canWrite(testObject)).toBe(false);
586
640
  expect(nonMember.canAdmin(testObject)).toBe(false);
641
+ expect(nonMember.canManage(testObject)).toBe(false);
587
642
  });
588
643
 
589
644
  describe("permissions over Groups and Accounts", () => {
@@ -810,6 +865,106 @@ describe("Account permissions", () => {
810
865
  expect(account.canAdmin(group)).toBe(false);
811
866
  });
812
867
  });
868
+
869
+ describe("manage", () => {
870
+ test("can manage Account if it's itself", async () => {
871
+ const account = await co.account().create({
872
+ creationProps: { name: "Test Account" },
873
+ crypto: Crypto,
874
+ });
875
+ expect(account.canManage(account)).toBe(true);
876
+ });
877
+
878
+ test("cannot manage other accounts", async () => {
879
+ const account = await co.account().create({
880
+ creationProps: { name: "Test Account" },
881
+ crypto: Crypto,
882
+ });
883
+ const otherAccount = await co.account().create({
884
+ creationProps: { name: "Other Account" },
885
+ crypto: Crypto,
886
+ });
887
+ expect(account.canManage(otherAccount)).toBe(false);
888
+ });
889
+
890
+ test("can manage Group if it's an manage for that group", async () => {
891
+ const account = await co.account().create({
892
+ creationProps: { name: "Test Account" },
893
+ crypto: Crypto,
894
+ });
895
+ const otherAccount = await co.account().create({
896
+ creationProps: { name: "Other Account" },
897
+ crypto: Crypto,
898
+ });
899
+ const group = Group.create({ owner: otherAccount });
900
+
901
+ group.addMember(account, "manager");
902
+
903
+ expect(account.canManage(group)).toBe(true);
904
+ });
905
+
906
+ test("cannot manage Group if it's a writer for that group", async () => {
907
+ const account = await co.account().create({
908
+ creationProps: { name: "Test Account" },
909
+ crypto: Crypto,
910
+ });
911
+ const otherAccount = await co.account().create({
912
+ creationProps: { name: "Other Account" },
913
+ crypto: Crypto,
914
+ });
915
+ const group = Group.create({ owner: otherAccount });
916
+
917
+ group.addMember(account, "writer");
918
+
919
+ expect(account.canManage(group)).toBe(false);
920
+ });
921
+
922
+ test("cannot manage Group if it has writeOnly permissions for that group", async () => {
923
+ const account = await co.account().create({
924
+ creationProps: { name: "Test Account" },
925
+ crypto: Crypto,
926
+ });
927
+ const otherAccount = await co.account().create({
928
+ creationProps: { name: "Other Account" },
929
+ crypto: Crypto,
930
+ });
931
+ const group = Group.create({ owner: otherAccount });
932
+
933
+ group.addMember(account, "writeOnly");
934
+
935
+ expect(account.canManage(group)).toBe(false);
936
+ });
937
+
938
+ test("cannot manage Group if it's a reader for that group", async () => {
939
+ const account = await co.account().create({
940
+ creationProps: { name: "Test Account" },
941
+ crypto: Crypto,
942
+ });
943
+ const otherAccount = await co.account().create({
944
+ creationProps: { name: "Other Account" },
945
+ crypto: Crypto,
946
+ });
947
+ const group = Group.create({ owner: otherAccount });
948
+
949
+ group.addMember(account, "reader");
950
+
951
+ expect(account.canManage(group)).toBe(false);
952
+ });
953
+
954
+ test("cannot manage Group if it has no permissions for that group", async () => {
955
+ const account = await co.account().create({
956
+ creationProps: { name: "Test Account" },
957
+ crypto: Crypto,
958
+ });
959
+ const otherAccount = await co.account().create({
960
+ creationProps: { name: "Other Account" },
961
+ crypto: Crypto,
962
+ });
963
+ const group = Group.create({ owner: otherAccount });
964
+
965
+ expect(account.canManage(group)).toBe(false);
966
+ });
967
+ });
813
968
  });
814
969
  });
815
970
 
@@ -1,9 +1,10 @@
1
1
  import { waitFor } from "@testing-library/dom";
2
2
  import { cojsonInternals, emptyKnownState } from "cojson";
3
3
  import { assert, beforeEach, expect, test } from "vitest";
4
- import { Account, Group, co, z } from "../exports.js";
4
+ import { Account, Group, co, exportCoValue, z } from "../exports.js";
5
5
  import {
6
6
  createJazzTestAccount,
7
+ disableJazzTestSync,
7
8
  getPeerConnectedToTestSyncServer,
8
9
  setupJazzTestSync,
9
10
  } from "../testing.js";
@@ -270,3 +271,199 @@ test("load a large coValue", async () => {
270
271
  largeMap.data.$jazz.raw.core.knownState(),
271
272
  );
272
273
  });
274
+
275
+ test("should wait for the full streaming of the group", async () => {
276
+ disableJazzTestSync();
277
+
278
+ const alice = await createJazzTestAccount({
279
+ isCurrentActiveAccount: true,
280
+ creationProps: { name: "Hermes Puggington" },
281
+ });
282
+
283
+ const Person = co.map({
284
+ name: z.string(),
285
+ update: z.number(),
286
+ });
287
+
288
+ const group = Group.create();
289
+
290
+ const person = Person.create(
291
+ {
292
+ name: "Bob",
293
+ update: 1,
294
+ },
295
+ group,
296
+ );
297
+
298
+ // Make the group to grow big enough to trigger the streaming
299
+ for (let i = 0; i <= 300; i++) {
300
+ group.$jazz.raw.rotateReadKey();
301
+ }
302
+
303
+ group.addMember("everyone", "reader");
304
+
305
+ const bob = await createJazzTestAccount({
306
+ isCurrentActiveAccount: true,
307
+ });
308
+
309
+ const personContent = await exportCoValue(Person, person.$jazz.id, {
310
+ loadAs: alice,
311
+ });
312
+ assert(personContent);
313
+
314
+ const lastGroupPiece = personContent.findLast(
315
+ (content) => content.id === group.$jazz.id,
316
+ );
317
+ assert(lastGroupPiece);
318
+
319
+ for (const content of personContent.filter(
320
+ (content) => content !== lastGroupPiece,
321
+ )) {
322
+ bob.$jazz.localNode.syncManager.handleNewContent(content, "import");
323
+ }
324
+
325
+ // Simulate the streaming delay on the last piece of the group
326
+ setTimeout(() => {
327
+ bob.$jazz.localNode.syncManager.handleNewContent(lastGroupPiece, "import");
328
+ }, 10);
329
+
330
+ // Load the value and expect the migration to run only once
331
+ const loadedPerson = await Person.load(person.$jazz.id, { loadAs: bob });
332
+ expect(loadedPerson).not.toBeNull();
333
+ assert(loadedPerson);
334
+
335
+ expect(loadedPerson.$jazz.owner.$jazz.raw.core.verified.isStreaming()).toBe(
336
+ false,
337
+ );
338
+ });
339
+
340
+ test.skip("should wait for the full streaming of the parent groups", async () => {
341
+ disableJazzTestSync();
342
+
343
+ const alice = await createJazzTestAccount({
344
+ isCurrentActiveAccount: true,
345
+ creationProps: { name: "Hermes Puggington" },
346
+ });
347
+
348
+ const Person = co.map({
349
+ name: z.string(),
350
+ update: z.number(),
351
+ });
352
+
353
+ const parentGroup = Group.create();
354
+ const group = Group.create();
355
+
356
+ const person = Person.create(
357
+ {
358
+ name: "Bob",
359
+ update: 1,
360
+ },
361
+ group,
362
+ );
363
+
364
+ // Make the parent group to grow big enough to trigger the streaming
365
+ for (let i = 0; i <= 300; i++) {
366
+ parentGroup.$jazz.raw.rotateReadKey();
367
+ }
368
+
369
+ group.addMember(parentGroup);
370
+ parentGroup.addMember("everyone", "reader");
371
+
372
+ const bob = await createJazzTestAccount({
373
+ isCurrentActiveAccount: true,
374
+ });
375
+
376
+ const personContent = await exportCoValue(Person, person.$jazz.id, {
377
+ loadAs: alice,
378
+ });
379
+ assert(personContent);
380
+
381
+ const lastParentGroupPiece = personContent.findLast(
382
+ (content) => content.id === parentGroup.$jazz.id,
383
+ );
384
+ assert(lastParentGroupPiece);
385
+
386
+ for (const content of personContent.filter(
387
+ (content) => content !== lastParentGroupPiece,
388
+ )) {
389
+ bob.$jazz.localNode.syncManager.handleNewContent(content, "import");
390
+ }
391
+
392
+ // Simulate the streaming delay on the last piece of the parent group
393
+ setTimeout(() => {
394
+ bob.$jazz.localNode.syncManager.handleNewContent(
395
+ lastParentGroupPiece,
396
+ "import",
397
+ );
398
+ }, 10);
399
+
400
+ // Load the value and expect the migration to run only once
401
+ const loadedPerson = await Person.load(person.$jazz.id, { loadAs: bob });
402
+ expect(loadedPerson).not.toBeNull();
403
+ assert(loadedPerson);
404
+
405
+ expect(loadedPerson.$jazz.owner.$jazz.raw.core.verified.isStreaming()).toBe(
406
+ false,
407
+ );
408
+ });
409
+
410
+ test("should correctly reject the load if after the group streaming the account has no access", async () => {
411
+ disableJazzTestSync();
412
+
413
+ const alice = await createJazzTestAccount({
414
+ isCurrentActiveAccount: true,
415
+ creationProps: { name: "Hermes Puggington" },
416
+ });
417
+
418
+ const Person = co.map({
419
+ name: z.string(),
420
+ update: z.number(),
421
+ });
422
+
423
+ const group = Group.create();
424
+
425
+ const person = Person.create(
426
+ {
427
+ name: "Bob",
428
+ update: 1,
429
+ },
430
+ group,
431
+ );
432
+
433
+ group.addMember("everyone", "reader");
434
+
435
+ for (let i = 0; i <= 150; i++) {
436
+ group.$jazz.raw.rotateReadKey();
437
+ }
438
+
439
+ group.removeMember("everyone");
440
+
441
+ const bob = await createJazzTestAccount({
442
+ isCurrentActiveAccount: true,
443
+ });
444
+
445
+ const personContent = await exportCoValue(Person, person.$jazz.id, {
446
+ loadAs: alice,
447
+ });
448
+ assert(personContent);
449
+
450
+ const lastGroupPiece = personContent.findLast(
451
+ (content) => content.id === group.$jazz.id,
452
+ );
453
+ assert(lastGroupPiece);
454
+
455
+ for (const content of personContent.filter(
456
+ (content) => content !== lastGroupPiece,
457
+ )) {
458
+ bob.$jazz.localNode.syncManager.handleNewContent(content, "import");
459
+ }
460
+
461
+ // Simulate the streaming delay on the last piece of the group
462
+ setTimeout(() => {
463
+ bob.$jazz.localNode.syncManager.handleNewContent(lastGroupPiece, "import");
464
+ }, 10);
465
+
466
+ // Load the value and expect the migration to run only once
467
+ const loadedPerson = await Person.load(person.$jazz.id, { loadAs: bob });
468
+ expect(loadedPerson).toBeNull();
469
+ });
@@ -10,7 +10,6 @@ describe("CoValue and Zod schema compatibility", () => {
10
10
 
11
11
  const Person = co.map({
12
12
  // @ts-expect-error: cannot use z.record with a CoValue schema
13
- // (z.record is not exported by jazz-tools)
14
13
  pets: z.record(z.string(), Dog),
15
14
  });
16
15
  });
@@ -68,7 +67,6 @@ describe("CoValue and Zod schema compatibility", () => {
68
67
 
69
68
  const Person = co.map({
70
69
  // @ts-expect-error: cannot use z.intersection with a CoValue schema
71
- // (z.intersection is not exported by jazz-tools)
72
70
  pets: z.intersection(Dog, Cat),
73
71
  });
74
72
  });
@@ -369,6 +369,18 @@ describe("co.map and Zod schema compatibility", () => {
369
369
  expect(map.person).toEqual({ name: "John" });
370
370
  });
371
371
 
372
+ it("should handle record fields", async () => {
373
+ const schema = co.map({
374
+ record: z.record(z.string(), z.string()),
375
+ });
376
+ const account = await createJazzTestAccount();
377
+ const map = schema.create(
378
+ { record: { key1: "value1", key2: "value2" } },
379
+ account,
380
+ );
381
+ expect(map.record).toEqual({ key1: "value1", key2: "value2" });
382
+ });
383
+
372
384
  it("should handle tuple fields", async () => {
373
385
  const schema = co.map({
374
386
  tuple: z.tuple([z.string(), z.number(), z.boolean()]),
@@ -402,46 +414,37 @@ describe("co.map and Zod schema compatibility", () => {
402
414
  expect(map2.value).toBe(42);
403
415
  });
404
416
 
405
- // it("should handle discriminated unions of primitives", async () => {
406
- // const schema = co.map({
407
- // result: z.discriminatedUnion("status", [
408
- // z.object({ status: z.literal("success"), data: z.string() }),
409
- // z.object({ status: z.literal("failed"), error: z.string() }),
410
- // ]),
411
- // });
412
- // const account = await createJazzTestAccount();
413
- // const successMap = schema.create(
414
- // { result: { status: "success", data: "data" } },
415
- // account,
416
- // );
417
- // const failedMap = schema.create(
418
- // { result: { status: "failed", error: "error" } },
419
- // account,
420
- // );
421
- // expect(successMap.result).toEqual({ status: "success", data: "data" });
422
- // expect(failedMap.result).toEqual({ status: "failed", error: "error" });
423
- // });
424
-
425
- // it("should handle intersections", async () => {
426
- // const schema = co.map({
427
- // value: z.intersection(
428
- // z.union([z.number(), z.string()]),
429
- // z.union([z.number(), z.boolean()])
430
- // ),
431
- // });
432
- // const account = await createJazzTestAccount();
433
- // const map = schema.create({ value: 42 }, account);
434
- // expect(map.value).toBe(42);
435
- // });
436
-
437
- // it("should handle record types", async () => {
438
- // const schema = co.map({
439
- // cache: z.record(z.string(), z.string()),
440
- // });
441
- // const account = await createJazzTestAccount();
442
- // const map = schema.create({ cache: { key1: "value1", key2: "value2" } }, account);
443
- // expect(map.cache).toEqual({ key1: "value1", key2: "value2" });
444
- // });
417
+ it("should handle discriminated unions of primitives", async () => {
418
+ const schema = co.map({
419
+ result: z.discriminatedUnion("status", [
420
+ z.object({ status: z.literal("success"), data: z.string() }),
421
+ z.object({ status: z.literal("failed"), error: z.string() }),
422
+ ]),
423
+ });
424
+ const account = await createJazzTestAccount();
425
+ const successMap = schema.create(
426
+ { result: { status: "success", data: "data" } },
427
+ account,
428
+ );
429
+ const failedMap = schema.create(
430
+ { result: { status: "failed", error: "error" } },
431
+ account,
432
+ );
433
+ expect(successMap.result).toEqual({ status: "success", data: "data" });
434
+ expect(failedMap.result).toEqual({ status: "failed", error: "error" });
435
+ });
436
+
437
+ it("should handle intersections", async () => {
438
+ const schema = co.map({
439
+ value: z.intersection(
440
+ z.union([z.number(), z.string()]),
441
+ z.union([z.number(), z.boolean()]),
442
+ ),
443
+ });
444
+ const account = await createJazzTestAccount();
445
+ const map = schema.create({ value: 42 }, account);
446
+ expect(map.value).toBe(42);
447
+ });
445
448
 
446
449
  it("should handle refined types", async () => {
447
450
  const schema = co.map({