jazz-tools 0.18.11 → 0.18.13

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 (132) hide show
  1. package/.svelte-kit/__package__/jazz.class.svelte.d.ts +24 -7
  2. package/.svelte-kit/__package__/jazz.class.svelte.d.ts.map +1 -1
  3. package/.svelte-kit/__package__/jazz.class.svelte.js +8 -0
  4. package/.turbo/turbo-build.log +52 -52
  5. package/CHANGELOG.md +25 -0
  6. package/dist/{chunk-RQHJFPIB.js → chunk-AEXYCCQS.js} +192 -71
  7. package/dist/chunk-AEXYCCQS.js.map +1 -0
  8. package/dist/index.js +1 -1
  9. package/dist/prosemirror/index.js +7 -1
  10. package/dist/prosemirror/index.js.map +1 -1
  11. package/dist/prosemirror/lib/plugin.d.ts.map +1 -1
  12. package/dist/react-core/hooks.d.ts +49 -1
  13. package/dist/react-core/hooks.d.ts.map +1 -1
  14. package/dist/react-core/index.js +49 -4808
  15. package/dist/react-core/index.js.map +1 -1
  16. package/dist/react-native-core/index.d.ts +1 -0
  17. package/dist/react-native-core/index.d.ts.map +1 -1
  18. package/dist/react-native-core/index.js +1 -0
  19. package/dist/react-native-core/index.js.map +1 -1
  20. package/dist/svelte/jazz.class.svelte.d.ts +24 -7
  21. package/dist/svelte/jazz.class.svelte.d.ts.map +1 -1
  22. package/dist/svelte/jazz.class.svelte.js +8 -0
  23. package/dist/testing.js +1 -1
  24. package/dist/tools/coValues/CoValueBase.d.ts +14 -2
  25. package/dist/tools/coValues/CoValueBase.d.ts.map +1 -1
  26. package/dist/tools/coValues/account.d.ts +3 -6
  27. package/dist/tools/coValues/account.d.ts.map +1 -1
  28. package/dist/tools/coValues/coFeed.d.ts +3 -11
  29. package/dist/tools/coValues/coFeed.d.ts.map +1 -1
  30. package/dist/tools/coValues/coList.d.ts +3 -6
  31. package/dist/tools/coValues/coList.d.ts.map +1 -1
  32. package/dist/tools/coValues/coMap.d.ts +3 -6
  33. package/dist/tools/coValues/coMap.d.ts.map +1 -1
  34. package/dist/tools/coValues/coPlainText.d.ts +0 -1
  35. package/dist/tools/coValues/coPlainText.d.ts.map +1 -1
  36. package/dist/tools/coValues/group.d.ts +9 -3
  37. package/dist/tools/coValues/group.d.ts.map +1 -1
  38. package/dist/tools/coValues/interfaces.d.ts +17 -0
  39. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  40. package/dist/tools/exports.d.ts +1 -1
  41. package/dist/tools/exports.d.ts.map +1 -1
  42. package/dist/tools/implementation/zodSchema/coExport.d.ts +2 -1
  43. package/dist/tools/implementation/zodSchema/coExport.d.ts.map +1 -1
  44. package/dist/tools/implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.d.ts.map +1 -1
  45. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts +7 -2
  46. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -1
  47. package/dist/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.d.ts +2 -1
  48. package/dist/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.d.ts.map +1 -1
  49. package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts +7 -1
  50. package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts.map +1 -1
  51. package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts +7 -1
  52. package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts.map +1 -1
  53. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts +7 -1
  54. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -1
  55. package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts +7 -1
  56. package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts.map +1 -1
  57. package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts +3 -0
  58. package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts.map +1 -1
  59. package/dist/tools/implementation/zodSchema/schemaTypes/GroupSchema.d.ts +25 -0
  60. package/dist/tools/implementation/zodSchema/schemaTypes/GroupSchema.d.ts.map +1 -0
  61. package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts +6 -1
  62. package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts.map +1 -1
  63. package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts +6 -1
  64. package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts.map +1 -1
  65. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchema.d.ts +3 -2
  66. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchema.d.ts.map +1 -1
  67. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.d.ts +3 -2
  68. package/dist/tools/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.d.ts.map +1 -1
  69. package/dist/tools/implementation/zodSchema/zodCo.d.ts +2 -0
  70. package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
  71. package/dist/tools/implementation/zodSchema/zodSchema.d.ts +4 -2
  72. package/dist/tools/implementation/zodSchema/zodSchema.d.ts.map +1 -1
  73. package/dist/tools/internal.d.ts +1 -0
  74. package/dist/tools/internal.d.ts.map +1 -1
  75. package/dist/tools/subscribe/CoValueCoreSubscription.d.ts +1 -5
  76. package/dist/tools/subscribe/CoValueCoreSubscription.d.ts.map +1 -1
  77. package/dist/tools/subscribe/SubscriptionScope.d.ts +3 -2
  78. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  79. package/dist/tools/subscribe/types.d.ts +5 -1
  80. package/dist/tools/subscribe/types.d.ts.map +1 -1
  81. package/dist/tools/testing.d.ts.map +1 -1
  82. package/dist/tools/tests/coFeed.branch.test.d.ts +2 -0
  83. package/dist/tools/tests/coFeed.branch.test.d.ts.map +1 -0
  84. package/dist/tools/tests/coList.branch.test.d.ts +2 -0
  85. package/dist/tools/tests/coList.branch.test.d.ts.map +1 -0
  86. package/dist/tools/tests/coMap.branch.test.d.ts +2 -0
  87. package/dist/tools/tests/coMap.branch.test.d.ts.map +1 -0
  88. package/dist/tools/tests/group.test.d.ts +2 -0
  89. package/dist/tools/tests/group.test.d.ts.map +1 -0
  90. package/package.json +4 -4
  91. package/src/prosemirror/lib/plugin.ts +7 -1
  92. package/src/react-core/hooks.ts +86 -6
  93. package/src/react-core/tests/useAccount.test.ts +94 -2
  94. package/src/react-core/tests/useCoState.test.ts +74 -0
  95. package/src/react-native-core/index.ts +4 -0
  96. package/src/svelte/jazz.class.svelte.ts +39 -7
  97. package/src/tools/coValues/CoValueBase.ts +44 -9
  98. package/src/tools/coValues/account.ts +6 -9
  99. package/src/tools/coValues/coFeed.ts +10 -18
  100. package/src/tools/coValues/coList.ts +10 -10
  101. package/src/tools/coValues/coMap.ts +7 -9
  102. package/src/tools/coValues/coPlainText.ts +1 -4
  103. package/src/tools/coValues/group.ts +23 -11
  104. package/src/tools/coValues/interfaces.ts +92 -1
  105. package/src/tools/exports.ts +1 -0
  106. package/src/tools/implementation/zodSchema/coExport.ts +2 -0
  107. package/src/tools/implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.ts +4 -0
  108. package/src/tools/implementation/zodSchema/schemaTypes/AccountSchema.ts +15 -1
  109. package/src/tools/implementation/zodSchema/schemaTypes/CoDiscriminatedUnionSchema.ts +2 -0
  110. package/src/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.ts +17 -0
  111. package/src/tools/implementation/zodSchema/schemaTypes/CoListSchema.ts +17 -0
  112. package/src/tools/implementation/zodSchema/schemaTypes/CoMapSchema.ts +23 -0
  113. package/src/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.ts +15 -0
  114. package/src/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.ts +9 -0
  115. package/src/tools/implementation/zodSchema/schemaTypes/GroupSchema.ts +71 -0
  116. package/src/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.ts +18 -2
  117. package/src/tools/implementation/zodSchema/schemaTypes/RichTextSchema.ts +18 -2
  118. package/src/tools/implementation/zodSchema/typeConverters/InstanceOfSchema.ts +36 -30
  119. package/src/tools/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.ts +42 -38
  120. package/src/tools/implementation/zodSchema/zodCo.ts +9 -0
  121. package/src/tools/implementation/zodSchema/zodSchema.ts +24 -19
  122. package/src/tools/internal.ts +2 -0
  123. package/src/tools/subscribe/CoValueCoreSubscription.test.ts +110 -7
  124. package/src/tools/subscribe/CoValueCoreSubscription.ts +17 -9
  125. package/src/tools/subscribe/SubscriptionScope.ts +5 -1
  126. package/src/tools/subscribe/types.ts +9 -1
  127. package/src/tools/tests/account.test.ts +4 -0
  128. package/src/tools/tests/coFeed.branch.test.ts +676 -0
  129. package/src/tools/tests/coList.branch.test.ts +797 -0
  130. package/src/tools/tests/coMap.branch.test.ts +883 -0
  131. package/src/tools/tests/group.test.ts +77 -0
  132. package/dist/chunk-RQHJFPIB.js.map +0 -1
