jazz-tools 0.19.11 → 0.19.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 (151) hide show
  1. package/.turbo/turbo-build.log +51 -51
  2. package/CHANGELOG.md +22 -0
  3. package/dist/browser/createBrowserContext.d.ts +1 -5
  4. package/dist/browser/createBrowserContext.d.ts.map +1 -1
  5. package/dist/browser/index.js +124 -47
  6. package/dist/browser/index.js.map +1 -1
  7. package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.d.ts +12 -0
  8. package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.d.ts.map +1 -0
  9. package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.test.d.ts +2 -0
  10. package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.test.d.ts.map +1 -0
  11. package/dist/browser/provideBrowserLockSession/SessionIDStorage.d.ts +6 -0
  12. package/dist/browser/provideBrowserLockSession/SessionIDStorage.d.ts.map +1 -0
  13. package/dist/browser/provideBrowserLockSession/index.d.ts +4 -0
  14. package/dist/browser/provideBrowserLockSession/index.d.ts.map +1 -0
  15. package/dist/{chunk-HX5S6W5E.js → chunk-GAPMDNJY.js} +492 -108
  16. package/dist/chunk-GAPMDNJY.js.map +1 -0
  17. package/dist/index.js +5 -3
  18. package/dist/index.js.map +1 -1
  19. package/dist/inspector/{chunk-C6BJPHBQ.js → chunk-YQNK5Y7B.js} +47 -35
  20. package/dist/inspector/chunk-YQNK5Y7B.js.map +1 -0
  21. package/dist/inspector/{custom-element-GJVBPZES.js → custom-element-KYV64IOC.js} +47 -35
  22. package/dist/inspector/{custom-element-GJVBPZES.js.map → custom-element-KYV64IOC.js.map} +1 -1
  23. package/dist/inspector/index.js +1 -1
  24. package/dist/inspector/register-custom-element.js +1 -1
  25. package/dist/inspector/standalone.js +1 -1
  26. package/dist/inspector/tests/utils/transactions-changes.test.d.ts +2 -0
  27. package/dist/inspector/tests/utils/transactions-changes.test.d.ts.map +1 -0
  28. package/dist/inspector/utils/transactions-changes.d.ts +13 -13
  29. package/dist/inspector/utils/transactions-changes.d.ts.map +1 -1
  30. package/dist/react/index.js +4 -1
  31. package/dist/react/index.js.map +1 -1
  32. package/dist/react/provider.d.ts.map +1 -1
  33. package/dist/react-core/index.js +2 -2
  34. package/dist/react-core/index.js.map +1 -1
  35. package/dist/react-native/index.js +45 -13
  36. package/dist/react-native/index.js.map +1 -1
  37. package/dist/react-native-core/ReactNativeSessionProvider.d.ts +11 -0
  38. package/dist/react-native-core/ReactNativeSessionProvider.d.ts.map +1 -0
  39. package/dist/react-native-core/index.js +45 -13
  40. package/dist/react-native-core/index.js.map +1 -1
  41. package/dist/react-native-core/platform.d.ts +2 -8
  42. package/dist/react-native-core/platform.d.ts.map +1 -1
  43. package/dist/react-native-core/provider.d.ts.map +1 -1
  44. package/dist/react-native-core/tests/ReactNativeSessionProvider.test.d.ts +2 -0
  45. package/dist/react-native-core/tests/ReactNativeSessionProvider.test.d.ts.map +1 -0
  46. package/dist/testing.js +4 -3
  47. package/dist/testing.js.map +1 -1
  48. package/dist/tools/coValues/account.d.ts.map +1 -1
  49. package/dist/tools/coValues/coFeed.d.ts +2 -2
  50. package/dist/tools/coValues/coFeed.d.ts.map +1 -1
  51. package/dist/tools/coValues/coList.d.ts +1 -2
  52. package/dist/tools/coValues/coList.d.ts.map +1 -1
  53. package/dist/tools/coValues/coMap.d.ts.map +1 -1
  54. package/dist/tools/coValues/coVector.d.ts.map +1 -1
  55. package/dist/tools/coValues/group.d.ts +5 -1
  56. package/dist/tools/coValues/group.d.ts.map +1 -1
  57. package/dist/tools/coValues/interfaces.d.ts +2 -1
  58. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  59. package/dist/tools/exports.d.ts +2 -2
  60. package/dist/tools/exports.d.ts.map +1 -1
  61. package/dist/tools/implementation/ContextManager.d.ts.map +1 -1
  62. package/dist/tools/implementation/createContext.d.ts +21 -11
  63. package/dist/tools/implementation/createContext.d.ts.map +1 -1
  64. package/dist/tools/implementation/schema.d.ts +14 -6
  65. package/dist/tools/implementation/schema.d.ts.map +1 -1
  66. package/dist/tools/implementation/schemaUtils.d.ts +1 -1
  67. package/dist/tools/implementation/schemaUtils.d.ts.map +1 -1
  68. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts.map +1 -1
  69. package/dist/tools/implementation/zodSchema/schemaPermissions.d.ts +99 -0
  70. package/dist/tools/implementation/zodSchema/schemaPermissions.d.ts.map +1 -0
  71. package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts +11 -0
  72. package/dist/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts.map +1 -1
  73. package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts +11 -0
  74. package/dist/tools/implementation/zodSchema/schemaTypes/CoListSchema.d.ts.map +1 -1
  75. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts +15 -1
  76. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -1
  77. package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts +10 -0
  78. package/dist/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts.map +1 -1
  79. package/dist/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.d.ts +9 -0
  80. package/dist/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.d.ts.map +1 -1
  81. package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts +13 -1
  82. package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts.map +1 -1
  83. package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts +10 -0
  84. package/dist/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts.map +1 -1
  85. package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts +6 -0
  86. package/dist/tools/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts.map +1 -1
  87. package/dist/tools/implementation/zodSchema/unionUtils.d.ts +12 -1
  88. package/dist/tools/implementation/zodSchema/unionUtils.d.ts.map +1 -1
  89. package/dist/tools/internal.d.ts +1 -0
  90. package/dist/tools/internal.d.ts.map +1 -1
  91. package/dist/tools/subscribe/SubscriptionScope.d.ts +3 -6
  92. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  93. package/dist/tools/testing.d.ts.map +1 -1
  94. package/dist/tools/tests/schema.withPermissions.test.d.ts +2 -0
  95. package/dist/tools/tests/schema.withPermissions.test.d.ts.map +1 -0
  96. package/dist/worker/index.js +2 -2
  97. package/dist/worker/index.js.map +1 -1
  98. package/package.json +4 -4
  99. package/src/browser/createBrowserContext.ts +3 -62
  100. package/src/browser/provideBrowserLockSession/BrowserSessionProvider.test.ts +406 -0
  101. package/src/browser/provideBrowserLockSession/BrowserSessionProvider.ts +132 -0
  102. package/src/browser/provideBrowserLockSession/SessionIDStorage.ts +33 -0
  103. package/src/browser/provideBrowserLockSession/index.ts +11 -0
  104. package/src/inspector/tests/utils/transactions-changes.test.ts +102 -0
  105. package/src/inspector/ui/icons/add-icon.tsx +3 -3
  106. package/src/inspector/utils/history.ts +6 -6
  107. package/src/inspector/utils/transactions-changes.ts +37 -3
  108. package/src/inspector/viewer/history-view.tsx +13 -13
  109. package/src/react/provider.tsx +6 -1
  110. package/src/react-core/hooks.ts +2 -2
  111. package/src/react-core/tests/useSuspenseCoState.test.tsx +47 -0
  112. package/src/react-native-core/ReactNativeSessionProvider.ts +52 -0
  113. package/src/react-native-core/platform.ts +5 -30
  114. package/src/react-native-core/provider.tsx +6 -1
  115. package/src/react-native-core/tests/ReactNativeSessionProvider.test.ts +124 -0
  116. package/src/tools/coValues/account.ts +4 -0
  117. package/src/tools/coValues/coFeed.ts +8 -3
  118. package/src/tools/coValues/coList.ts +6 -3
  119. package/src/tools/coValues/coMap.ts +10 -0
  120. package/src/tools/coValues/coVector.ts +2 -1
  121. package/src/tools/coValues/group.ts +6 -4
  122. package/src/tools/coValues/interfaces.ts +19 -7
  123. package/src/tools/exports.ts +3 -1
  124. package/src/tools/implementation/ContextManager.ts +10 -0
  125. package/src/tools/implementation/createContext.ts +43 -15
  126. package/src/tools/implementation/schema.ts +23 -13
  127. package/src/tools/implementation/schemaUtils.ts +1 -1
  128. package/src/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.ts +105 -4
  129. package/src/tools/implementation/zodSchema/schemaPermissions.ts +188 -0
  130. package/src/tools/implementation/zodSchema/schemaTypes/CoFeedSchema.ts +46 -3
  131. package/src/tools/implementation/zodSchema/schemaTypes/CoListSchema.ts +46 -3
  132. package/src/tools/implementation/zodSchema/schemaTypes/CoMapSchema.ts +50 -13
  133. package/src/tools/implementation/zodSchema/schemaTypes/CoRecordSchema.ts +14 -0
  134. package/src/tools/implementation/zodSchema/schemaTypes/CoVectorSchema.ts +24 -1
  135. package/src/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.ts +51 -4
  136. package/src/tools/implementation/zodSchema/schemaTypes/PlainTextSchema.ts +25 -1
  137. package/src/tools/implementation/zodSchema/schemaTypes/RichTextSchema.ts +21 -1
  138. package/src/tools/implementation/zodSchema/unionUtils.ts +72 -20
  139. package/src/tools/internal.ts +1 -0
  140. package/src/tools/subscribe/SubscriptionScope.ts +61 -39
  141. package/src/tools/testing.ts +3 -1
  142. package/src/tools/tests/ContextManager.test.ts +2 -1
  143. package/src/tools/tests/coPlainText.test.ts +2 -2
  144. package/src/tools/tests/createContext.test.ts +79 -1
  145. package/src/tools/tests/deepLoading.test.ts +25 -2
  146. package/src/tools/tests/schema.resolved.test.ts +10 -0
  147. package/src/tools/tests/schema.withPermissions.test.ts +859 -0
  148. package/src/tools/tests/utils.ts +2 -2
  149. package/src/worker/index.ts +2 -2
  150. package/dist/chunk-HX5S6W5E.js.map +0 -1
  151. package/dist/inspector/chunk-C6BJPHBQ.js.map +0 -1
