jazz-tools 0.19.3 → 0.19.5

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 (75) hide show
  1. package/.svelte-kit/__package__/jazz.class.svelte.d.ts +2 -2
  2. package/.svelte-kit/__package__/jazz.class.svelte.d.ts.map +1 -1
  3. package/.svelte-kit/__package__/jazz.class.svelte.js +15 -17
  4. package/.turbo/turbo-build.log +65 -65
  5. package/CHANGELOG.md +23 -0
  6. package/dist/{chunk-JPWM4CS2.js → chunk-DFFRRRRF.js} +137 -77
  7. package/dist/chunk-DFFRRRRF.js.map +1 -0
  8. package/dist/index.js +14 -7
  9. package/dist/index.js.map +1 -1
  10. package/dist/inspector/{custom-element-3JAYHXWQ.js → custom-element-P76EIWEV.js} +301 -142
  11. package/dist/inspector/{custom-element-3JAYHXWQ.js.map → custom-element-P76EIWEV.js.map} +1 -1
  12. package/dist/inspector/index.js +281 -122
  13. package/dist/inspector/index.js.map +1 -1
  14. package/dist/inspector/register-custom-element.js +1 -1
  15. package/dist/inspector/tests/viewer/co-plain-text-view.test.d.ts +2 -0
  16. package/dist/inspector/tests/viewer/co-plain-text-view.test.d.ts.map +1 -0
  17. package/dist/inspector/utils/history.d.ts +5 -1
  18. package/dist/inspector/utils/history.d.ts.map +1 -1
  19. package/dist/inspector/viewer/co-plain-text-view.d.ts +4 -2
  20. package/dist/inspector/viewer/co-plain-text-view.d.ts.map +1 -1
  21. package/dist/inspector/viewer/page.d.ts.map +1 -1
  22. package/dist/inspector/viewer/use-resolve-covalue.d.ts +0 -1
  23. package/dist/inspector/viewer/use-resolve-covalue.d.ts.map +1 -1
  24. package/dist/react-core/hooks.d.ts.map +1 -1
  25. package/dist/react-core/index.js +4 -17
  26. package/dist/react-core/index.js.map +1 -1
  27. package/dist/svelte/jazz.class.svelte.d.ts +2 -2
  28. package/dist/svelte/jazz.class.svelte.d.ts.map +1 -1
  29. package/dist/svelte/jazz.class.svelte.js +15 -17
  30. package/dist/testing.js +1 -1
  31. package/dist/tools/coValues/coFeed.d.ts.map +1 -1
  32. package/dist/tools/coValues/group.d.ts.map +1 -1
  33. package/dist/tools/coValues/interfaces.d.ts +7 -6
  34. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  35. package/dist/tools/coValues/request.d.ts.map +1 -1
  36. package/dist/tools/exports.d.ts +1 -1
  37. package/dist/tools/exports.d.ts.map +1 -1
  38. package/dist/tools/implementation/refs.d.ts +1 -1
  39. package/dist/tools/implementation/refs.d.ts.map +1 -1
  40. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts +3 -1
  41. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts.map +1 -1
  42. package/dist/tools/subscribe/SubscriptionScope.d.ts +5 -2
  43. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  44. package/dist/tools/subscribe/index.d.ts +1 -1
  45. package/dist/tools/subscribe/index.d.ts.map +1 -1
  46. package/dist/tools/subscribe/types.d.ts +2 -1
  47. package/dist/tools/subscribe/types.d.ts.map +1 -1
  48. package/dist/tools/tests/SubscriptionScope.test.d.ts +2 -0
  49. package/dist/tools/tests/SubscriptionScope.test.d.ts.map +1 -0
  50. package/package.json +4 -4
  51. package/src/inspector/tests/utils/history.test.ts +233 -2
  52. package/src/inspector/tests/viewer/co-plain-text-view.test.tsx +125 -0
  53. package/src/inspector/tests/viewer/history-view.test.tsx +134 -2
  54. package/src/inspector/utils/history.ts +168 -1
  55. package/src/inspector/viewer/co-plain-text-view.tsx +102 -3
  56. package/src/inspector/viewer/history-view.tsx +5 -25
  57. package/src/inspector/viewer/page.tsx +8 -1
  58. package/src/inspector/viewer/use-resolve-covalue.ts +2 -6
  59. package/src/react-core/hooks.ts +5 -29
  60. package/src/svelte/jazz.class.svelte.ts +16 -34
  61. package/src/tools/coValues/coFeed.ts +10 -7
  62. package/src/tools/coValues/coMap.ts +10 -7
  63. package/src/tools/coValues/group.ts +6 -2
  64. package/src/tools/coValues/interfaces.ts +48 -28
  65. package/src/tools/coValues/request.ts +12 -8
  66. package/src/tools/exports.ts +1 -0
  67. package/src/tools/implementation/refs.ts +9 -17
  68. package/src/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.ts +62 -30
  69. package/src/tools/subscribe/SubscriptionScope.ts +38 -2
  70. package/src/tools/subscribe/index.ts +28 -13
  71. package/src/tools/subscribe/types.ts +5 -2
  72. package/src/tools/tests/SubscriptionScope.test.ts +397 -0
  73. package/src/tools/tests/deepLoading.test.ts +22 -0
  74. package/src/tools/tests/subscribe.test.ts +69 -0
  75. package/dist/chunk-JPWM4CS2.js.map +0 -1