@@ -13,6 +13,7 @@ import {
13
13
  AnyAccountSchema,
14
14
  CoValue,
15
15
  CoValueClassOrSchema,
16
+ Group,
16
17
  InboxSender,
17
18
  InstanceOfSchema,
18
19
  JazzContextManager,
@@ -22,10 +23,10 @@ import {
22
23
  ResolveQueryStrict,
23
24
  SubscriptionScope,
24
25
  coValueClassFromCoValueClassOrSchema,
26
+ type BranchDefinition,
25
27
  } from "jazz-tools";
26
28
  import { JazzContext, JazzContextManagerContext } from "./provider.js";
27
29
  import { getCurrentAccountFromContextManager } from "./utils.js";
28
- import { TypeSym } from "../tools/internal.js";
29
30
 
30
31
  export function useJazzContext<Acc extends Account>() {
31
32
  const value = useContext(JazzContext) as JazzContextType<Acc>;
@@ -89,6 +90,7 @@ function useCoValueSubscription<
89
90
  id: string | undefined | null,
90
91
  options?: {
91
92
  resolve?: ResolveQueryStrict<S, R>;
93
+ unstable_branch?: BranchDefinition;
92
94
  },
93
95
  ) {
94
96
  const contextManager = useJazzContextManager();
@@ -103,6 +105,15 @@ function useCoValueSubscription<
103
105
  };
104
106
  }
