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
@@ -7,10 +7,15 @@ import {
7
7
  Settled,
8
8
  coOptionalDefiner,
9
9
  unstable_mergeBranchWithResolve,
10
+ withSchemaPermissions,
10
11
  } from "../../../internal.js";
11
12
  import { AnonymousJazzAgent } from "../../anonymousJazzAgent.js";
12
13
  import { CoOptionalSchema } from "./CoOptionalSchema.js";
13
14
  import { CoreCoValueSchema } from "./CoValueSchema.js";
15
+ import {
16
+ DEFAULT_SCHEMA_PERMISSIONS,
17
+ SchemaPermissions,
18
+ } from "../schemaPermissions.js";
14
19
 
15
20
  export interface CorePlainTextSchema extends CoreCoValueSchema {
16
21
  builtin: "CoPlainText";
@@ -29,6 +34,12 @@ export class PlainTextSchema implements CorePlainTextSchema {
29
34
  readonly builtin = "CoPlainText" as const;
30
35
  readonly resolveQuery = true as const;
31
36
 
37
+ /**
38
+ * Permissions to be used when creating or composing CoValues
39
+ * @internal
40
+ */
41
+ permissions: SchemaPermissions = DEFAULT_SCHEMA_PERMISSIONS;
42
+
32
43
  constructor(private coValueClass: typeof CoPlainText) {}
33
44
 
34
45
  create(text: string, options?: { owner: Group } | Group): CoPlainText;
@@ -41,7 +52,11 @@ export class PlainTextSchema implements CorePlainTextSchema {
41
52
  text: string,
42
53
  options?: { owner: Account | Group } | Account | Group,
43
54
  ): CoPlainText {
44
- return this.coValueClass.create(text, options);
55
+ const optionsWithPermissions = withSchemaPermissions(
56
+ options,
57
+ this.permissions,
58
+ );
59
+ return this.coValueClass.create(text, optionsWithPermissions);
45
60
  }
46
61
 
47
62
  load(
@@ -90,4 +105,13 @@ export class PlainTextSchema implements CorePlainTextSchema {
90
105
  optional(): CoOptionalSchema<this> {
91
106
  return coOptionalDefiner(this);
92
107
  }
108
+
109
+ /**
110
+ * Configure permissions to be used when creating or composing CoValues
111
+ */
112
+ withPermissions(permissions: SchemaPermissions): PlainTextSchema {
113
+ const copy = new PlainTextSchema(this.coValueClass);
114
+ copy.permissions = permissions;
115
+ return copy;
116
+ }
93
117
  }
@@ -6,10 +6,15 @@ import {
6
6
  Settled,
7
7
  coOptionalDefiner,
8
8
  unstable_mergeBranchWithResolve,
9
+ withSchemaPermissions,
9
10
  } from "../../../internal.js";
10
11
  import { AnonymousJazzAgent } from "../../anonymousJazzAgent.js";
11
12
  import { CoOptionalSchema } from "./CoOptionalSchema.js";
12
13
  import { CoreCoValueSchema } from "./CoValueSchema.js";
14
+ import {
15
+ DEFAULT_SCHEMA_PERMISSIONS,
16
+ SchemaPermissions,
17
+ } from "../schemaPermissions.js";
13
18
 
14
19
  export interface CoreRichTextSchema extends CoreCoValueSchema {
15
20
  builtin: "CoRichText";
@@ -28,6 +33,8 @@ export class RichTextSchema implements CoreRichTextSchema {
28
33
  readonly builtin = "CoRichText" as const;
29
34
  readonly resolveQuery = true as const;
30
35
 
36
+ permissions: SchemaPermissions = DEFAULT_SCHEMA_PERMISSIONS;
37
+
31
38
  constructor(private coValueClass: typeof CoRichText) {}
32
39
 
33
40
  create(text: string, options?: { owner: Group } | Group): CoRichText;
@@ -40,7 +47,11 @@ export class RichTextSchema implements CoreRichTextSchema {
40
47
  text: string,
41
48
  options?: { owner: Account | Group } | Account | Group,
42
49
  ): CoRichText {
43
- return this.coValueClass.create(text, options);
50
+ const optionsWithPermissions = withSchemaPermissions(
51
+ options,
52
+ this.permissions,
53
+ );
54
+ return this.coValueClass.create(text, optionsWithPermissions);
44
55
  }
45
56
 
46
57
  load(
@@ -85,4 +96,13 @@ export class RichTextSchema implements CoreRichTextSchema {
85
96
  optional(): CoOptionalSchema<this> {
86
97
  return coOptionalDefiner(this);
87
98
  }
99
+
100
+ /**
101
+ * Configure permissions to be used when creating or composing CoValues
102
+ */
103
+ withPermissions(permissions: SchemaPermissions): RichTextSchema {
104
+ const copy = new RichTextSchema(this.coValueClass);
105
+ copy.permissions = permissions;
106
+ return copy;
107
+ }
88
108
  }
@@ -1,3 +1,4 @@
1
+ import { JsonValue, RawCoMap } from "cojson";
1
2
  import {
2
3
  AnyZodOrCoValueSchema,
3
4
  CoDiscriminatedUnionSchema,
@@ -37,25 +38,7 @@ export function schemaUnionDiscriminatorFor(
37
38
  }
38
39
  }
39
40
 
40
- const availableOptions: DiscriminableCoreCoValueSchema[] = [];
41
-
42
- for (const option of options) {
43
- if (option.builtin === "CoMap") {
44
- availableOptions.push(option);
45
- } else if (option.builtin === "CoDiscriminatedUnion") {
46
- for (const subOption of (
47
- option as CoDiscriminatedUnionSchema<any>
48
- ).getDefinition().options) {
49
- if (!options.includes(subOption)) {
50
- options.push(subOption);
51
- }
52
- }
53
- } else {
54
- throw new Error(
55
- "Unsupported zod type in co.discriminatedUnion() of collaborative types",
56
- );
57
- }
58
- }
41
+ const availableOptions = getFlattenedUnionOptions(schema);
59
42
 
60
43
  const determineSchema: SchemaUnionDiscriminator<CoMap> = (
61
44
  discriminable,
@@ -77,7 +60,10 @@ export function schemaUnionDiscriminatorFor(
77
60
 
78
61
  for (const key of Object.keys(discriminatorMap)) {
79
62
  const discriminatorDef = optionDef.shape[key];
80
- const discriminatorValue = discriminable.get(key);
63
+ const discriminatorValue = resolveDiscriminantValue(
64
+ discriminable,
65
+ key,
66
+ );
81
67
 
82
68
  if (discriminatorValue && typeof discriminatorValue === "object") {
83
69
  throw new Error("Discriminator must be a primitive value");
@@ -175,3 +161,69 @@ function isCoDiscriminatedUnion(
175
161
  ): def is CoreCoDiscriminatedUnionSchema<any> {
176
162
  return def.builtin === "CoDiscriminatedUnion";
177
163
  }
164
+
165
+ /**
166
+ * Flattens all options from a discriminated union schema, including nested unions.
167
+ * Returns all options in a flat array.
168
+ */
169
+ export function getFlattenedUnionOptions(
170
+ schema: CoreCoDiscriminatedUnionSchema<DiscriminableCoValueSchemas>,
171
+ ): DiscriminableCoreCoValueSchema[] {
172
+ const definition = schema.getDefinition();
173
+ const options = definition.options;
174
+ const availableOptions: DiscriminableCoreCoValueSchema[] = [];
175
+
176
+ for (const option of options) {
177
+ if (option.builtin === "CoMap") {
178
+ availableOptions.push(option);
179
+ } else if (option.builtin === "CoDiscriminatedUnion") {
180
+ const nestedOptions = getFlattenedUnionOptions(
181
+ option as CoreCoDiscriminatedUnionSchema<DiscriminableCoValueSchemas>,
182
+ );
183
+ for (const subOption of nestedOptions) {
184
+ if (!availableOptions.includes(subOption)) {
185
+ availableOptions.push(subOption);
186
+ }
187
+ }
188
+ } else {
189
+ throw new Error(
190
+ "Unsupported zod type in co.discriminatedUnion() of collaborative types",
191
+ );
192
+ }
193
+ }
194
+
195
+ return availableOptions;
196
+ }
197
+
198
+ /**
199
+ * Gets the discriminator values for a given option and discriminator key
200
+ */
201
+ export function getDiscriminatorValuesForOption(
202
+ option: DiscriminableCoreCoValueSchema,
203
+ discriminatorKey: string,
204
+ ): Set<unknown> | undefined {
205
+ const optionDefinition = option.getDefinition();
206
+ return optionDefinition.discriminatorMap?.[discriminatorKey];
207
+ }
208
+
209
+ export function resolveDiscriminantValue(
210
+ init: unknown,
211
+ discriminatorKey: string,
212
+ ): JsonValue | undefined {
213
+ if (init == null) {
214
+ return undefined;
215
+ }
216
+
217
+ if (init instanceof Map || init instanceof RawCoMap) {
218
+ return init.get(discriminatorKey);
219
+ }
220
+
221
+ if (typeof init === "object") {
222
+ const record = init as Record<string, JsonValue>;
223
+ if (discriminatorKey in record) {
224
+ return record[discriminatorKey];
225
+ }
226
+ }
227
+
228
+ return undefined;
229
+ }
@@ -53,6 +53,7 @@ export * from "./implementation/zodSchema/typeConverters/InstanceOfSchemaCoValue
53
53
  export * from "./implementation/zodSchema/typeConverters/CoFieldSchemaInit.js";
54
54
  export * from "./implementation/zodSchema/runtimeConverters/coValueSchemaTransformation.js";
55
55
  export * from "./implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.js";
56
+ export * from "./implementation/zodSchema/schemaPermissions.js";
56
57
  export * from "./coValues/extensions/imageDef.js";
57
58
 
58
59
  export * from "./implementation/ContextManager.js";
@@ -309,64 +309,86 @@ export class SubscriptionScope<D extends CoValue> {
309
309
 
310
310
  unloadedValue: NotLoaded<D> | undefined;
311
311
 
312
- lastPromise:
313
- | {
314
- value: MaybeLoaded<D> | undefined;
315
- promise: PromiseWithStatus<MaybeLoaded<D>>;
316
- }
317
- | undefined;
318
-
319
- cachePromise(value: MaybeLoaded<D>, callback: () => PromiseWithStatus<D>) {
320
- if (this.lastPromise?.value === value) {
321
- return this.lastPromise.promise;
322
- }
323
-
324
- const promise = callback();
325
- this.lastPromise = { value, promise };
326
-
327
- return promise;
328
- }
312
+ lastPromise: PromiseWithStatus<D> | undefined;
329
313
 
330
314
  getPromise() {
331
315
  const currentValue = this.getCurrentValue();
332
316
 
333
317
  if (currentValue.$isLoaded) {
334
- return resolvedPromise(currentValue);
318
+ return resolvedPromise<D>(currentValue);
335
319
  }
336
320
 
337
321
  if (currentValue.$jazz.loadingState !== CoValueLoadingState.LOADING) {
338
322
  const error = this.getError();
339
- return rejectedPromise(
323
+ return rejectedPromise<D>(
340
324
  new Error(error?.toString() ?? "Unknown error", {
341
325
  cause: this.callerStack,
342
326
  }),
343
327
  );
344
328
  }
345
329
 
346
- // We need to cache rejected promises to make React Suspense happy
347
- return this.cachePromise(currentValue, () => {
348
- return new Promise<D>((resolve, reject) => {
349
- const unsubscribe = this.subscribe(() => {
350
- const currentValue = this.getCurrentValue();
330
+ const promise = new Promise<D>((resolve, reject) => {
331
+ const unsubscribe = this.subscribe(() => {
332
+ const currentValue = this.getCurrentValue();
351
333
 
352
- if (currentValue.$jazz.loadingState === CoValueLoadingState.LOADING) {
353
- return;
354
- }
334
+ if (currentValue.$jazz.loadingState === CoValueLoadingState.LOADING) {
335
+ return;
336
+ }
355
337
 
356
- if (currentValue.$isLoaded) {
357
- resolve(currentValue);
358
- } else {
359
- reject(
360
- new Error(this.getError()?.toString() ?? "Unknown error", {
361
- cause: this.callerStack,
362
- }),
363
- );
364
- }
338
+ if (currentValue.$isLoaded) {
339
+ promise.status = "fulfilled";
340
+ promise.value = currentValue;
341
+ resolve(currentValue);
342
+ } else {
343
+ promise.status = "rejected";
344
+ promise.reason = new Error(
345
+ this.getError()?.toString() ?? "Unknown error",
346
+ {
347
+ cause: this.callerStack,
348
+ },
349
+ );
350
+ reject(
351
+ new Error(this.getError()?.toString() ?? "Unknown error", {
352
+ cause: this.callerStack,
353
+ }),
354
+ );
355
+ }
365
356
 
366
- unsubscribe();
367
- });
357
+ unsubscribe();
368
358
  });
369
- });
359
+ }) as PromiseWithStatus<D>;
360
+
361
+ promise.status = "pending";
362
+
363
+ return promise;
364
+ }
365
+
366
+ getCachedPromise() {
367
+ if (this.lastPromise) {
368
+ const value = this.getCurrentValue();
369
+
370
+ // if the value is loaded, we update the promise state
371
+ // to ensure that the value provided is always up to date
372
+ if (value.$isLoaded) {
373
+ this.lastPromise.status = "fulfilled";
374
+ this.lastPromise.value = value;
375
+ } else if (value.$jazz.loadingState !== CoValueLoadingState.LOADING) {
376
+ this.lastPromise.status = "rejected";
377
+ this.lastPromise.reason = new Error(
378
+ this.getError()?.toString() ?? "Unknown error",
379
+ {
380
+ cause: this.callerStack,
381
+ },
382
+ );
383
+ } else if (this.lastPromise.status !== "pending") {
384
+ // Value got into loading state, we need to suspend again
385
+ this.lastPromise = this.getPromise();
386
+ }
387
+ } else {
388
+ this.lastPromise = this.getPromise();
389
+ }
390
+
391
+ return this.lastPromise;
370
392
  }
371
393
 
372
394
  private getUnloadedValue(reason: NotLoadedCoValueState): NotLoaded<D> {
@@ -16,9 +16,11 @@ import {
16
16
  coValueClassFromCoValueClassOrSchema,
17
17
  createAnonymousJazzContext,
18
18
  createJazzContext,
19
- randomSessionProvider,
19
+ MockSessionProvider,
20
20
  } from "./internal.js";
21
21
 
22
+ const randomSessionProvider = new MockSessionProvider();
23
+
22
24
  export { assertLoaded } from "./lib/utils.js";
23
25
 
24
26
  const syncServer: { current: LocalNode | null; asyncPeers: boolean } = {
@@ -19,7 +19,7 @@ import {
19
19
  } from "../implementation/ContextManager";
20
20
  import {
21
21
  createJazzContext,
22
- randomSessionProvider,
22
+ MockSessionProvider,
23
23
  } from "../implementation/createContext";
24
24
  import {
25
25
  CoValueFromRaw,
@@ -37,6 +37,7 @@ import { SubscriptionCache } from "../subscribe/SubscriptionCache";
37
37
  import { createAsyncStorage, getDbPath } from "./testStorage";
38
38
 
39
39
  const Crypto = await WasmCrypto.create();
40
+ const randomSessionProvider = new MockSessionProvider();
40
41
 
41
42
  class TestJazzContextManager<Acc extends Account> extends JazzContextManager<
42
43
  Acc,
@@ -7,10 +7,10 @@ import {
7
7
  createJazzContextFromExistingCredentials,
8
8
  isControlledAccount,
9
9
  } from "../index.js";
10
- import { co, randomSessionProvider } from "../internal.js";
10
+ import { co, MockSessionProvider } from "../internal.js";
11
11
 
12
12
  const Crypto = await WasmCrypto.create();
13
-
13
+ const randomSessionProvider = new MockSessionProvider();
14
14
  const connectedPeers = cojsonInternals.connectedPeers;
15
15
 
16
16
  describe("CoPlainText", () => {
@@ -15,7 +15,7 @@ import {
15
15
  createJazzContext,
16
16
  createJazzContextForNewAccount,
17
17
  createJazzContextFromExistingCredentials,
18
- randomSessionProvider,
18
+ MockSessionProvider,
19
19
  } from "../exports";
20
20
  import { activeAccountContext } from "../implementation/activeAccountContext";
21
21
  import {
@@ -26,6 +26,7 @@ import {
26
26
  import { assertLoaded, loadCoValueOrFail } from "./utils";
27
27
  const Crypto = await WasmCrypto.create();
28
28
 
29
+ let randomSessionProvider = new MockSessionProvider();
29
30
  KvStoreContext.getInstance().initialize(new InMemoryKVStore());
30
31
 
31
32
  describe("createContext methods", () => {
@@ -35,6 +36,9 @@ describe("createContext methods", () => {
35
36
  authSecretStorage = new AuthSecretStorage();
36
37
  authSecretStorage.clear();
37
38
  await setupJazzTestSync();
39
+ randomSessionProvider = new MockSessionProvider();
40
+ vi.spyOn(randomSessionProvider, "acquireSession");
41
+ vi.spyOn(randomSessionProvider, "persistSession");
38
42
  });
39
43
 
40
44
  describe("createJazzContextFromExistingCredentials", () => {
@@ -49,6 +53,13 @@ describe("createContext methods", () => {
49
53
  secret: account.$jazz.localNode.getCurrentAgent().agentSecret,
50
54
  };
51
55
 
56
+ const sessionID = Crypto.newRandomSessionID(account.$jazz.raw.id);
57
+
58
+ vi.spyOn(randomSessionProvider, "acquireSession").mockResolvedValue({
59
+ sessionID: sessionID,
60
+ sessionDone: () => {},
61
+ });
62
+
52
63
  const context = await createJazzContextFromExistingCredentials({
53
64
  credentials,
54
65
  peers: [getPeerConnectedToTestSyncServer()],
@@ -60,10 +71,47 @@ describe("createContext methods", () => {
60
71
  expect(context.node).toBeDefined();
61
72
  expect(context.account).toBeDefined();
62
73
  expect(context.account.$jazz.id).toBe(credentials.accountID);
74
+ expect(context.node.currentSessionID).toBe(sessionID);
63
75
  expect(typeof context.done).toBe("function");
64
76
  expect(typeof context.logOut).toBe("function");
65
77
  });
66
78
 
79
+ test("releasese the session when the context is done", async () => {
80
+ const spy = vi.fn();
81
+
82
+ // Create an account first to get valid credentials
83
+ const account = await createJazzTestAccount({
84
+ isCurrentActiveAccount: true,
85
+ });
86
+
87
+ const credentials: Credentials = {
88
+ accountID: account.$jazz.id,
89
+ secret: account.$jazz.localNode.getCurrentAgent().agentSecret,
90
+ };
91
+
92
+ const sessionID = Crypto.newRandomSessionID(account.$jazz.raw.id);
93
+
94
+ vi.spyOn(randomSessionProvider, "acquireSession").mockResolvedValue({
95
+ sessionID: sessionID,
96
+ sessionDone: spy,
97
+ });
98
+
99
+ const context = await createJazzContextFromExistingCredentials({
100
+ credentials,
101
+ peers: [getPeerConnectedToTestSyncServer()],
102
+ crypto: Crypto,
103
+ sessionProvider: randomSessionProvider,
104
+ asActiveAccount: true,
105
+ });
106
+
107
+ expect(randomSessionProvider.acquireSession).toHaveBeenCalled();
108
+ expect(spy).not.toHaveBeenCalled();
109
+
110
+ context.done();
111
+
112
+ expect(spy).toHaveBeenCalled();
113
+ });
114
+
67
115
  test("handles custom account schema", async () => {
68
116
  const CustomAccount = co
69
117
  .account({
@@ -187,6 +235,7 @@ describe("createContext methods", () => {
187
235
  creationProps: { name: "New User" },
188
236
  peers: [],
189
237
  crypto: Crypto,
238
+ sessionProvider: randomSessionProvider,
190
239
  });
191
240
 
192
241
  expect(context.account).toBeDefined();
@@ -202,11 +251,38 @@ describe("createContext methods", () => {
202
251
  initialAgentSecret: initialSecret,
203
252
  peers: [],
204
253
  crypto: Crypto,
254
+ sessionProvider: randomSessionProvider,
205
255
  });
206
256
 
257
+ expect(randomSessionProvider.persistSession).toHaveBeenCalledWith(
258
+ context.account.$jazz.id,
259
+ context.node.currentSessionID,
260
+ );
261
+ expect(randomSessionProvider.acquireSession).not.toHaveBeenCalled();
207
262
  expect(context.node.getCurrentAgent().agentSecret).toBe(initialSecret);
208
263
  });
209
264
 
265
+ test("releasese the session when the context is done", async () => {
266
+ const spy = vi.fn();
267
+ vi.spyOn(randomSessionProvider, "persistSession").mockResolvedValue({
268
+ sessionDone: spy,
269
+ });
270
+
271
+ const context = await createJazzContextForNewAccount({
272
+ creationProps: { name: "New User" },
273
+ peers: [],
274
+ crypto: Crypto,
275
+ sessionProvider: randomSessionProvider,
276
+ });
277
+
278
+ expect(randomSessionProvider.persistSession).toHaveBeenCalled();
279
+ expect(spy).not.toHaveBeenCalled();
280
+
281
+ context.done();
282
+
283
+ expect(spy).toHaveBeenCalled();
284
+ });
285
+
210
286
  test("handles custom account schema", async () => {
211
287
  const CustomAccount = co
212
288
  .account({
@@ -220,6 +296,7 @@ describe("createContext methods", () => {
220
296
  peers: [],
221
297
  crypto: Crypto,
222
298
  AccountSchema: CustomAccount,
299
+ sessionProvider: randomSessionProvider,
223
300
  });
224
301
 
225
302
  expect(context.account).toBeInstanceOf(
@@ -232,6 +309,7 @@ describe("createContext methods", () => {
232
309
  creationProps: { name: "New User" },
233
310
  peers: [],
234
311
  crypto: Crypto,
312
+ sessionProvider: randomSessionProvider,
235
313
  });
236
314
  expect(activeAccountContext.get()).toBe(context.account);
237
315
  });
@@ -23,7 +23,7 @@ import {
23
23
  MaybeLoaded,
24
24
  Settled,
25
25
  co,
26
- randomSessionProvider,
26
+ MockSessionProvider,
27
27
  CoValueLoadingState,
28
28
  CoValueErrorState,
29
29
  } from "../internal.js";
@@ -33,7 +33,7 @@ import { setCustomErrorReporter } from "../config.js";
33
33
 
34
34
  const Crypto = await WasmCrypto.create();
35
35
  const { connectedPeers } = cojsonInternals;
36
-
36
+ const randomSessionProvider = new MockSessionProvider();
37
37
  const InnermostMap = co.map({
38
38
  value: z.string(),
39
39
  });
@@ -1192,6 +1192,29 @@ test("should not throw when calling ensureLoaded a record with a non-existent ke
1192
1192
  expect(loadedPerson.pet1).toBeUndefined();
1193
1193
  });
1194
1194
 
1195
+ test("should load a record with a non-existent key if there's a catch block", async () => {
1196
+ const Person = co.record(
1197
+ z.string(),
1198
+ co.map({
1199
+ name: z.string(),
1200
+ breed: z.string(),
1201
+ }),
1202
+ );
1203
+
1204
+ const person = Person.create({});
1205
+
1206
+ const loadedPerson = await Person.load(person.$jazz.id, {
1207
+ resolve: {
1208
+ pet1: {
1209
+ $onError: "catch",
1210
+ },
1211
+ },
1212
+ });
1213
+
1214
+ assertLoaded(loadedPerson);
1215
+ expect(loadedPerson.pet1).toBeUndefined();
1216
+ });
1217
+
1195
1218
  // This was a regression that ocurred when we migrated `DeeplyLoaded` to use explicit loading states.
1196
1219
  // Keeping this test to prevent it from happening again.
1197
1220
  test("deep loaded CoList nested inside another CoValue can be iterated over", async () => {
@@ -796,4 +796,14 @@ describe("Schema.resolved()", () => {
796
796
  expect(loadedPerson.friends[0]?.friends.$isLoaded).toBe(false);
797
797
  });
798
798
  });
799
+
800
+ test("resolved() does not override previous schema configuration", () => {
801
+ const TestMap = co
802
+ .map({ name: co.plainText() })
803
+ .withPermissions({ onInlineCreate: "sameAsContainer" });
804
+ const TestMapWithName = TestMap.resolved({ name: true });
805
+ expect(TestMapWithName.permissions).toEqual({
806
+ onInlineCreate: "sameAsContainer",
807
+ });
808
+ });
799
809
  });