@@ -62,10 +62,10 @@ export function getTransactionChanges(
62
62
  const firstChange = tx.changes[0]!;
63
63
 
64
64
  if (
65
- TransactionChanges.isItemAppend(firstChange) &&
65
+ TransactionChanges.isItemAppend(coValue, firstChange) &&
66
66
  tx.changes.every(
67
67
  (c) =>
68
- TransactionChanges.isItemAppend(c) &&
68
+ TransactionChanges.isItemAppend(coValue, c) &&
69
69
  areSameOpIds(c.after, firstChange.after),
70
70
  )
71
71
  ) {
@@ -84,10 +84,10 @@ export function getTransactionChanges(
84
84
  }
85
85
 
86
86
  if (
87
- TransactionChanges.isItemPrepend(firstChange) &&
87
+ TransactionChanges.isItemPrepend(coValue, firstChange) &&
88
88
  tx.changes.every(
89
89
  (c) =>
90
- TransactionChanges.isItemPrepend(c) &&
90
+ TransactionChanges.isItemPrepend(coValue, c) &&
91
91
  areSameOpIds(c.before, firstChange.before),
92
92
  )
93
93
  ) {
@@ -106,8 +106,8 @@ export function getTransactionChanges(
106
106
  }
107
107
 
108
108
  if (
109
- TransactionChanges.isItemDeletion(firstChange) &&
110
- tx.changes.every((c) => TransactionChanges.isItemDeletion(c))
109
+ TransactionChanges.isItemDeletion(coValue, firstChange) &&
110
+ tx.changes.every((c) => TransactionChanges.isItemDeletion(coValue, c))
111
111
  ) {
112
112
  const coValueBeforeDeletions = coValue.atTime(tx.madeAt - 1);
113
113
 
@@ -14,85 +14,119 @@ import type {
14
14
  import { isCoId } from "../viewer/types";
15
15
 
16
16
  export const isGroupExtension = (
17
+ coValue: RawCoValue,
17
18
  change: any,
18
19
  ): change is Extract<
19
20
  MapOpPayload<`child_${string}`, "extend">,
20
21
  { op: "set" }
21
22
  > => {
23
+ if (coValue.core.isGroup() === false) return false;
22
24
  return change?.op === "set" && change?.value === "extend";
23
25
  };
24
26
 
25
27
  export const isGroupExtendRevocation = (
28
+ coValue: RawCoValue,
26
29
  change: any,
27
30
  ): change is Extract<
28
31
  MapOpPayload<`child_${string}`, "revoked">,
29
32
  { op: "set" }
30
33
  > => {
34
+ if (coValue.core.isGroup() === false) return false;
31
35
  return change?.op === "set" && change?.value === "revoked";
32
36
  };
33
37
 
34
38
  export const isGroupPromotion = (
39
+ coValue: RawCoValue,
35
40
  change: any,
36
41
  ): change is Extract<
37
42
  MapOpPayload<`parent_co_${string}`, AccountRole>,
38
43
  { op: "set" }
39
44
  > => {
45
+ if (coValue.core.isGroup() === false) return false;
40
46
  return change?.op === "set" && change?.key.startsWith("parent_co_");
41
47
  };
42
48
 
43
49
  export const isUserPromotion = (
50
+ coValue: RawCoValue,
44
51
  change: any,
45
52
  ): change is Extract<MapOpPayload<CoID<RawCoValue>, Role>, { op: "set" }> => {
53
+ if (coValue.core.isGroup() === false) return false;
46
54
  return (
47
55
  change?.op === "set" && (isCoId(change?.key) || change?.key === "everyone")
48
56
  );
49
57
  };
50
58
 
51
59
  export const isKeyRevelation = (
60
+ coValue: RawCoValue,
52
61
  change: any,
53
62
  ): change is Extract<
54
63
  MapOpPayload<`${string}_for_${string}`, string>,
55
64
  { op: "set" }
56
65
  > => {
66
+ if (
67
+ coValue.core.isGroup() === false &&
68
+ coValue.headerMeta?.type !== "account"
69
+ )
70
+ return false;
57
71
  return change?.op === "set" && change?.key.includes("_for_");
58
72
  };
59
73
 
60
74
  export const isPropertySet = (
75
+ coValue: RawCoValue,
61
76
  change: any,
62
77
  ): change is Extract<MapOpPayload<string, any>, { op: "set" }> => {
63
78
  return change?.op === "set" && "key" in change && "value" in change;
64
79
  };
65
80
  export const isPropertyDeletion = (
81
+ coValue: RawCoValue,
66
82
  change: any,
67
83
  ): change is Extract<MapOpPayload<string, any>, { op: "del" }> => {
68
84
  return change?.op === "del" && "key" in change;
69
85
  };
70
86
 
71
87
  export const isItemAppend = (
88
+ coValue: RawCoValue,
72
89
  change: any,
73
90
  ): change is Extract<ListOpPayload<any>, { op: "app" }> => {
91
+ if (coValue.type !== "colist" && coValue.type !== "coplaintext") return false;
74
92
  return change?.op === "app" && "after" in change && "value" in change;
75
93
  };
76
94
  export const isItemPrepend = (
95
+ coValue: RawCoValue,
77
96
  change: any,
78
97
  ): change is Extract<ListOpPayload<any>, { op: "pre" }> => {
98
+ if (coValue.type !== "colist" && coValue.type !== "coplaintext") return false;
79
99
  return change?.op === "pre" && "before" in change && "value" in change;
80
100
  };
81
101
 
82
102
  export const isItemDeletion = (
103
+ coValue: RawCoValue,
83
104
  change: any,
84
105
  ): change is Extract<ListOpPayload<any>, { op: "del" }> => {
106
+ if (coValue.type !== "colist" && coValue.type !== "coplaintext") return false;
85
107
  return change?.op === "del" && "insertion" in change;
86
108
  };
87
109
 
88
- export const isStreamStart = (change: any): change is BinaryStreamStart => {
110
+ export const isStreamStart = (
111
+ coValue: RawCoValue,
112
+ change: any,
113
+ ): change is BinaryStreamStart => {
114
+ if (coValue.type !== "coStream") return false;
89
115
  return change?.type === "start" && "mimeType" in change;
90
116
  };
91
117
 
92
- export const isStreamChunk = (change: any): change is BinaryStreamChunk => {
118
+ export const isStreamChunk = (
119
+ coValue: RawCoValue,
120
+ change: any,
121
+ ): change is BinaryStreamChunk => {
122
+ if (coValue.type !== "coStream") return false;
93
123
  return change?.type === "chunk" && "chunk" in change;
94
124
  };
95
125
 
96
- export const isStreamEnd = (change: any): change is BinaryStreamEnd => {
126
+ export const isStreamEnd = (
127
+ coValue: RawCoValue,
128
+ change: any,
129
+ ): change is BinaryStreamEnd => {
130
+ if (coValue.type !== "coStream") return false;
97
131
  return change?.type === "end";
98
132
  };
@@ -128,7 +128,7 @@ function mapTransactionToAction(
128
128
  coValue: RawCoValue,
129
129
  ): string {
130
130
  // Group changes
131
- if (TransactionChanges.isUserPromotion(change)) {
131
+ if (TransactionChanges.isUserPromotion(coValue, change)) {
132
132
  if (change.value === "revoked") {
133
133
  return `${change.key} has been revoked`;
134
134
  }
@@ -136,28 +136,28 @@ function mapTransactionToAction(
136
136
  return `${change.key} has been promoted to ${change.value}`;
137
137
  }
138
138
 
139
- if (TransactionChanges.isGroupExtension(change)) {
139
+ if (TransactionChanges.isGroupExtension(coValue, change)) {
140
140
  const child = change.key.slice(6);
141
141
  return `Group became a member of ${child}`;
142
142
  }
143
143
 
144
- if (TransactionChanges.isGroupExtendRevocation(change)) {
144
+ if (TransactionChanges.isGroupExtendRevocation(coValue, change)) {
145
145
  const child = change.key.slice(6);
146
146
  return `Group's membership of ${child} has been revoked.`;
147
147
  }
148
148
 
149
- if (TransactionChanges.isGroupPromotion(change)) {
149
+ if (TransactionChanges.isGroupPromotion(coValue, change)) {
150
150
  const parent = change.key.slice(7);
151
151
  return `Group ${parent} has been promoted to ${change.value}`;
152
152
  }
153
153
 
154
- if (TransactionChanges.isKeyRevelation(change)) {
154
+ if (TransactionChanges.isKeyRevelation(coValue, change)) {
155
155
  const [key, target] = change.key.split("_for_");
156
156
  return `Key "${key}" has been revealed to "${target}"`;
157
157
  }
158
158
 
159
159
  // coList changes
160
- if (TransactionChanges.isItemAppend(change)) {
160
+ if (TransactionChanges.isItemAppend(coValue, change)) {
161
161
  if (change.after === "start") {
162
162
  return `"${change.value}" has been appended`;
163
163
  }
@@ -171,7 +171,7 @@ function mapTransactionToAction(
171
171
  return `"${change.value}" has been inserted after "${(after as any).value}"`;
172
172
  }
173
173
 
174
- if (TransactionChanges.isItemPrepend(change)) {
174
+ if (TransactionChanges.isItemPrepend(coValue, change)) {
175
175
  if (change.before === "end") {
176
176
  return `"${change.value}" has been prepended`;
177
177
  }
@@ -185,7 +185,7 @@ function mapTransactionToAction(
185
185
  return `"${change.value}" has been inserted before "${(before as any).value}"`;
186
186
  }
187
187
 
188
- if (TransactionChanges.isItemDeletion(change)) {
188
+ if (TransactionChanges.isItemDeletion(coValue, change)) {
189
189
  const insertion = findListChange(change.insertion, coValue);
190
190
  if (insertion === undefined) {
191
191
  return `An undefined item has been deleted`;
@@ -195,24 +195,24 @@ function mapTransactionToAction(
195
195
  }
196
196
 
197
197
  // coStream changes
198
- if (TransactionChanges.isStreamStart(change)) {
198
+ if (TransactionChanges.isStreamStart(coValue, change)) {
199
199
  return `Stream started with mime type "${change.mimeType}" and file name "${change.fileName}"`;
200
200
  }
201
201
 
202
- if (TransactionChanges.isStreamChunk(change)) {
202
+ if (TransactionChanges.isStreamChunk(coValue, change)) {
203
203
  return `Stream chunk added`;
204
204
  }
205
205
 
206
- if (TransactionChanges.isStreamEnd(change)) {
206
+ if (TransactionChanges.isStreamEnd(coValue, change)) {
207
207
  return `Stream ended`;
208
208
  }
209
209
 
210
210
  // coMap changes
211
- if (TransactionChanges.isPropertySet(change)) {
211
+ if (TransactionChanges.isPropertySet(coValue, change)) {
212
212
  return `Property "${change.key}" has been set to ${JSON.stringify(change.value)}`;
213
213
  }
214
214
 
215
- if (TransactionChanges.isPropertyDeletion(change)) {
215
+ if (TransactionChanges.isPropertyDeletion(coValue, change)) {
216
216
  return `Property "${change.key}" has been deleted`;
217
217
  }
218
218
 
@@ -58,6 +58,9 @@ export function JazzReactProvider<
58
58
  );
59
59
  const logoutReplacementActiveRef = useRef(false);
60
60
  logoutReplacementActiveRef.current = Boolean(logOutReplacement);
61
+ const onAnonymousAccountDiscardedEnabled = Boolean(
62
+ onAnonymousAccountDiscarded,
63
+ );
61
64
 
62
65
  const value = React.useSyncExternalStore<
63
66
  JazzContextType<InstanceOfSchema<S>> | undefined
@@ -74,7 +77,9 @@ export function JazzReactProvider<
74
77
  logOutReplacement: logoutReplacementActiveRef.current
75
78
  ? logOutReplacementRefCallback
76
79
  : undefined,
77
- onAnonymousAccountDiscarded: onAnonymousAccountDiscardedRefCallback,
80
+ onAnonymousAccountDiscarded: onAnonymousAccountDiscardedEnabled
81
+ ? onAnonymousAccountDiscardedRefCallback
82
+ : undefined,
78
83
  } satisfies JazzContextManagerProps<S>;
79
84
 
80
85
  if (contextManager.propsChanged(props)) {
@@ -489,7 +489,7 @@ export function useSuspenseCoState<
489
489
  throw new Error("Subscription not found");
490
490
  }
491
491
 
492
- use(subscription.getPromise());
492
+ use(subscription.getCachedPromise());
493
493
 
494
494
  const getCurrentValue = () => {
495
495
  const value = subscription.getCurrentValue();
@@ -824,7 +824,7 @@ export function useSuspenseAccount<
824
824
  );
825
825
  }
826
826
 
827
- use(subscription.getPromise());
827
+ use(subscription.getCachedPromise());
828
828
 
829
829
  const getCurrentValue = () => {
830
830
  const value = subscription.getCurrentValue();
@@ -370,6 +370,53 @@ describe("useSuspenseCoState", () => {
370
370
  });
371
371
  });
372
372
 
373
+ it("should throw error when CoValue becomes unauthorized", async () => {
374
+ const TestMap = co.map({
375
+ value: z.string(),
376
+ });
377
+
378
+ const group = Group.create();
379
+ group.addMember("everyone", "reader");
380
+
381
+ // Create CoValue owned by another account without sharing
382
+ const map = TestMap.create(
383
+ {
384
+ value: "123",
385
+ },
386
+ group,
387
+ );
388
+
389
+ await createJazzTestAccount({
390
+ isCurrentActiveAccount: true,
391
+ });
392
+
393
+ const TestComponent = () => {
394
+ const value = useSuspenseCoState(TestMap, map.$jazz.id);
395
+ return <div>{value.value}</div>;
396
+ };
397
+
398
+ const { container } = await act(async () => {
399
+ return render(
400
+ <ErrorBoundary fallback={<div>Error!</div>}>
401
+ <Suspense fallback={<div>Loading...</div>}>
402
+ <TestComponent />
403
+ </Suspense>
404
+ </ErrorBoundary>,
405
+ );
406
+ });
407
+ await waitFor(() => {
408
+ expect(container.textContent).toContain("123");
409
+ expect(container.textContent).not.toContain("Loading...");
410
+ });
411
+
412
+ group.removeMember("everyone");
413
+
414
+ // Wait for error to be thrown (unauthorized access)
415
+ await waitFor(() => {
416
+ expect(container.textContent).toContain("Error!");
417
+ });
418
+ });
419
+
373
420
  it("should update value when CoValue changes", async () => {
374
421
  const TestMap = co.map({
375
422
  value: z.string(),
@@ -0,0 +1,52 @@
1
+ import {
2
+ CryptoProvider,
3
+ KvStoreContext,
4
+ SessionID,
5
+ SessionProvider,
6
+ } from "jazz-tools";
7
+ import { AgentID, RawAccountID } from "cojson";
8
+
9
+ export class ReactNativeSessionProvider implements SessionProvider {
10
+ async acquireSession(
11
+ accountID: string,
12
+ crypto: CryptoProvider,
13
+ ): Promise<{ sessionID: SessionID; sessionDone: () => void }> {
14
+ const kvStore = KvStoreContext.getInstance().getStorage();
15
+ const existingSession = await kvStore.get(accountID as string);
16
+
17
+ if (existingSession) {
18
+ console.log("Using existing session", existingSession);
19
+ return Promise.resolve({
20
+ sessionID: existingSession as SessionID,
21
+ sessionDone: () => {},
22
+ });
23
+ }
24
+
25
+ // We need to provide this for backwards compatibility with the old session provider
26
+ // With the current session provider we should never get here because:
27
+ // - New accounts provide their session and go through the persistSession method
28
+ // - Existing accounts should already have a session
29
+ const newSessionID = crypto.newRandomSessionID(
30
+ accountID as RawAccountID | AgentID,
31
+ );
32
+ await kvStore.set(accountID, newSessionID);
33
+
34
+ console.error("Created new session", newSessionID);
35
+
36
+ return Promise.resolve({
37
+ sessionID: newSessionID,
38
+ sessionDone: () => {},
39
+ });
40
+ }
41
+
42
+ async persistSession(
43
+ accountID: string,
44
+ sessionID: SessionID,
45
+ ): Promise<{ sessionDone: () => void }> {
46
+ const kvStore = KvStoreContext.getInstance().getStorage();
47
+ await kvStore.set(accountID, sessionID);
48
+ return Promise.resolve({
49
+ sessionDone: () => {},
50
+ });
51
+ }
52
+ }
@@ -1,25 +1,22 @@
1
1
  import NetInfo from "@react-native-community/netinfo";
2
- import { LocalNode, Peer, RawAccountID, getSqliteStorageAsync } from "cojson";
2
+ import { LocalNode, Peer, getSqliteStorageAsync } from "cojson";
3
3
  import { PureJSCrypto } from "cojson/dist/crypto/PureJSCrypto"; // Importing from dist to not rely on the exports field
4
4
  import {
5
5
  Account,
6
6
  AccountClass,
7
- AgentID,
8
7
  AnyAccountSchema,
9
8
  AuthCredentials,
10
9
  AuthSecretStorage,
11
10
  CoValue,
12
11
  CoValueFromRaw,
13
- CryptoProvider,
14
- ID,
15
12
  NewAccountProps,
16
- SessionID,
17
13
  SyncConfig,
18
14
  createInviteLink as baseCreateInviteLink,
19
15
  createAnonymousJazzContext,
20
16
  createJazzContext,
21
17
  } from "jazz-tools";
22
18
  import { KvStore, KvStoreContext } from "./storage/kv-store-context.js";
19
+ import { ReactNativeSessionProvider } from "./ReactNativeSessionProvider.js";
23
20
 
24
21
  import { SQLiteDatabaseDriverAsync } from "cojson";
25
22
  import { WebSocketPeerWithReconnection } from "cojson-transport-ws";
@@ -200,6 +197,8 @@ export async function createJazzReactNativeContext<
200
197
  handleAuthUpdate(authSecretStorage.isAuthenticated);
201
198
  }
202
199
 
200
+ const sessionProvider = new ReactNativeSessionProvider();
201
+
203
202
  const context = await createJazzContext({
204
203
  credentials: options.credentials,
205
204
  newAccountProps: options.newAccountProps,
@@ -207,7 +206,7 @@ export async function createJazzReactNativeContext<
207
206
  crypto,
208
207
  defaultProfileName: options.defaultProfileName,
209
208
  AccountSchema: options.AccountSchema,
210
- sessionProvider: provideLockSession,
209
+ sessionProvider,
211
210
  authSecretStorage: options.authSecretStorage,
212
211
  storage,
213
212
  });
@@ -233,30 +232,6 @@ export async function createJazzReactNativeContext<
233
232
  };
234
233
  }
235
234
 
236
- /** @category Auth Providers */
237
- export type SessionProvider = (
238
- accountID: ID<Account> | AgentID,
239
- ) => Promise<SessionID>;
240
-
241
- export async function provideLockSession(
242
- accountID: ID<Account> | AgentID,
243
- crypto: CryptoProvider,
244
- ) {
245
- const sessionDone = () => {};
246
-
247
- const kvStore = KvStoreContext.getInstance().getStorage();
248
-
249
- const sessionID =
250
- ((await kvStore.get(accountID)) as SessionID) ||
251
- crypto.newRandomSessionID(accountID as RawAccountID | AgentID);
252
- await kvStore.set(accountID, sessionID);
253
-
254
- return Promise.resolve({
255
- sessionID,
256
- sessionDone,
257
- });
258
- }
259
-
260
235
  /** @category Invite Links */
261
236
  export function createInviteLink<C extends CoValue>(
262
237
  value: C,
@@ -55,6 +55,9 @@ export function JazzProviderCore<
55
55
  );
56
56
  const logoutReplacementActiveRef = useRef(false);
57
57
  logoutReplacementActiveRef.current = Boolean(logOutReplacement);
58
+ const onAnonymousAccountDiscardedEnabled = Boolean(
59
+ onAnonymousAccountDiscarded,
60
+ );
58
61
 
59
62
  const value = React.useSyncExternalStore<
60
63
  JazzContextType<InstanceOfSchema<S>> | undefined
@@ -71,7 +74,9 @@ export function JazzProviderCore<
71
74
  logOutReplacement: logoutReplacementActiveRef.current
72
75
  ? logOutReplacementRefCallback
73
76
  : undefined,
74
- onAnonymousAccountDiscarded: onAnonymousAccountDiscardedRefCallback,
77
+ onAnonymousAccountDiscarded: onAnonymousAccountDiscardedEnabled
78
+ ? onAnonymousAccountDiscardedRefCallback
79
+ : undefined,
75
80
  CryptoProvider,
76
81
  } satisfies JazzContextManagerProps<S>;
77
82
 
@@ -0,0 +1,124 @@
1
+ import { WasmCrypto } from "cojson/crypto/WasmCrypto";
2
+ import { SessionID } from "cojson";
3
+ import { beforeEach, describe, expect, test } from "vitest";
4
+ import { InMemoryKVStore } from "jazz-tools";
5
+ import { KvStoreContext, type KvStore } from "jazz-tools";
6
+ import { ReactNativeSessionProvider } from "../ReactNativeSessionProvider.js";
7
+ import { createJazzTestAccount } from "jazz-tools/testing";
8
+ import type { CryptoProvider } from "jazz-tools";
9
+
10
+ // Initialize KV store for tests
11
+ const kvStore = new InMemoryKVStore() as KvStore;
12
+ KvStoreContext.getInstance().initialize(kvStore);
13
+
14
+ const Crypto = await WasmCrypto.create();
15
+
16
+ describe("ReactNativeSessionProvider", () => {
17
+ let sessionProvider: ReactNativeSessionProvider;
18
+ let account: Awaited<ReturnType<typeof createJazzTestAccount>>;
19
+
20
+ beforeEach(async () => {
21
+ // Clear KV store
22
+ kvStore.clearAll();
23
+
24
+ // Create new session provider instance
25
+ sessionProvider = new ReactNativeSessionProvider();
26
+
27
+ // Create test account
28
+ account = await createJazzTestAccount({
29
+ isCurrentActiveAccount: true,
30
+ });
31
+ });
32
+
33
+ describe("acquireSession", () => {
34
+ test("creates new session when none exists", async () => {
35
+ const accountID = account.$jazz.id;
36
+
37
+ // Verify no session exists
38
+ const existingSessionBefore = await kvStore.get(accountID);
39
+ expect(existingSessionBefore).toBeNull();
40
+
41
+ // Acquire session
42
+ const result = await sessionProvider.acquireSession(
43
+ accountID,
44
+ Crypto as CryptoProvider,
45
+ );
46
+
47
+ // Verify a new session ID is generated
48
+ expect(result.sessionID).toBeDefined();
49
+
50
+ // Verify the session is stored in KvStore
51
+ const storedSession = await kvStore.get(accountID);
52
+ expect(storedSession).toBeDefined();
53
+ expect(storedSession).toBe(result.sessionID);
54
+ });
55
+
56
+ test("returns existing session when one exists", async () => {
57
+ const accountID = account.$jazz.id;
58
+ const existingSessionID = "existing-session-id" as SessionID;
59
+
60
+ // Pre-populate KvStore with a session ID
61
+ await kvStore.set(accountID, existingSessionID);
62
+
63
+ // Verify session exists before calling acquireSession
64
+ const sessionBefore = await kvStore.get(accountID);
65
+ expect(sessionBefore).toBe(existingSessionID);
66
+
67
+ // Acquire session
68
+ const result = await sessionProvider.acquireSession(
69
+ accountID,
70
+ Crypto as CryptoProvider,
71
+ );
72
+
73
+ // Verify the existing session ID is returned (not a new one)
74
+ expect(result.sessionID).toBe(existingSessionID);
75
+
76
+ // Verify no new session is created (same value still in store)
77
+ const sessionAfter = await kvStore.get(accountID);
78
+ expect(sessionAfter).toBe(existingSessionID);
79
+ expect(sessionAfter).toBe(result.sessionID);
80
+ });
81
+ });
82
+
83
+ describe("persistSession", () => {
84
+ test("stores session ID correctly", async () => {
85
+ const accountID = account.$jazz.id;
86
+ const sessionID = "test-session-id" as SessionID;
87
+
88
+ // Verify no session exists before
89
+ const sessionBefore = await kvStore.get(accountID);
90
+ expect(sessionBefore).toBeNull();
91
+
92
+ // Persist session
93
+ await sessionProvider.persistSession(accountID, sessionID);
94
+
95
+ // Verify the session ID is stored in KvStore
96
+ const storedSession = await kvStore.get(accountID);
97
+ expect(storedSession).toBeDefined();
98
+
99
+ // Verify the stored value matches the provided session ID
100
+ expect(storedSession).toBe(sessionID);
101
+ });
102
+
103
+ test("overwrites existing session", async () => {
104
+ const accountID = account.$jazz.id;
105
+ const initialSessionID = "initial-session-id" as SessionID;
106
+ const newSessionID = "new-session-id" as SessionID;
107
+
108
+ // Store an initial session ID
109
+ await kvStore.set(accountID, initialSessionID);
110
+
111
+ // Verify initial session is stored
112
+ const sessionBefore = await kvStore.get(accountID);
113
+ expect(sessionBefore).toBe(initialSessionID);
114
+
115
+ // Persist a different session ID
116
+ await sessionProvider.persistSession(accountID, newSessionID);
117
+
118
+ // Verify the new session ID replaces the old one
119
+ const sessionAfter = await kvStore.get(accountID);
120
+ expect(sessionAfter).toBe(newSessionID);
121
+ expect(sessionAfter).not.toBe(initialSessionID);
122
+ });
123
+ });
124
+ });
@@ -466,10 +466,14 @@ class AccountJazzApi<A extends Account> extends CoValueJazzApi<A> {
466
466
  | undefined;
467
467
  if (!refId) {
468
468
  const descriptor = this.schema[key];
469
+ const newOwnerStrategy = descriptor.permissions?.newInlineOwnerStrategy;
470
+ const onCreate = descriptor.permissions?.onCreate;
469
471
  const coValue = instantiateRefEncodedWithInit(
470
472
  descriptor,
471
473
  value,
472
474
  accountOrGroupToGroup(this.account),
475
+ newOwnerStrategy,
476
+ onCreate,
473
477
  );
474
478
  refId = coValue.$jazz.id as CoID<RawCoMap>;
475
479
  }
@@ -32,8 +32,6 @@ import {
32
32
  SubscribeRestArgs,
33
33
  TypeSym,
34
34
  BranchDefinition,
35
- } from "../internal.js";
36
- import {
37
35
  Account,
38
36
  CoValueBase,
39
37
  CoValueJazzApi,
@@ -379,10 +377,15 @@ export class CoFeedJazzApi<F extends CoFeed> extends CoValueJazzApi<F> {
379
377
  } else if (isRefEncoded(itemDescriptor)) {
380
378
  let refId = (item as unknown as CoValue).$jazz?.id;
381
379
  if (!refId) {
380
+ const newOwnerStrategy =
381
+ itemDescriptor.permissions?.newInlineOwnerStrategy;
382
+ const onCreate = itemDescriptor.permissions?.onCreate;
382
383
  const coValue = instantiateRefEncodedWithInit(
383
384
  itemDescriptor,
384
385
  item,
385
386
  this.owner,
387
+ newOwnerStrategy,
388
+ onCreate,
386
389
  );
387
390
  refId = coValue.$jazz.id;
388
391
  }
@@ -720,6 +723,7 @@ export class FileStream extends CoValueBase implements CoValue {
720
723
  *
721
724
  * @param options - Configuration options for the new FileStream
722
725
  * @param options.owner - The Account or Group that will own this FileStream and control access rights
726
+ * @param schemaConfiguration - Internal schema configuration
723
727
  *
724
728
  * @example
725
729
  * ```typescript
@@ -743,7 +747,8 @@ export class FileStream extends CoValueBase implements CoValue {
743
747
  this: CoValueClass<S>,
744
748
  options?: { owner?: Account | Group } | Account | Group,
745
749
  ) {
746
- return new this(parseCoValueCreateOptions(options));
750
+ const { owner } = parseCoValueCreateOptions(options);
751
+ return new this({ owner });
747
752
  }
748
753
 
749
754
  getMetadata(): BinaryStreamInfo | undefined {