105
107
 
108
+ if (options?.unstable_branch?.owner === null) {
109
+ return {
110
+ subscription: null,
111
+ contextManager,
112
+ id,
113
+ Schema,
114
+ };
115
+ }
116
+
106
117
  const node = contextManager.getCurrentValue()!.node;
107
118
  const subscription = new SubscriptionScope<any>(
108
119
  node,
@@ -112,6 +123,9 @@ function useCoValueSubscription<
112
123
  ref: coValueClassFromCoValueClassOrSchema(Schema),
113
124
  optional: true,
114
125
  },
126
+ false,
127
+ false,
128
+ options?.unstable_branch,
115
129
  );
116
130
 
117
131
  return {
@@ -119,16 +133,23 @@ function useCoValueSubscription<
119
133
  contextManager,
120
134
  id,
121
135
  Schema,
136
+ branchName: options?.unstable_branch?.name,
137
+ branchOwnerId: options?.unstable_branch?.owner?.$jazz.id,
122
138
  };
123
139
  };
124
140
 
125
141
  const [subscription, setSubscription] = React.useState(createSubscription);
126
142
 
143
+ const branchName = options?.unstable_branch?.name;
144
+ const branchOwnerId = options?.unstable_branch?.owner?.$jazz.id;
145
+
127
146
  React.useLayoutEffect(() => {
128
147
  if (
129
148
  subscription.contextManager !== contextManager ||
130
149
  subscription.id !== id ||
131
- subscription.Schema !== Schema
150
+ subscription.Schema !== Schema ||
151
+ subscription.branchName !== branchName ||
152
+ subscription.branchOwnerId !== branchOwnerId
132
153
  ) {
133
154
  subscription.subscription?.destroy();
134
155
  setSubscription(createSubscription());
@@ -138,7 +159,7 @@ function useCoValueSubscription<
138
159
  subscription.subscription?.destroy();
139
160
  setSubscription(createSubscription());
140
161
  });
141
- }, [Schema, id, contextManager]);
162
+ }, [Schema, id, contextManager, branchName, branchOwnerId]);
142
163
 
143
164
  return subscription.subscription;
144
165
  }
@@ -240,6 +261,22 @@ export function useCoState<
240
261
  options?: {
241
262
  /** Resolve query to specify which nested CoValues to load */
242
263
  resolve?: ResolveQueryStrict<S, R>;
264
+ /**
265
+ * Create or load a branch for isolated editing.
266
+ *
267
+ * Branching lets you take a snapshot of the current state and start modifying it without affecting the canonical/shared version.
268
+ * It's a fork of your data graph: the same schema, but with diverging values.
269
+ *
270
+ * The checkout of the branch is applied on all the resolved values.
271
+ *
272
+ * @param name - A unique name for the branch. This identifies the branch
273
+ * and can be used to switch between different branches of the same CoValue.
274
+ * @param owner - The owner of the branch. Determines who can access and modify
275
+ * the branch. If not provided, the branch is owned by the current user.
276
+ *
277
+ * For more info see the [branching](https://jazz.tools/docs/react/using-covalues/version-control) documentation.
278
+ */
279
+ unstable_branch?: BranchDefinition;
243
280
  },