@@ -17,8 +17,8 @@ import type {
17
17
  import {
18
18
  coValueClassFromCoValueClassOrSchema,
19
19
  subscribeToCoValue,
20
- createUnloadedCoValue,
21
20
  CoValueLoadingState,
21
+ getUnloadedCoValueWithoutId,
22
22
  } from "jazz-tools";
23
23
  import { untrack } from "svelte";
24
24
  import { createSubscriber } from "svelte/reactivity";
@@ -55,11 +55,9 @@ export class CoState<
55
55
  // @ts-expect-error we can't statically enforce the schema's resolve query is a valid resolve query, but in practice it is
56
56
  R extends ResolveQuery<V> = SchemaResolveQuery<V>,
57
57
  > {
58
- #value: MaybeLoaded<Loaded<V, R>> = createUnloadedCoValue(
59
- "",
58
+ #value: MaybeLoaded<Loaded<V, R>> = getUnloadedCoValueWithoutId(
60
59
  CoValueLoadingState.LOADING,
61
60
  );
62
- #previousValue: MaybeLoaded<CoValue> | undefined = undefined;
63
61
  #ctx = getJazzContext<InstanceOfSchema<AccountClass<Account>>>();
64
62
  #id: CoStateId;
65
63
  #subscribe: () => void;
@@ -86,9 +84,9 @@ export class CoState<
86
84
  const options = this.#options;
87
85
 
88
86
  return untrack(() => {
89
- if (!ctx || !id) {
87
+ if (!id) {
90
88
  return this.update(
91
- createUnloadedCoValue(id ?? "", CoValueLoadingState.UNAVAILABLE),
89
+ getUnloadedCoValueWithoutId(CoValueLoadingState.UNAVAILABLE),
92
90
  );
93
91
  }
94
92
  const agent = "me" in ctx ? ctx.me : ctx.guest;
@@ -101,15 +99,11 @@ export class CoState<
101
99
  // @ts-expect-error The resolve query type isn't compatible with the coValueClassFromCoValueClassOrSchema conversion
102
100
  resolve,
103
101
  loadAs: agent,
104
- onUnavailable: () => {
105
- this.update(
106
- createUnloadedCoValue(id, CoValueLoadingState.UNAVAILABLE),
107
- );
102
+ onUnavailable: (value) => {
103
+ this.update(value);
108
104
  },
109
- onUnauthorized: () => {
110
- this.update(
111
- createUnloadedCoValue(id, CoValueLoadingState.UNAUTHORIZED),
112
- );
105
+ onUnauthorized: (value) => {
106
+ this.update(value);
113
107
  },
114
108
  syncResolution: true,
115
109
  unstable_branch: options?.unstable_branch,
@@ -130,7 +124,6 @@ export class CoState<
130
124
  if (shouldSkipUpdate(value, this.#value)) {
131
125
  return;
132
126
  }
133
- this.#previousValue = value;
134
127
  this.#value = value;
135
128
  this.#update();
136
129
  }
@@ -148,11 +141,10 @@ export class AccountCoState<
148
141
  // @ts-expect-error we can't statically enforce the schema's resolve query is a valid resolve query, but in practice it is
149
142
  R extends ResolveQuery<A> = SchemaResolveQuery<A>,
150
143
  > {
151
- #value: MaybeLoaded<Loaded<A, R>> = createUnloadedCoValue(
152
- "",
144
+ #value: MaybeLoaded<Loaded<A, R>> = getUnloadedCoValueWithoutId(
153
145
  CoValueLoadingState.LOADING,
154
146
  );
155
- #ctx = getJazzContext<InstanceOfSchema<A>>();
147
+ #ctx = getJazzContext<InstanceOfSchema<AccountClass<Account>>>();
156
148
  #subscribe: () => void;
157
149
  #options: CoStateOptions<A, R> | undefined;
158
150
  #update = () => {};
@@ -174,9 +166,9 @@ export class AccountCoState<
174
166
  const options = this.#options;
175
167
 
176
168
  return untrack(() => {
177
- if (!ctx || !("me" in ctx)) {
169
+ if (!("me" in ctx)) {
178
170
  return this.update(
179
- createUnloadedCoValue("", CoValueLoadingState.UNAVAILABLE),
171
+ getUnloadedCoValueWithoutId(CoValueLoadingState.UNAVAILABLE),
180
172
  );
181
173
  }
182
174
 
@@ -189,21 +181,11 @@ export class AccountCoState<
189
181
  {
190
182
  resolve,
191
183
  loadAs: me,
192
- onUnavailable: () => {
193
- this.update(
194
- createUnloadedCoValue(
195
- me.$jazz.id,
196
- CoValueLoadingState.UNAVAILABLE,
197
- ),
198
- );
184
+ onUnavailable: (value) => {
185
+ this.update(value);
199
186
  },
200
- onUnauthorized: () => {
201
- this.update(
202
- createUnloadedCoValue(
203
- me.$jazz.id,
204
- CoValueLoadingState.UNAUTHORIZED,
205
- ),
206
- );
187
+ onUnauthorized: (value) => {
188
+ this.update(value);
207
189
  },
208
190
  syncResolution: true,
209
191
  unstable_branch: options?.unstable_branch,
@@ -512,13 +512,16 @@ function entryFromRawEntry<Item>(
512
512
  }
513
513
  },
514
514
  get by() {
515
- return (
516
- accountID &&
517
- accessChildById(accessFrom, accountID, {
518
- ref: Account,
519
- optional: false,
520
- })
521
- );
515
+ if (!accountID) return null;
516
+
517
+ const account = accessChildById(accessFrom, accountID, {
518
+ ref: Account,
519
+ optional: false,
520
+ }) as Account;
521
+
522
+ if (!account.$isLoaded) return null;
523
+
524
+ return account;
522
525
  },
523
526
  madeAt: rawEntry.at,
524
527
  tx: rawEntry.tx,
@@ -1051,13 +1051,16 @@ function getEditFromRaw(
1051
1051
  )
1052
1052
  : undefined,
1053
1053
  get by() {
1054
- return (
1055
- rawEdit.by &&
1056
- accessChildById(target, rawEdit.by, {
1057
- ref: Account,
1058
- optional: false,
1059
- })
1060
- );
1054
+ if (!rawEdit.by) return null;
1055
+
1056
+ const account = accessChildById(target, rawEdit.by, {
1057
+ ref: Account,
1058
+ optional: false,
1059
+ }) as Account;
1060
+
1061
+ if (!account.$isLoaded) return null;
1062
+
1063
+ return account;
1061
1064
  },
1062
1065
  madeAt: rawEdit.at,
1063
1066
  key,
@@ -178,7 +178,11 @@ export class Group extends CoValueBase implements CoValue {
178
178
  ref,
179
179
  get account() {
180
180
  // Accounts values are non-nullable because are loaded as dependencies
181
- return accessChildById(group, accountID, refEncodedAccountSchema);
181
+ return accessChildById(
182
+ group,
183
+ accountID,
184
+ refEncodedAccountSchema,
185
+ ) as Account;
182
186
  },
183
187
  });
184
188
  }
@@ -410,7 +414,7 @@ export function getCoValueOwner(coValue: CoValue): Group {
410
414
  ref: RegisteredSchemas["Group"],
411
415
  optional: false,
412
416
  });
413
- if (!group) {
417
+ if (!group.$isLoaded) {
414
418
  throw new Error("CoValue has no owner");
415
419
  }
416
420
  return group;
@@ -107,6 +107,23 @@ export function isCoValueClass<V extends CoValue>(
107
107
  */
108
108
  export type ID<T> = string;
109
109
 
110
+ const unloadedCoValueStates = new Map<
111
+ NotLoadedCoValueState,
112
+ NotLoaded<CoValue>
113
+ >();
114
+
115
+ export function getUnloadedCoValueWithoutId<T extends CoValue>(
116
+ loadingState: NotLoadedCoValueState,
117
+ ): NotLoaded<T> {
118
+ const value = unloadedCoValueStates.get(loadingState);
119
+ if (value) {
120
+ return value;
121
+ }
122
+ const newValue = createUnloadedCoValue("", loadingState);
123
+ unloadedCoValueStates.set(loadingState, newValue);
124
+ return newValue;
125
+ }
126
+
110
127
  export function createUnloadedCoValue<T extends CoValue>(
111
128
  id: ID<T>,
112
129
  loadingState: NotLoadedCoValueState,
@@ -159,12 +176,8 @@ export function loadCoValue<
159
176
  loadAs: options.loadAs,
160
177
  syncResolution: true,
161
178
  skipRetry: options.skipRetry,
162
- onUnavailable: () => {
163
- resolve(createUnloadedCoValue(id, CoValueLoadingState.UNAVAILABLE));
164
- },
165
- onUnauthorized: () => {
166
- resolve(createUnloadedCoValue(id, CoValueLoadingState.UNAUTHORIZED));
167
- },
179
+ onUnavailable: resolve,
180
+ onUnauthorized: resolve,
168
181
  unstable_branch: options.unstable_branch,
169
182
  },
170
183
  (value, unsubscribe) => {
@@ -215,8 +228,8 @@ export type SubscribeListenerOptions<
215
228
  > = {
216
229
  resolve?: RefsToResolveStrict<V, R>;
217
230
  loadAs?: Account | AnonymousJazzAgent;
218
- onUnauthorized?: () => void;
219
- onUnavailable?: () => void;
231
+ onUnauthorized?: (value: NotLoaded<V>) => void;
232
+ onUnavailable?: (value: NotLoaded<V>) => void;
220
233
  unstable_branch?: BranchDefinition;
221
234
  };
222
235
 
@@ -290,8 +303,8 @@ export function subscribeToCoValue<
290
303
  options: {
291
304
  resolve?: RefsToResolveStrict<V, R>;
292
305
  loadAs: Account | AnonymousJazzAgent;
293
- onUnavailable?: () => void;
294
- onUnauthorized?: () => void;
306
+ onUnavailable?: (value: NotLoaded<V>) => void;
307
+ onUnauthorized?: (value: NotLoaded<V>) => void;
295
308
  syncResolution?: boolean;
296
309
  skipRetry?: boolean;
297
310
  unstable_branch?: BranchDefinition;
@@ -318,35 +331,42 @@ export function subscribeToCoValue<
318
331
  options.unstable_branch,
319
332
  );
320
333
 
321
- const handleUpdate = (value: SubscriptionValue<V, any>) => {
334
+ const handleUpdate = () => {
322
335
  if (unsubscribed) return;
323
336
 
324
- if (value.type === CoValueLoadingState.UNAVAILABLE) {
325
- options.onUnavailable?.();
337
+ const value = rootNode.getCurrentValue();
326
338
 
327
- // Don't log unavailable errors when `loadUnique` or `upsertUnique` are used
328
- if (!options.skipRetry) {
329
- console.error(value.toString());
330
- }
331
- } else if (value.type === CoValueLoadingState.UNAUTHORIZED) {
332
- options.onUnauthorized?.();
339
+ if (value.$isLoaded) {
340
+ listener(value as Resolved<V, R>, unsubscribe);
341
+ return;
342
+ }
333
343
 
334
- console.error(value.toString());
335
- } else if (value.type === CoValueLoadingState.LOADED) {
336
- listener(value.value as Resolved<V, R>, unsubscribe);
344
+ switch (value.$jazz.loadingState) {
345
+ case CoValueLoadingState.UNAVAILABLE:
346
+ options.onUnavailable?.(value);
347
+
348
+ // Don't log unavailable errors when `loadUnique` or `upsertUnique` are used
349
+ if (!options.skipRetry) {
350
+ console.error(value.toString());
351
+ }
352
+ break;
353
+ case CoValueLoadingState.UNAUTHORIZED:
354
+ options.onUnauthorized?.(value);
355
+ console.error(value.toString());
356
+ break;
337
357
  }
338
358
  };
339
359
 
340
360
  let shouldDefer = !options.syncResolution;
341
361
 
342
- rootNode.setListener((value) => {
362
+ rootNode.setListener(() => {
343
363
  if (shouldDefer) {
344
364
  shouldDefer = false;
345
365
  Promise.resolve().then(() => {
346
- handleUpdate(value);
366
+ handleUpdate();
347
367
  });
348
368
  } else {
349
- handleUpdate(value);
369
+ handleUpdate();
350
370
  }
351
371
  });
352
372
 
@@ -366,8 +386,8 @@ export function subscribeToExistingCoValue<
366
386
  options:
367
387
  | {
368
388
  resolve?: RefsToResolveStrict<V, R>;
369
- onUnavailable?: () => void;
370
- onUnauthorized?: () => void;
389
+ onUnavailable?: (value: NotLoaded<V>) => void;
390
+ onUnauthorized?: (value: NotLoaded<V>) => void;
371
391
  unstable_branch?: BranchDefinition;
372
392
  }
373
393
  | undefined,
@@ -703,7 +723,7 @@ function loadContentPiecesFromSubscription(
703
723
 
704
724
  const currentValue = subscription.getCurrentValue();
705
725
 
706
- if (typeof currentValue !== "string") {
726
+ if (currentValue.$isLoaded) {
707
727
  const core = currentValue.$jazz.raw.core as AvailableCoValueCore;
708
728
  loadContentPiecesFromCoValue(core, valuesExported, contentPieces);
709
729
  }
@@ -14,8 +14,6 @@ import {
14
14
  CoMapSchemaInit,
15
15
  CoValueClass,
16
16
  CoreCoMapSchema,
17
- CoValueLoadingState,
18
- createUnloadedCoValue,
19
17
  Group,
20
18
  Loaded,
21
19
  ResolveQuery,
@@ -377,9 +375,6 @@ export class HttpRoute<
377
375
  const as = options?.owner ?? Account.getMe();
378
376
 
379
377
  const target = await loadWorkerAccountOrGroup(this.workerId, as);
380
- if (!target.$isLoaded) {
381
- throw new JazzRequestError("Worker account not found", 400);
382
- }
383
378
 
384
379
  const response = await fetch(this.url, {
385
380
  method: "POST",
@@ -621,20 +616,29 @@ async function loadWorkerAccountOrGroup(id: string, loadAs: Account) {
621
616
  const coValue = await node.loadCoValueCore(id as `co_z${string}`);
622
617
 
623
618
  if (!coValue.isAvailable()) {
624
- return createUnloadedCoValue(id, CoValueLoadingState.UNAVAILABLE);
619
+ throw new JazzRequestError("Worker account not found", 400);
625
620
  }
626
621
 
627
622
  const content = coValue.getCurrentContent();
628
623
 
629
624
  if (content instanceof RawAccount) {
630
- return Account.load(content.id, {
625
+ const account = await Account.load(content.id, {
631
626
  loadAs,
632
627
  });
628
+ if (!account.$isLoaded) {
629
+ throw new JazzRequestError("Worker account not found", 400);
630
+ }
631
+ return account;
633
632
  }
634
633
 
635
- return Group.load(content.id, {
634
+ const group = await Group.load(content.id, {
636
635
  loadAs,
637
636
  });
637
+
638
+ if (!group.$isLoaded) {
639
+ throw new JazzRequestError("Worker group not found", 400);
640
+ }
641
+ return group;
638
642
  }
639
643
 
640
644
  function defaultGetToken(request: Request) {
@@ -64,6 +64,7 @@ export {
64
64
  Ref,
65
65
  createUnloadedCoValue,
66
66
  unstable_loadUnique,
67
+ getUnloadedCoValueWithoutId,
67
68
  } from "./internal.js";
68
69
 
69
70
  export {
@@ -53,29 +53,21 @@ export class Ref<out V extends CoValue> {
53
53
  }
54
54
 
55
55
  if (!node) {
56
- return createUnloadedCoValue(this.id, CoValueLoadingState.LOADING);
56
+ return createUnloadedCoValue(this.id, CoValueLoadingState.UNAVAILABLE);
57
57
  }
58
58
 
59
- const value = node.value;
59
+ const value = node.getCurrentValue();
60
60
 
61
- if (value?.type === CoValueLoadingState.LOADED) {
62
- return value.value as V;
61
+ if (value.$isLoaded) {
62
+ return value as V;
63
63
  } else {
64
64
  return new Promise((resolve) => {
65
65
  const unsubscribe = node.subscribe((value) => {
66
- if (value?.type === CoValueLoadingState.LOADED) {
67
- unsubscribe();
68
- resolve(value.value as V);
69
- } else if (value?.type === CoValueLoadingState.UNAVAILABLE) {
70
- unsubscribe();
71
- resolve(
72
- createUnloadedCoValue(this.id, CoValueLoadingState.UNAVAILABLE),
73
- );
74
- } else if (value?.type === CoValueLoadingState.UNAUTHORIZED) {
66
+ const currentValue = node.getCurrentValue();
67
+
68
+ if (currentValue.$jazz.loadingState !== CoValueLoadingState.LOADING) {
75
69
  unsubscribe();
76
- resolve(
77
- createUnloadedCoValue(this.id, CoValueLoadingState.UNAUTHORIZED),
78
- );
70
+ resolve(currentValue as V);
79
71
  }
80
72
 
81
73
  if (subscriptionScope.closed) {
@@ -86,7 +78,7 @@ export class Ref<out V extends CoValue> {
86
78
  }
87
79
  }
88
80
 
89
- get value(): V | null | undefined {
81
+ get value(): MaybeLoaded<V> {
90
82
  return accessChildById(this.parent, this.id, this.schema);
91
83
  }
92
84
  }
@@ -60,16 +60,33 @@ function makeCodecCoField(
60
60
  });
61
61
  }
62
62
 
63
- export function schemaFieldToCoFieldDef(schema: SchemaField) {
63
+ // CoFieldDefs are inherently type-unsafe. This type exists only for documentation purposes.
64
+ type CoFieldDef = any;
65
+ const schemaFieldCache = new WeakMap<SchemaField, CoFieldDef>();
66
+
67
+ function cacheSchemaField(schema: SchemaField, value: CoFieldDef): CoFieldDef {
68
+ schemaFieldCache.set(schema, value);
69
+ return value;
70
+ }
71
+
72
+ export function schemaFieldToCoFieldDef(schema: SchemaField): CoFieldDef {
73
+ const cachedCoFieldDef = schemaFieldCache.get(schema);
74
+ if (cachedCoFieldDef !== undefined) {
75
+ return cachedCoFieldDef;
76
+ }
77
+
64
78
  if (isCoValueClass(schema)) {
65
- return coField.ref(schema);
79
+ return cacheSchemaField(schema, coField.ref(schema));
66
80
  } else if (isCoValueSchema(schema)) {
67
81
  if (schema.builtin === "CoOptional") {
68
- return coField.ref(schema.getCoValueClass(), {
69
- optional: true,
70
- });
82
+ return cacheSchemaField(
83
+ schema,
84
+ coField.ref(schema.getCoValueClass(), {
85
+ optional: true,
86
+ }),
87
+ );
71
88
  }
72
- return coField.ref(schema.getCoValueClass());
89
+ return cacheSchemaField(schema, coField.ref(schema.getCoValueClass()));
73
90
  } else {
74
91
  if ("_zod" in schema) {
75
92
  const zodSchemaDef = schema._zod.def;
@@ -90,29 +107,35 @@ export function schemaFieldToCoFieldDef(schema: SchemaField) {
90
107
  // Primitive coField types support null and undefined as values,
91
108
  // so we can just return the inner type here and rely on support
92
109
  // for null/undefined at the type level
93
- return coFieldDef;
110
+ return cacheSchemaField(schema, coFieldDef);
94
111
  } else if (zodSchemaDef.type === "string") {
95
- return coField.string;
112
+ return cacheSchemaField(schema, coField.string);
96
113
  } else if (zodSchemaDef.type === "number") {
97
- return coField.number;
114
+ return cacheSchemaField(schema, coField.number);
98
115
  } else if (zodSchemaDef.type === "boolean") {
99
- return coField.boolean;
116
+ return cacheSchemaField(schema, coField.boolean);
100
117
  } else if (zodSchemaDef.type === "null") {
101
- return coField.null;
118
+ return cacheSchemaField(schema, coField.null);
102
119
  } else if (zodSchemaDef.type === "enum") {
103
- return coField.string;
120
+ return cacheSchemaField(schema, coField.string);
104
121
  } else if (zodSchemaDef.type === "readonly") {
105
- return schemaFieldToCoFieldDef(
106
- (schema as unknown as ZodReadonly).def.innerType as SchemaField,
122
+ return cacheSchemaField(
123
+ schema,
124
+ schemaFieldToCoFieldDef(
125
+ (schema as unknown as ZodReadonly).def.innerType as SchemaField,
126
+ ),
107
127
  );
108
128
  } else if (zodSchemaDef.type === "date") {
109
- return coField.optional.Date;
129
+ return cacheSchemaField(schema, coField.optional.Date);
110
130
  } else if (zodSchemaDef.type === "template_literal") {
111
- return coField.string;
131
+ return cacheSchemaField(schema, coField.string);
112
132
  } else if (zodSchemaDef.type === "lazy") {
113
133
  // Mostly to support z.json()
114
- return schemaFieldToCoFieldDef(
115
- (schema as unknown as ZodLazy).unwrap() as SchemaField,
134
+ return cacheSchemaField(
135
+ schema,
136
+ schemaFieldToCoFieldDef(
137
+ (schema as unknown as ZodLazy).unwrap() as SchemaField,
138
+ ),
116
139
  );
117
140
  } else if (
118
141
  zodSchemaDef.type === "default" ||
@@ -122,9 +145,12 @@ export function schemaFieldToCoFieldDef(schema: SchemaField) {
122
145
  "z.default()/z.catch() are not supported in collaborative schemas. They will be ignored.",
123
146
  );
124
147
 
125
- return schemaFieldToCoFieldDef(
126
- (schema as unknown as ZodDefault | ZodCatch).def
127
- .innerType as SchemaField,
148
+ return cacheSchemaField(
149
+ schema,
150
+ schemaFieldToCoFieldDef(
151
+ (schema as unknown as ZodDefault | ZodCatch).def
152
+ .innerType as SchemaField,
153
+ ),
128
154
  );
129
155
  } else if (zodSchemaDef.type === "literal") {
130
156
  if (
@@ -140,11 +166,14 @@ export function schemaFieldToCoFieldDef(schema: SchemaField) {
140
166
  ) {
141
167
  throw new Error("z.literal() with bigint is not supported");
142
168
  }
143
- return coField.literal(
144
- ...(zodSchemaDef.values as Exclude<
145
- (typeof zodSchemaDef.values)[number],
146
- undefined | null | bigint
147
- >[]),
169
+ return cacheSchemaField(
170
+ schema,
171
+ coField.literal(
172
+ ...(zodSchemaDef.values as Exclude<
173
+ (typeof zodSchemaDef.values)[number],
174
+ undefined | null | bigint
175
+ >[]),
176
+ ),
148
177
  );
149
178
  } else if (
150
179
  zodSchemaDef.type === "object" ||
@@ -153,10 +182,10 @@ export function schemaFieldToCoFieldDef(schema: SchemaField) {
153
182
  zodSchemaDef.type === "tuple" ||
154
183
  zodSchemaDef.type === "intersection"
155
184
  ) {
156
- return coField.json();
185
+ return cacheSchemaField(schema, coField.json());
157
186
  } else if (zodSchemaDef.type === "union") {
158
187
  if (isUnionOfPrimitivesDeeply(schema)) {
159
- return coField.json();
188
+ return cacheSchemaField(schema, coField.json());
160
189
  } else {
161
190
  throw new Error(
162
191
  "z.union()/z.discriminatedUnion() of collaborative types is not supported. Use co.discriminatedUnion() instead.",
@@ -183,8 +212,11 @@ export function schemaFieldToCoFieldDef(schema: SchemaField) {
183
212
  throw error;
184
213
  }
185
214
 
186
- return makeCodecCoField(
187
- schema as z.core.$ZodCodec<z.core.$ZodType, z.core.$ZodType>,
215
+ return cacheSchemaField(
216
+ schema,
217
+ makeCodecCoField(
218
+ schema as z.core.$ZodCodec<z.core.$ZodType, z.core.$ZodType>,
219
+ ),
188
220
  );
189
221
  } else {
190
222
  throw new Error(
@@ -6,9 +6,11 @@ import {
6
6
  type CoValue,
7
7
  type ID,
8
8
  MaybeLoaded,
9
+ NotLoaded,
9
10
  type RefEncoded,
10
11
  type RefsToResolve,
11
12
  TypeSym,
13
+ createUnloadedCoValue,
12
14
  instantiateRefEncodedFromRaw,
13
15
  isRefEncoded,
14
16
  } from "../internal.js";
@@ -283,7 +285,35 @@ export class SubscriptionScope<D extends CoValue> {
283
285
  return this.pendingLoadedChildren.size === 0;
284
286
  }
285
287
 
286
- getCurrentValue(): D | NotLoadedCoValueState {
288
+ unloadedValue: NotLoaded<D> | undefined;
289
+
290
+ private getUnloadedValue(reason: NotLoadedCoValueState): NotLoaded<D> {
291
+ if (this.unloadedValue?.$jazz.loadingState === reason) {
292
+ return this.unloadedValue;
293
+ }
294
+
295
+ const unloadedValue: NotLoaded<D> = createUnloadedCoValue(this.id, reason);
296
+
297
+ this.unloadedValue = unloadedValue;
298
+
299
+ return unloadedValue;
300
+ }
301
+
302
+ getCurrentValue(): MaybeLoaded<D> {
303
+ const rawValue = this.getCurrentRawValue();
304
+
305
+ if (
306
+ rawValue === CoValueLoadingState.UNAUTHORIZED ||
307
+ rawValue === CoValueLoadingState.UNAVAILABLE ||
308
+ rawValue === CoValueLoadingState.LOADING
309
+ ) {
310
+ return this.getUnloadedValue(rawValue);
311
+ }
312
+
313
+ return rawValue;
314
+ }
315
+
316
+ getCurrentRawValue(): D | NotLoadedCoValueState {
287
317
  if (
288
318
  this.value.type === CoValueLoadingState.UNAUTHORIZED ||
289
319
  this.value.type === CoValueLoadingState.UNAVAILABLE
@@ -406,7 +436,7 @@ export class SubscriptionScope<D extends CoValue> {
406
436
  this.subscription.pullValue();
407
437
 
408
438
  // Check if the value is now available
409
- const value = this.getCurrentValue();
439
+ const value = this.getCurrentRawValue();
410
440
 
411
441
  // If the value is available, trigger the listener
412
442
  if (typeof value !== "string") {
@@ -590,6 +620,12 @@ export class SubscriptionScope<D extends CoValue> {
590
620
  return undefined;
591
621
  }
592
622
 
623
+ // Check if $onError: "catch" is specified for this key
624
+ const skipInvalid = typeof depth === "object" && depth.$onError === "catch";
625
+ if (skipInvalid) {
626
+ this.skipInvalidKeys.add(key);
627
+ }
628
+
593
629
  const id = map.$jazz.raw.get(key) as string | undefined;
594
630
  const descriptor = map.$jazz.getDescriptor(key);
595
631