244
281
  ): Loaded<S, R> | undefined | null {
245
282
  const subscription = useCoValueSubscription(Schema, id, options);
@@ -398,6 +435,22 @@ export function useCoStateWithSelector<
398
435
  select: (value: Loaded<S, R> | undefined | null) => TSelectorReturn;
399
436
  /** Equality function to determine if the selected value has changed, defaults to `Object.is` */
400
437
  equalityFn?: (a: TSelectorReturn, b: TSelectorReturn) => boolean;
438
+ /**
439
+ * Create or load a branch for isolated editing.
440
+ *
441
+ * Branching lets you take a snapshot of the current state and start modifying it without affecting the canonical/shared version.
442
+ * It's a fork of your data graph: the same schema, but with diverging values.
443
+ *
444
+ * The checkout of the branch is applied on all the resolved values.
445
+ *
446
+ * @param name - A unique name for the branch. This identifies the branch
447
+ * and can be used to switch between different branches of the same CoValue.
448
+ * @param owner - The owner of the branch. Determines who can access and modify
449
+ * the branch. If not provided, the branch is owned by the current user.
450
+ *
451
+ * For more info see the [branching](https://jazz.tools/docs/react/using-covalues/version-control) documentation.
452
+ */
453
+ unstable_branch?: BranchDefinition;
401
454
  },
402
455
  ): TSelectorReturn {
403
456
  const subscription = useCoValueSubscription(Schema, id, options);
@@ -430,6 +483,7 @@ function useAccountSubscription<
430
483
  Schema: S,
431
484
  options?: {
432
485
  resolve?: ResolveQueryStrict<S, R>;
486
+ unstable_branch?: BranchDefinition;
433
487
  },
434
488
  ) {
435
489
  const contextManager = useJazzContextManager();
@@ -437,7 +491,7 @@ function useAccountSubscription<
437
491
  const createSubscription = () => {
438
492
  const agent = getCurrentAccountFromContextManager(contextManager);
439
493
 
440
- if (agent[TypeSym] === "Anonymous") {
494
+ if (agent.$type$ === "Anonymous") {
441
495
  return {
442
496
  subscription: null,
443
497
  contextManager,
@@ -457,21 +511,31 @@ function useAccountSubscription<
457
511
  ref: coValueClassFromCoValueClassOrSchema(Schema),
458
512
  optional: true,
459
513
  },
514
+ false,
515
+ false,
516
+ options?.unstable_branch,
460
517
  );
461
518
 
462
519
  return {
463
520
  subscription,
464
521
  contextManager,
465
522
  Schema,
523
+ branchName: options?.unstable_branch?.name,
524
+ branchOwnerId: options?.unstable_branch?.owner?.$jazz.id,
466
525
  };
467
526
  };
468
527
 
469
528
  const [subscription, setSubscription] = React.useState(createSubscription);
470
529
 
530
+ const branchName = options?.unstable_branch?.name;
531
+ const branchOwnerId = options?.unstable_branch?.owner?.$jazz.id;
532
+
471
533
  React.useLayoutEffect(() => {
472
534
  if (
473
535
  subscription.contextManager !== contextManager ||
474
- subscription.Schema !== Schema
536
+ subscription.Schema !== Schema ||
537
+ subscription.branchName !== options?.unstable_branch?.name ||
538
+ subscription.branchOwnerId !== options?.unstable_branch?.owner?.$jazz.id
475
539
  ) {
476
540
  subscription.subscription?.destroy();
477
541
  setSubscription(createSubscription());
@@ -481,7 +545,7 @@ function useAccountSubscription<
481
545
  subscription.subscription?.destroy();
482
546
  setSubscription(createSubscription());
483
547
  });
484
- }, [Schema, contextManager]);
548
+ }, [Schema, contextManager, branchName, branchOwnerId]);
485
549
 
486
550
  return subscription.subscription;
487
551
  }
@@ -547,6 +611,22 @@ export function useAccount<
547
611
  options?: {
548
612
  /** Resolve query to specify which nested CoValues to load from the account */
549
613
  resolve?: ResolveQueryStrict<A, R>;
614
+ /**
615
+ * Create or load a branch for isolated editing.
616
+ *
617
+ * Branching lets you take a snapshot of the current state and start modifying it without affecting the canonical/shared version.
618
+ * It's a fork of your data graph: the same schema, but with diverging values.
619
+ *
620
+ * The checkout of the branch is applied on all the resolved values.
621
+ *
622
+ * @param name - A unique name for the branch. This identifies the branch
623
+ * and can be used to switch between different branches of the same CoValue.
624
+ * @param owner - The owner of the branch. Determines who can access and modify
625
+ * the branch. If not provided, the branch is owned by the current user.
626
+ *
627
+ * For more info see the [branching](https://jazz.tools/docs/react/using-covalues/version-control) documentation.
628
+ */
629
+ unstable_branch?: BranchDefinition;
550
630
  },
551
631
  ): {
552
632
  me: Loaded<A, R> | undefined | null;
@@ -1,7 +1,7 @@
1
1
  // @vitest-environment happy-dom
2
2
 
3
- import { RefsToResolve, co, z } from "jazz-tools";
4
- import { beforeEach, describe, expect, it } from "vitest";
3
+ import { Group, RefsToResolve, co, z } from "jazz-tools";
4
+ import { assert, beforeEach, describe, expect, it } from "vitest";
5
5
  import { useAccount, useJazzContextManager } from "../hooks.js";
6
6
  import { useIsAuthenticated } from "../index.js";
7
7
  import {
@@ -219,4 +219,96 @@ describe("useAccount", () => {
219
219
  expect(result.current.me).toBe(null);
220
220
  expect(result.current.agent).toBe(account.guest);
221
221
  });
222
+
223
+ it("should work with branches - create branch, edit and merge", async () => {
224
+ const AccountRoot = co.map({
225
+ name: z.string(),
226
+ age: z.number(),
227
+ email: z.string(),
228
+ });
229
+
230
+ const AccountSchema = co
231
+ .account({
232
+ root: AccountRoot,
233
+ profile: co.profile(),
234
+ })
235
+ .withMigration((account, creationProps) => {
236
+ if (!account.$jazz.refs.root) {
237
+ account.$jazz.set("root", {
238
+ name: "John Doe",
239
+ age: 30,
240
+ email: "john@example.com",
241
+ });
242
+ }
243
+ });
244
+
245
+ const account = await createJazzTestAccount({
246
+ AccountSchema,
247
+ isCurrentActiveAccount: true,
248
+ });
249
+
250
+ const group = Group.create();
251
+ group.addMember("everyone", "writer");
252
+ // Use useAccount with the branch
253
+ const { result } = renderHook(
254
+ () => {
255
+ const branchAccount = useAccount(AccountSchema, {
256
+ resolve: {
257
+ root: true,
258
+ },
259
+ unstable_branch: { name: "feature-branch" },
260
+ });
261
+
262
+ const mainAccount = useAccount(AccountSchema, {
263
+ resolve: {
264
+ root: true,
265
+ },
266
+ });
267
+
268
+ return { branchAccount, mainAccount };
269
+ },
270
+ {
271
+ account,
272
+ },
273
+ );
274
+
275
+ await act(async () => {
276
+ // Wait for the account to be loaded
277
+ await new Promise((resolve) => setTimeout(resolve, 10));
278
+ });
279
+
280
+ expect(result.current).not.toBeNull();
281
+
282
+ const branchAccount = result.current.branchAccount;
283
+ const mainAccount = result.current.mainAccount;
284
+
285
+ assert(branchAccount?.me);
286
+ assert(mainAccount?.me);
287
+
288
+ act(() => {
289
+ branchAccount.me?.root.$jazz.applyDiff({
290
+ name: "John Smith",
291
+ age: 31,
292
+ email: "john.smith@example.com",
293
+ });
294
+ });
295
+
296
+ // Verify the branch has the changes
297
+ expect(branchAccount.me.root?.name).toBe("John Smith");
298
+ expect(branchAccount.me.root?.age).toBe(31);
299
+ expect(branchAccount.me.root?.email).toBe("john.smith@example.com");
300
+
301
+ // Verify the original is unchanged
302
+ expect(mainAccount.me.root?.name).toBe("John Doe");
303
+ expect(mainAccount.me.root?.age).toBe(30);
304
+ expect(mainAccount.me.root?.email).toBe("john@example.com");
305
+
306
+ // Merge the branch back
307
+ branchAccount.me.root.$jazz.unstable_merge();
308
+
309
+ // Verify the original now has the merged changes
310
+ expect(mainAccount.me.root?.name).toBe("John Smith");
311
+ expect(mainAccount.me.root?.age).toBe(31);
312
+ expect(mainAccount.me.root?.email).toBe("john.smith@example.com");
313
+ });
222
314
  });
@@ -559,4 +559,78 @@ describe("useCoState", () => {
559
559
 
560
560
  expect(renderings).toEqual([true]);
561
561
  });
562
+
563
+ it("should work with branches - create branch, edit and merge", async () => {
564
+ const Person = co.map({
565
+ name: z.string(),
566
+ age: z.number(),
567
+ email: z.string(),
568
+ });
569
+
570
+ const group = Group.create();
571
+ group.addMember("everyone", "writer");
572
+
573
+ const originalPerson = Person.create(
574
+ {
575
+ name: "John Doe",
576
+ age: 30,
577
+ email: "john@example.com",
578
+ },
579
+ group,
580
+ );
581
+
582
+ const account = await createJazzTestAccount({
583
+ isCurrentActiveAccount: true,
584
+ });
585
+
586
+ // Use useCoState with the branch
587
+ const { result } = renderHook(
588
+ () => {
589
+ const branch = useCoState(Person, originalPerson.$jazz.id, {
590
+ unstable_branch: { name: "feature-branch" },
591
+ });
592
+
593
+ const main = useCoState(Person, originalPerson.$jazz.id);
594
+
595
+ return { branch, main };
596
+ },
597
+ {
598
+ account,
599
+ },
600
+ );
601
+
602
+ await waitFor(() => {
603
+ expect(result.current).not.toBeNull();
604
+ });
605
+
606
+ const branchPerson = result.current.branch;
607
+
608
+ assert(branchPerson);
609
+
610
+ act(() => {
611
+ branchPerson.$jazz.applyDiff({
612
+ name: "John Smith",
613
+ age: 31,
614
+ email: "john.smith@example.com",
615
+ });
616
+ });
617
+
618
+ // Verify the branch has the changes
619
+ expect(result.current?.branch?.name).toBe("John Smith");
620
+ expect(result.current?.branch?.age).toBe(31);
621
+ expect(result.current?.branch?.email).toBe("john.smith@example.com");
622
+
623
+ // Verify the original is unchanged
624
+ expect(result.current?.main?.name).toBe("John Doe");
625
+ expect(result.current?.main?.age).toBe(30);
626
+ expect(result.current?.main?.email).toBe("john@example.com");
627
+
628
+ // Merge the branch back
629
+ await branchPerson.$jazz.unstable_merge();
630
+
631
+ // Verify the original now has the merged changes
632
+ expect(result.current?.main?.name).toBe("John Smith");
633
+ expect(result.current?.main?.age).toBe(31);
634
+ expect(result.current?.main?.email).toBe("john.smith@example.com");
635
+ });
562
636
  });
@@ -7,3 +7,7 @@ export * from "./media/image.js";
7
7
  export { SQLiteDatabaseDriverAsync } from "cojson";
8
8
  export { parseInviteLink } from "jazz-tools";
9
9
  export { createInviteLink, setupKvStore } from "./platform.js";
10
+ export {
11
+ ReactNativeContextManager,
12
+ type JazzContextManagerProps,
13
+ } from "./ReactNativeContextManager.js";
@@ -2,6 +2,7 @@ import type {
2
2
  Account,
3
3
  AccountClass,
4
4
  AnyAccountSchema,
5
+ BranchDefinition,
5
6
  CoValueClassOrSchema,
6
7
  CoValueFromRaw,
7
8
  InstanceOfSchema,
@@ -18,22 +19,46 @@ import { createSubscriber } from "svelte/reactivity";
18
19
  import { useIsAuthenticated } from "./auth/useIsAuthenticated.svelte.js";
19
20
  import { getJazzContext } from "./jazz.svelte";
20
21
 
22
+ type CoStateOptions<V extends CoValueClassOrSchema, R extends ResolveQuery<V>> = {
23
+ resolve?: ResolveQueryStrict<V, R>,
24
+ /**
25
+ * Create or load a branch for isolated editing.
26
+ *
27
+ * Branching lets you take a snapshot of the current state and start modifying it without affecting the canonical/shared version.
28
+ * It's a fork of your data graph: the same schema, but with diverging values.
29
+ *
30
+ * The checkout of the branch is applied on all the resolved values.
31
+ *
32
+ * @param name - A unique name for the branch. This identifies the branch
33
+ * and can be used to switch between different branches of the same CoValue.
34
+ * @param owner - The owner of the branch. Determines who can access and modify
35
+ * the branch. If not provided, the branch is owned by the current user.
36
+ *
37
+ * For more info see the [branching](https://jazz.tools/docs/svelte/using-covalues/version-control) documentation.
38
+ */
39
+ unstable_branch?: BranchDefinition
40
+ };
41
+
42
+ type CoStateId = string | undefined | null;
43
+
21
44
  export class CoState<
22
45
  V extends CoValueClassOrSchema,
23
46
  R extends ResolveQuery<V> = true,
24
47
  > {
25
48
  #value: Loaded<V, R> | undefined | null = undefined;
26
49
  #ctx = getJazzContext<InstanceOfSchema<AccountClass<Account>>>();
27
- #id: string | undefined | null;
50
+ #id: CoStateId;
28
51
  #subscribe: () => void;
29
52
  #update = () => {};
53
+ #options: CoStateOptions<V, R> | undefined;
30
54
 
31
55
  constructor(
32
56
  Schema: V,
33
- id: string | undefined | null | (() => string | undefined | null),
34
- options?: { resolve?: ResolveQueryStrict<V, R> },
57
+ id: CoStateId | (() => CoStateId),
58
+ options?: CoStateOptions<V, R> | (() => CoStateOptions<V, R>),
35
59
  ) {
36
60
  this.#id = $derived.by(typeof id === "function" ? id : () => id);
61
+ this.#options = $derived.by(typeof options === "function" ? options : () => options);
37
62
 
38
63
  this.#subscribe = createSubscriber((update) => {
39
64
  this.#update = update;
@@ -42,6 +67,7 @@ export class CoState<
42
67
  $effect.pre(() => {
43
68
  const ctx = this.#ctx.current;
44
69
  const id = this.#id;
70
+ const options = this.#options;
45
71
 
46
72
  return untrack(() => {
47
73
  if (!ctx || !id) {
@@ -63,6 +89,7 @@ export class CoState<
63
89
  this.update(null);
64
90
  },
65
91
  syncResolution: true,
92
+ unstable_branch: options?.unstable_branch,
66
93
  },
67
94
  (value) => {
68
95
  this.update(value as Loaded<V, R>);
@@ -90,22 +117,26 @@ export class CoState<
90
117
 
91
118
  export class AccountCoState<
92
119
  A extends
93
- | (AccountClass<Account> & CoValueFromRaw<Account>)
94
- | AnyAccountSchema,
120
+ | (AccountClass<Account> & CoValueFromRaw<Account>)
121
+ | AnyAccountSchema,
95
122
  R extends ResolveQuery<A> = true,
96
123
  > {
97
124
  #value: Loaded<A, R> | undefined | null = undefined;
98
125
  #ctx = getJazzContext<InstanceOfSchema<A>>();
99
126
  #subscribe: () => void;
100
- #update = () => {};
127
+ #options: CoStateOptions<A, R> | undefined;
128
+ #update = () => { };
129
+
130
+ constructor(Schema: A, options?: CoStateOptions<A, R> | (() => CoStateOptions<A, R>)) {
131
+ this.#options = $derived.by(typeof options === "function" ? options : () => options);
101
132
 
102
- constructor(Schema: A, options?: { resolve?: ResolveQueryStrict<A, R> }) {
103
133
  this.#subscribe = createSubscriber((update) => {
104
134
  this.#update = update;
105
135
  });
106
136
 
107
137
  $effect.pre(() => {
108
138
  const ctx = this.#ctx.current;
139
+ const options = this.#options;
109
140
 
110
141
  return untrack(() => {
111
142
  if (!ctx || !("me" in ctx)) {
@@ -128,6 +159,7 @@ export class AccountCoState<
128
159
  this.update(null);
129
160
  },
130
161
  syncResolution: true,
162
+ unstable_branch: options?.unstable_branch,
131
163
  },
132
164
  (value) => {
133
165
  this.update(value as Loaded<A, R>);
@@ -1,22 +1,17 @@
1
1
  import { ControlledAccount, LocalNode, type RawCoValue } from "cojson";
2
- import { CoreCoValueSchema } from "../implementation/zodSchema/schemaTypes/CoValueSchema.js";
3
2
  import {
4
3
  AnonymousJazzAgent,
5
4
  CoValue,
6
5
  CoValueClass,
7
- CoValueFromRaw,
8
6
  ID,
9
7
  RegisteredSchemas,
8
+ type SubscriptionScope,
10
9
  coValueClassFromCoValueClassOrSchema,
11
10
  coValuesCache,
12
11
  inspect,
12
+ unstable_mergeBranch,
13
13
  } from "../internal.js";
14
- import {
15
- CoValueClassOrSchema,
16
- Group,
17
- InstanceOfSchemaCoValuesNullable,
18
- TypeSym,
19
- } from "../internal.js";
14
+ import { Group, TypeSym } from "../internal.js";
20
15
 
21
16
  /** @internal */
22
17
  export abstract class CoValueBase implements CoValue {
@@ -46,6 +41,7 @@ export abstract class CoValueBase implements CoValue {
46
41
  export abstract class CoValueJazzApi<V extends CoValue> {
47
42
  /** @category Internals */
48
43
  declare _instanceID: string;
44
+ declare _subscriptionScope: SubscriptionScope<CoValue> | undefined;
49
45
 
50
46
  constructor(private coValue: V) {
51
47
  Object.defineProperty(this, "_instanceID", {
@@ -54,7 +50,16 @@ export abstract class CoValueJazzApi<V extends CoValue> {
54
50
  });
55
51
  }
56
52
 
57
- abstract get id(): ID<V>;
53
+ get id(): string {
54
+ const sourceId = this.raw.core.getCurrentBranchSourceId();
55
+
56
+ if (sourceId) {
57
+ return sourceId;
58
+ }
59
+
60
+ return this.raw.id;
61
+ }
62
+
58
63
  abstract get raw(): RawCoValue;
59
64
  abstract get owner(): Group | undefined;
60
65
 
@@ -109,4 +114,34 @@ export abstract class CoValueJazzApi<V extends CoValue> {
109
114
 
110
115
  return value;
111
116
  }
117
+
118
+ /**
119
+ * The name of the active branch of the CoValue
120
+ */
121
+ get branchName(): string | undefined {
122
+ const subscriptionScope = this._subscriptionScope;
123
+
124
+ return subscriptionScope?.unstable_branch?.name;
125
+ }
126
+
127
+ get isBranched(): boolean {
128
+ const subscriptionScope = this._subscriptionScope;
129
+
130
+ return Boolean(subscriptionScope?.unstable_branch);
131
+ }
132
+
133
+ /**
134
+ * Deeply merge the current branch into the main CoValues.
135
+ *
136
+ * Doesn't have any effect when there are no changes to merge, or the current CoValue is not a branch
137
+ */
138
+ unstable_merge() {
139
+ const subscriptionScope = this._subscriptionScope;
140
+
141
+ if (!subscriptionScope) {
142
+ return;
143
+ }
144
+
145
+ unstable_mergeBranch(subscriptionScope);
146
+ }
112
147
  }
@@ -15,6 +15,7 @@ import {
15
15
  } from "cojson";
16
16
  import {
17
17
  AnonymousJazzAgent,
18
+ BranchDefinition,
18
19
  CoFieldInit,
19
20
  type CoMap,
20
21
  type CoValue,
@@ -376,14 +377,6 @@ class AccountJazzApi<A extends Account> extends CoValueJazzApi<A> {
376
377
  }
377
378
  }
378
379
 
379
- /**
380
- * The ID of this `Account`
381
- * @category Content
382
- */
383
- get id(): ID<A> {
384
- return this.raw.id;
385
- }
386
-
387
380
  /**
388
381
  * Accounts have no owner. They can be accessed by everyone.
389
382
  */
@@ -487,6 +480,7 @@ class AccountJazzApi<A extends Account> extends CoValueJazzApi<A> {
487
480
  this: AccountJazzApi<A>,
488
481
  options: {
489
482
  resolve: RefsToResolveStrict<A, R>;
483
+ unstable_branch?: BranchDefinition;
490
484
  },
491
485
  ): Promise<Resolved<A, R>> {
492
486
  return ensureCoValueLoaded(this.account as unknown as A, options);
@@ -499,7 +493,10 @@ class AccountJazzApi<A extends Account> extends CoValueJazzApi<A> {
499
493
  ): () => void;
500
494
  subscribe<A extends Account, const R extends RefsToResolve<A>>(
501
495
  this: AccountJazzApi<A>,
502
- options: { resolve?: RefsToResolveStrict<A, R> },
496
+ options: {
497
+ resolve?: RefsToResolveStrict<A, R>;
498
+ unstable_branch?: BranchDefinition;
499
+ },
503
500
  listener: (value: Resolved<A, R>, unsubscribe: () => void) => void,
504
501
  ): () => void;
505
502
  subscribe<A extends Account, const R extends RefsToResolve<A>>(