jazz-tools 0.18.28 → 0.18.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/.svelte-kit/__package__/media/image.svelte +7 -4
  2. package/.svelte-kit/__package__/media/image.svelte.d.ts.map +1 -1
  3. package/.svelte-kit/__package__/media/image.types.d.ts +1 -0
  4. package/.svelte-kit/__package__/media/image.types.d.ts.map +1 -1
  5. package/.svelte-kit/__package__/tests/media/image.svelte.test.js +63 -0
  6. package/.turbo/turbo-build.log +60 -60
  7. package/CHANGELOG.md +27 -0
  8. package/dist/better-auth/auth/client.d.ts +1 -1
  9. package/dist/better-auth/auth/server.d.ts +1 -1
  10. package/dist/better-auth/auth/server.d.ts.map +1 -1
  11. package/dist/better-auth/auth/server.js.map +1 -1
  12. package/dist/better-auth/database-adapter/index.d.ts +3 -3
  13. package/dist/better-auth/database-adapter/index.d.ts.map +1 -1
  14. package/dist/better-auth/database-adapter/index.js +6 -2
  15. package/dist/better-auth/database-adapter/index.js.map +1 -1
  16. package/dist/better-auth/database-adapter/utils.d.ts.map +1 -1
  17. package/dist/browser/index.d.ts +2 -1
  18. package/dist/browser/index.d.ts.map +1 -1
  19. package/dist/browser/index.js.map +1 -1
  20. package/dist/{chunk-YOL3XDDW.js → chunk-6BIYT3KH.js} +84 -50
  21. package/dist/chunk-6BIYT3KH.js.map +1 -0
  22. package/dist/index.js +3 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/inspector/{custom-element-A7UAELEG.js → custom-element-RQTLPAPJ.js} +2137 -1848
  25. package/dist/inspector/custom-element-RQTLPAPJ.js.map +1 -0
  26. package/dist/inspector/custom-element.d.ts.map +1 -1
  27. package/dist/inspector/index.d.ts +1 -1
  28. package/dist/inspector/index.d.ts.map +1 -1
  29. package/dist/inspector/index.js +570 -339
  30. package/dist/inspector/index.js.map +1 -1
  31. package/dist/inspector/register-custom-element.js +1 -1
  32. package/dist/inspector/ui/index.d.ts +6 -0
  33. package/dist/inspector/ui/index.d.ts.map +1 -0
  34. package/dist/inspector/viewer/group-view.d.ts +3 -2
  35. package/dist/inspector/viewer/group-view.d.ts.map +1 -1
  36. package/dist/inspector/viewer/page.d.ts.map +1 -1
  37. package/dist/react/index.js +2 -2
  38. package/dist/react/index.js.map +1 -1
  39. package/dist/react/media/image.d.ts +8 -0
  40. package/dist/react/media/image.d.ts.map +1 -1
  41. package/dist/react-native-core/index.js +3 -3
  42. package/dist/react-native-core/index.js.map +1 -1
  43. package/dist/react-native-core/media/image.d.ts +15 -0
  44. package/dist/react-native-core/media/image.d.ts.map +1 -1
  45. package/dist/svelte/media/image.svelte +7 -4
  46. package/dist/svelte/media/image.svelte.d.ts.map +1 -1
  47. package/dist/svelte/media/image.types.d.ts +1 -0
  48. package/dist/svelte/media/image.types.d.ts.map +1 -1
  49. package/dist/svelte/tests/media/image.svelte.test.js +63 -0
  50. package/dist/testing.js +8 -1
  51. package/dist/testing.js.map +1 -1
  52. package/dist/tools/coValues/account.d.ts +1 -0
  53. package/dist/tools/coValues/account.d.ts.map +1 -1
  54. package/dist/tools/coValues/group.d.ts +3 -3
  55. package/dist/tools/coValues/group.d.ts.map +1 -1
  56. package/dist/tools/coValues/interfaces.d.ts +9 -2
  57. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  58. package/dist/tools/exports.d.ts +2 -2
  59. package/dist/tools/exports.d.ts.map +1 -1
  60. package/dist/tools/implementation/invites.d.ts +2 -2
  61. package/dist/tools/implementation/invites.d.ts.map +1 -1
  62. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts +1 -1
  63. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts.map +1 -1
  64. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -1
  65. package/dist/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.d.ts +3 -1
  66. package/dist/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.d.ts.map +1 -1
  67. package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
  68. package/dist/tools/implementation/zodSchema/zodReExport.d.ts +1 -1
  69. package/dist/tools/implementation/zodSchema/zodReExport.d.ts.map +1 -1
  70. package/dist/tools/subscribe/CoValueCoreSubscription.d.ts.map +1 -1
  71. package/dist/tools/subscribe/SubscriptionScope.d.ts +0 -2
  72. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  73. package/dist/tools/testing.d.ts +1 -0
  74. package/dist/tools/testing.d.ts.map +1 -1
  75. package/dist/tools/tests/CoValueCoreSubscription.test.d.ts.map +1 -0
  76. package/package.json +5 -5
  77. package/src/better-auth/auth/server.ts +7 -2
  78. package/src/better-auth/auth/tests/server.test.ts +39 -17
  79. package/src/better-auth/database-adapter/index.ts +8 -5
  80. package/src/better-auth/database-adapter/utils.ts +4 -0
  81. package/src/browser/index.ts +2 -1
  82. package/src/inspector/custom-element.tsx +4 -0
  83. package/src/inspector/index.tsx +0 -2
  84. package/src/inspector/ui/index.ts +5 -0
  85. package/src/inspector/viewer/group-view.tsx +304 -20
  86. package/src/inspector/viewer/new-app.tsx +4 -4
  87. package/src/inspector/viewer/page.tsx +16 -2
  88. package/src/react/media/image.tsx +11 -2
  89. package/src/react/tests/media/image.test.tsx +94 -0
  90. package/src/react-native-core/media/image.tsx +11 -3
  91. package/src/svelte/media/image.svelte +7 -4
  92. package/src/svelte/media/image.types.ts +1 -0
  93. package/src/svelte/tests/media/image.svelte.test.ts +85 -0
  94. package/src/tools/coValues/account.ts +30 -5
  95. package/src/tools/coValues/coList.ts +3 -3
  96. package/src/tools/coValues/coMap.ts +3 -3
  97. package/src/tools/coValues/group.ts +13 -12
  98. package/src/tools/coValues/inbox.ts +5 -5
  99. package/src/tools/coValues/interfaces.ts +49 -18
  100. package/src/tools/exports.ts +1 -1
  101. package/src/tools/implementation/invites.ts +3 -8
  102. package/src/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.ts +5 -1
  103. package/src/tools/implementation/zodSchema/schemaTypes/AccountSchema.ts +2 -0
  104. package/src/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.ts +63 -50
  105. package/src/tools/implementation/zodSchema/zodReExport.ts +2 -2
  106. package/src/tools/subscribe/CoValueCoreSubscription.ts +17 -0
  107. package/src/tools/subscribe/SubscriptionScope.ts +1 -27
  108. package/src/tools/testing.ts +7 -0
  109. package/src/tools/{subscribe → tests}/CoValueCoreSubscription.test.ts +233 -3
  110. package/src/tools/tests/coFeed.branch.test.ts +14 -5
  111. package/src/tools/tests/coMap.test.ts +139 -42
  112. package/src/tools/tests/coMap.unique.test.ts +106 -1
  113. package/src/tools/tests/coOptional.test.ts +9 -1
  114. package/src/tools/tests/groupsAndAccounts.test.ts +156 -1
  115. package/src/tools/tests/load.test.ts +198 -1
  116. package/src/tools/tests/zod.test-d.ts +0 -2
  117. package/src/tools/tests/zod.test.ts +43 -40
  118. package/dist/chunk-YOL3XDDW.js.map +0 -1
  119. package/dist/inspector/custom-element-A7UAELEG.js.map +0 -1
  120. package/dist/tools/subscribe/CoValueCoreSubscription.test.d.ts.map +0 -1
  121. /package/dist/tools/{subscribe → tests}/CoValueCoreSubscription.test.d.ts +0 -0
@@ -104,6 +104,91 @@ describe("Image", async () => {
104
104
  expect(img!.src).toBe(placeholderDataUrl);
105
105
  });
106
106
 
107
+ it("should not override actual placeholders", async () => {
108
+ const placeholderDataUrl =
109
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=";
110
+ const customPlaceholder =
111
+ "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXVzZXItaWNvbiBsdWNpZGUtdXNlciI+PHBhdGggZD0iTTE5IDIxdi0yYTQgNCAwIDAgMC00LTRIOWE0IDQgMCAwIDAtNCA0djIiLz48Y2lyY2xlIGN4PSIxMiIgY3k9IjciIHI9IjQiLz48L3N2Zz4=";
112
+
113
+ const original = FileStream.create({ owner: account });
114
+ original.start({ mimeType: "image/jpeg" });
115
+ // Don't end original, so it has no chunks
116
+
117
+ const im = ImageDefinition.create(
118
+ {
119
+ original,
120
+ originalSize: [100, 100],
121
+ progressive: false,
122
+ placeholderDataURL: placeholderDataUrl,
123
+ },
124
+ {
125
+ owner: account,
126
+ },
127
+ );
128
+
129
+ const { container } = renderWithAccount({
130
+ imageId: im.$jazz.id,
131
+ alt: "test",
132
+ placeholder: customPlaceholder
133
+ });
134
+
135
+ const img = container.querySelector("img");
136
+ expect(img).toBeDefined();
137
+ expect(img!.src).toBe(placeholderDataUrl);
138
+ });
139
+
140
+ it("should show custom placeholder while loading and replace with loaded image", async () => {
141
+ const createObjectURLSpy = vi
142
+ .spyOn(URL, "createObjectURL")
143
+ .mockImplementation((blob) => {
144
+ if (!(blob instanceof Blob)) {
145
+ throw new Error("Blob expected");
146
+ }
147
+ return `blob:test-${blob.size}`;
148
+ });
149
+
150
+ const customPlaceholder =
151
+ "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXVzZXItaWNvbiBsdWNpZGUtdXNlciI+PHBhdGggZD0iTTE5IDIxdi0yYTQgNCAwIDAgMC00LTRIOWE0IDQgMCAwIDAtNCA0djIiLz48Y2lyY2xlIGN4PSIxMiIgY3k9IjciIHI9IjQiLz48L3N2Zz4=";
152
+
153
+ // Create an image with no chunks initially (loading state)
154
+ const original = FileStream.create({ owner: account });
155
+ original.start({ mimeType: "image/jpeg" });
156
+ // Don't end original, so it has no chunks
157
+
158
+ const im = ImageDefinition.create(
159
+ {
160
+ original,
161
+ originalSize: [100, 100],
162
+ progressive: false,
163
+ },
164
+ {
165
+ owner: account,
166
+ },
167
+ );
168
+
169
+ const { container } = renderWithAccount({
170
+ imageId: im.$jazz.id,
171
+ alt: "test-loading-custom-placeholder",
172
+ placeholder: customPlaceholder
173
+ });
174
+ // Initially should show custom placeholder
175
+ let img = container.querySelector("img");
176
+ expect(img).toBeDefined();
177
+ expect(img!.src).toBe(customPlaceholder);
178
+
179
+ // Now add the actual image data
180
+ const imageData = await createDummyFileStream(100, account);
181
+ im.$jazz.set("100x100", imageData);
182
+
183
+ // Wait for the image to load and replace the placeholder
184
+ await waitFor(() => {
185
+ img = container.querySelector("img");
186
+ expect(img!.src).toBe("blob:test-100");
187
+ });
188
+
189
+ expect(createObjectURLSpy).toHaveBeenCalledTimes(1);
190
+ });
191
+
107
192
  it("should render the original image once loaded", async () => {
108
193
  const createObjectURLSpy = vi
109
194
  .spyOn(URL, "createObjectURL")
@@ -12,6 +12,7 @@ import {
12
12
  RawCoValue,
13
13
  SessionID,
14
14
  cojsonInternals,
15
+ isAccountRole,
15
16
  } from "cojson";
16
17
  import {
17
18
  AnonymousJazzAgent,
@@ -166,29 +167,52 @@ export class Account extends CoValueBase implements CoValue {
166
167
  }
167
168
  const role = valueOwner.getRoleOf(this.$jazz.id);
168
169
 
170
+ return isAccountRole(role);
171
+ }
172
+
173
+ canWrite(value: CoValue): boolean {
174
+ const valueOwner = value.$jazz.owner;
175
+ if (!valueOwner) {
176
+ if (value[TypeSym] === "Group") {
177
+ const roleInGroup = (value as Group).getRoleOf(this.$jazz.id);
178
+ return (
179
+ roleInGroup === "admin" ||
180
+ roleInGroup === "manager" ||
181
+ roleInGroup === "writer"
182
+ );
183
+ }
184
+ if (value[TypeSym] === "Account") {
185
+ return value.$jazz.id === this.$jazz.id;
186
+ }
187
+ return false;
188
+ }
189
+ const role = valueOwner.getRoleOf(this.$jazz.id);
190
+
169
191
  return (
170
192
  role === "admin" ||
193
+ role === "manager" ||
171
194
  role === "writer" ||
172
- role === "reader" ||
173
195
  role === "writeOnly"
174
196
  );
175
197
  }
176
198
 
177
- canWrite(value: CoValue): boolean {
199
+ canManage(value: CoValue): boolean {
178
200
  const valueOwner = value.$jazz.owner;
179
201
  if (!valueOwner) {
180
202
  if (value[TypeSym] === "Group") {
181
203
  const roleInGroup = (value as Group).getRoleOf(this.$jazz.id);
182
- return roleInGroup === "admin" || roleInGroup === "writer";
204
+ return roleInGroup === "manager" || roleInGroup === "admin";
183
205
  }
184
206
  if (value[TypeSym] === "Account") {
185
207
  return value.$jazz.id === this.$jazz.id;
186
208
  }
187
209
  return false;
188
210
  }
189
- const role = valueOwner.getRoleOf(this.$jazz.id);
190
211
 
191
- return role === "admin" || role === "writer" || role === "writeOnly";
212
+ return (
213
+ valueOwner.getRoleOf(this.$jazz.id) === "admin" ||
214
+ valueOwner.getRoleOf(this.$jazz.id) === "manager"
215
+ );
192
216
  }
193
217
 
194
218
  canAdmin(value: CoValue): boolean {
@@ -203,6 +227,7 @@ export class Account extends CoValueBase implements CoValue {
203
227
  }
204
228
  return false;
205
229
  }
230
+
206
231
  return valueOwner.getRoleOf(this.$jazz.id) === "admin";
207
232
  }
208
233
 
@@ -22,7 +22,7 @@ import {
22
22
  TypeSym,
23
23
  BranchDefinition,
24
24
  getIdFromHeader,
25
- unstable_loadUnique,
25
+ internalLoadUnique,
26
26
  } from "../internal.js";
27
27
  import {
28
28
  AnonymousJazzAgent,
@@ -380,7 +380,7 @@ export class CoList<out Item = any>
380
380
  options.owner.$jazz.id,
381
381
  );
382
382
 
383
- return unstable_loadUnique(this, {
383
+ return internalLoadUnique(this, {
384
384
  header,
385
385
  owner: options.owner,
386
386
  resolve: options.resolve,
@@ -423,7 +423,7 @@ export class CoList<out Item = any>
423
423
 
424
424
  if (!owner) return owner;
425
425
 
426
- return unstable_loadUnique(this, {
426
+ return internalLoadUnique(this, {
427
427
  header,
428
428
  owner,
429
429
  resolve: options?.resolve,
@@ -29,7 +29,7 @@ import {
29
29
  TypeSym,
30
30
  BranchDefinition,
31
31
  getIdFromHeader,
32
- unstable_loadUnique,
32
+ internalLoadUnique,
33
33
  } from "../internal.js";
34
34
  import {
35
35
  Account,
@@ -499,7 +499,7 @@ export class CoMap extends CoValueBase implements CoValue {
499
499
  options.owner.$jazz.id,
500
500
  );
501
501
 
502
- return unstable_loadUnique(this, {
502
+ return internalLoadUnique(this, {
503
503
  header,
504
504
  owner: options.owner,
505
505
  resolve: options.resolve,
@@ -544,7 +544,7 @@ export class CoMap extends CoValueBase implements CoValue {
544
544
 
545
545
  if (!owner) return owner;
546
546
 
547
- return unstable_loadUnique(this, {
547
+ return internalLoadUnique(this, {
548
548
  header,
549
549
  owner,
550
550
  resolve: options?.resolve,
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  RawAccount,
3
+ isAccountRole,
3
4
  type AccountRole,
4
5
  type AgentID,
5
6
  type Everyone,
@@ -10,7 +11,6 @@ import {
10
11
  } from "cojson";
11
12
  import {
12
13
  AnonymousJazzAgent,
13
- BranchDefinition,
14
14
  CoValue,
15
15
  CoValueClass,
16
16
  ID,
@@ -108,13 +108,16 @@ export class Group extends CoValueBase implements CoValue {
108
108
  */
109
109
  addMember(
110
110
  member: Group,
111
- role?: "reader" | "writer" | "admin" | "inherit",
111
+ role?: "reader" | "writer" | "admin" | "manager" | "inherit",
112
+ ): void;
113
+ addMember(
114
+ member: Group | Account,
115
+ role: "reader" | "writer" | "admin" | "manager",
112
116
  ): void;
113
- addMember(member: Group | Account, role: "reader" | "writer" | "admin"): void;
114
117
  addMember(
115
118
  member: Group | Everyone | Account,
116
119
  role?: AccountRole | "inherit",
117
- ) {
120
+ ): void {
118
121
  if (isGroupValue(member)) {
119
122
  if (role === "writeOnly")
120
123
  throw new Error("Cannot add group as member with write-only role");
@@ -158,12 +161,7 @@ export class Group extends CoValueBase implements CoValue {
158
161
 
159
162
  const role = this.$jazz.raw.roleOf(accountID);
160
163
 
161
- if (
162
- role === "admin" ||
163
- role === "writer" ||
164
- role === "reader" ||
165
- role === "writeOnly"
166
- ) {
164
+ if (isAccountRole(role)) {
167
165
  const ref = new Ref<Account>(
168
166
  accountID,
169
167
  this.$jazz.loadedAs,
@@ -249,9 +247,12 @@ export class Group extends CoValueBase implements CoValue {
249
247
  */
250
248
  extend(
251
249
  parent: Group,
252
- roleMapping?: "reader" | "writer" | "admin" | "inherit",
250
+ roleMapping?: "reader" | "writer" | "admin" | "manager" | "inherit",
253
251
  ): this {
254
- this.$jazz.raw.extend(parent.$jazz.raw, roleMapping);
252
+ this.$jazz.raw.extend(
253
+ parent.$jazz.raw,
254
+ roleMapping as "reader" | "writer" | "admin" | "manager" | "inherit",
255
+ );
255
256
  return this;
256
257
  }
257
258
 
@@ -431,12 +431,12 @@ export class InboxSender<I extends CoValue, O extends CoValue | undefined> {
431
431
  throw new Error("Failed to load the inbox owner profile");
432
432
  }
433
433
 
434
+ const inboxOwnerRole = inboxOwnerProfileRaw.group.roleOf(
435
+ currentAccount.$jazz.raw.id,
436
+ );
437
+
434
438
  if (
435
- inboxOwnerProfileRaw.group.roleOf(currentAccount.$jazz.raw.id) !==
436
- "reader" &&
437
- inboxOwnerProfileRaw.group.roleOf(currentAccount.$jazz.raw.id) !==
438
- "writer" &&
439
- inboxOwnerProfileRaw.group.roleOf(currentAccount.$jazz.raw.id) !== "admin"
439
+ !["reader", "writer", "admin", "manager"].includes(inboxOwnerRole ?? "")
440
440
  ) {
441
441
  throw new Error(
442
442
  "Insufficient permissions to access the inbox, make sure its user profile is publicly readable.",
@@ -461,6 +461,41 @@ export function getIdFromHeader(
461
461
  }
462
462
 
463
463
  export async function unstable_loadUnique<
464
+ S extends CoValueClassOrSchema,
465
+ const R extends ResolveQuery<S>,
466
+ >(
467
+ schema: S,
468
+ options: {
469
+ unique: CoValueUniqueness["uniqueness"];
470
+ onCreateWhenMissing?: () => void;
471
+ onUpdateWhenFound?: (value: Loaded<S, R>) => void;
472
+ owner: Account | Group;
473
+ resolve?: ResolveQueryStrict<S, R>;
474
+ },
475
+ ): Promise<Loaded<S, R> | null> {
476
+ const cls = coValueClassFromCoValueClassOrSchema(schema);
477
+
478
+ if (
479
+ !("_getUniqueHeader" in cls) ||
480
+ typeof cls._getUniqueHeader !== "function"
481
+ ) {
482
+ throw new Error("CoValue class does not support unique headers");
483
+ }
484
+
485
+ const header = cls._getUniqueHeader(options.unique, options.owner.$jazz.id);
486
+
487
+ return internalLoadUnique(cls, {
488
+ header,
489
+ onCreateWhenMissing: options.onCreateWhenMissing,
490
+ // @ts-expect-error loaded is not compatible with Resolved at type level, but they are the same thing
491
+ onUpdateWhenFound: options.onUpdateWhenFound,
492
+ owner: options.owner,
493
+ // @ts-expect-error loaded is not compatible with Resolved at type level, but they are the same thing
494
+ resolve: options.resolve,
495
+ }) as unknown as Loaded<S, R> | null;
496
+ }
497
+
498
+ export async function internalLoadUnique<
464
499
  V extends CoValue,
465
500
  R extends RefsToResolve<V>,
466
501
  >(
@@ -484,32 +519,28 @@ export async function unstable_loadUnique<
484
519
  // retrying failures
485
520
  // This way when we want to upsert we are sure that, if the load failed
486
521
  // it failed because the unique value was missing
487
- let result = await loadCoValueWithoutMe(cls, id, {
522
+ await loadCoValueWithoutMe(cls, id, {
488
523
  skipRetry: true,
489
524
  loadAs,
490
525
  });
491
526
 
492
- if (options.onCreateWhenMissing) {
493
- // if load returns unavailable, we check the state in localNode
494
- // to ward against race conditions that would happen when
495
- // running the same upsert unique concurrently
496
- if (!result && node.getCoValue(id).hasVerifiedContent()) {
497
- result = await loadCoValueWithoutMe(cls, id, {
498
- loadAs,
499
- });
500
- }
527
+ const isAvailable = node.getCoValue(id).hasVerifiedContent();
501
528
 
502
- if (!result) {
503
- options.onCreateWhenMissing();
529
+ // if load returns unavailable, we check the state in localNode
530
+ // to ward against race conditions that would happen when
531
+ // running the same upsert unique concurrently
532
+ if (options.onCreateWhenMissing && !isAvailable) {
533
+ options.onCreateWhenMissing();
504
534
 
505
- return loadCoValueWithoutMe(cls, id, {
506
- loadAs,
507
- resolve: options.resolve,
508
- });
509
- }
535
+ return loadCoValueWithoutMe(cls, id, {
536
+ loadAs,
537
+ resolve: options.resolve,
538
+ });
510
539
  }
511
540
 
512
- if (!result) return result;
541
+ if (!isAvailable) {
542
+ return null;
543
+ }
513
544
 
514
545
  if (options.onUpdateWhenFound) {
515
546
  // we deeply load the value, retrying any failures
@@ -36,7 +36,6 @@ export type {
36
36
  AccountClass,
37
37
  AccountCreationProps,
38
38
  BaseProfileShape,
39
- unstable_loadUnique,
40
39
  } from "./internal.js";
41
40
 
42
41
  export {
@@ -57,6 +56,7 @@ export {
57
56
  exportCoValue,
58
57
  importContentPieces,
59
58
  Ref,
59
+ unstable_loadUnique,
60
60
  } from "./internal.js";
61
61
 
62
62
  export {
@@ -1,16 +1,11 @@
1
- import { type InviteSecret, cojsonInternals } from "cojson";
1
+ import { AccountRole, type InviteSecret, cojsonInternals } from "cojson";
2
2
  import { Account } from "../coValues/account.js";
3
- import type {
4
- CoValue,
5
- CoValueClass,
6
- CoValueClassOrSchema,
7
- ID,
8
- } from "../internal.js";
3
+ import type { CoValue, CoValueClassOrSchema } from "../internal.js";
9
4
 
10
5
  /** @category Invite Links */
11
6
  export function createInviteLink<C extends CoValue>(
12
7
  value: C,
13
- role: "reader" | "writer" | "admin" | "writeOnly",
8
+ role: AccountRole,
14
9
  baseURL: string,
15
10
  valueHint?: string,
16
11
  ): string {
@@ -26,7 +26,9 @@ export type SchemaField =
26
26
  | z.core.$ZodNullable<z.core.$ZodType>
27
27
  | z.core.$ZodUnion<z.core.$ZodType[]>
28
28
  | z.core.$ZodDiscriminatedUnion<z.core.$ZodType[]>
29
+ | z.core.$ZodIntersection<z.core.$ZodType, z.core.$ZodType>
29
30
  | z.core.$ZodObject<z.core.$ZodLooseShape>
31
+ | z.core.$ZodRecord<z.core.$ZodRecordKey, z.core.$ZodType>
30
32
  | z.core.$ZodArray<z.core.$ZodType>
31
33
  | z.core.$ZodTuple<z.core.$ZodType[]>
32
34
  | z.core.$ZodReadonly<z.core.$ZodType>
@@ -146,8 +148,10 @@ export function schemaFieldToCoFieldDef(schema: SchemaField) {
146
148
  );
147
149
  } else if (
148
150
  zodSchemaDef.type === "object" ||
151
+ zodSchemaDef.type === "record" ||
149
152
  zodSchemaDef.type === "array" ||
150
- zodSchemaDef.type === "tuple"
153
+ zodSchemaDef.type === "tuple" ||
154
+ zodSchemaDef.type === "intersection"
151
155
  ) {
152
156
  return coField.json();
153
157
  } else if (zodSchemaDef.type === "union") {
@@ -2,6 +2,7 @@ import {
2
2
  Account,
3
3
  AccountCreationProps,
4
4
  BranchDefinition,
5
+ coOptionalDefiner,
5
6
  Group,
6
7
  RefsToResolveStrict,
7
8
  Simplify,
@@ -149,6 +150,7 @@ export function enrichAccountSchema<Shape extends BaseAccountShape>(
149
150
  getCoValueClass: () => {
150
151
  return coValueClass;
151
152
  },
153
+ optional: () => coOptionalDefiner(enrichedSchema),
152
154
  }) as unknown as AccountSchema<Shape>;
153
155
  return enrichedSchema;
154
156
  }
@@ -25,59 +25,72 @@ export type TypeOfZodSchema<S extends z.core.$ZodType> =
25
25
  infer Members extends readonly z.core.$ZodType[]
26
26
  >
27
27
  ? TypeOfZodSchema<Members[number]>
28
- : S extends z.core.$ZodObject<infer Shape>
29
- ? /**
30
- * Cannot use {@link PartialOnUndefined} because evaluating TypeOfZodSchema<Shape[key]>
31
- * to know if the value can be undefined does not work with recursive types.
32
- */
33
- {
34
- -readonly [key in keyof Shape as Shape[key] extends OptionalInSchema
35
- ? never
36
- : key]: TypeOfZodSchema<Shape[key]>;
37
- } & {
38
- -readonly [key in keyof Shape as Shape[key] extends OptionalInSchema
39
- ? key
40
- : never]?: TypeOfZodSchema<Shape[key]>;
41
- }
42
- : S extends z.core.$ZodArray<infer Item extends z.core.$ZodType>
43
- ? TypeOfZodSchema<Item>[]
44
- : S extends z.core.$ZodTuple<
45
- infer Items extends readonly z.core.$ZodType[]
28
+ : S extends z.core.$ZodIntersection<
29
+ infer Left extends z.core.$ZodType,
30
+ infer Right extends z.core.$ZodType
31
+ >
32
+ ? TypeOfZodSchema<Left> & TypeOfZodSchema<Right>
33
+ : S extends z.core.$ZodObject<infer Shape>
34
+ ? /**
35
+ * Cannot use {@link PartialOnUndefined} because evaluating TypeOfZodSchema<Shape[key]>
36
+ * to know if the value can be undefined does not work with recursive types.
37
+ */
38
+ {
39
+ -readonly [key in keyof Shape as Shape[key] extends OptionalInSchema
40
+ ? never
41
+ : key]: TypeOfZodSchema<Shape[key]>;
42
+ } & {
43
+ -readonly [key in keyof Shape as Shape[key] extends OptionalInSchema
44
+ ? key
45
+ : never]?: TypeOfZodSchema<Shape[key]>;
46
+ }
47
+ : S extends z.core.$ZodRecord<
48
+ infer Key,
49
+ infer Value extends z.core.$ZodType
46
50
  >
47
51
  ? {
48
- [key in keyof Items]: TypeOfZodSchema<Items[key]>;
52
+ [key in z.output<Key>]: TypeOfZodSchema<Value>;
49
53
  }
50
- : S extends z.core.$ZodString
51
- ? string
52
- : S extends z.core.$ZodNumber
53
- ? number
54
- : S extends z.core.$ZodBoolean
55
- ? boolean
56
- : S extends z.core.$ZodLiteral<infer Literal>
57
- ? Literal
58
- : S extends z.core.$ZodDate
59
- ? Date
60
- : S extends z.core.$ZodEnum<infer Enum>
61
- ? Enum[keyof Enum]
62
- : S extends z.core.$ZodTemplateLiteral<
63
- infer pattern
64
- >
65
- ? pattern
66
- : S extends z.core.$ZodReadonly<
67
- infer Inner extends z.core.$ZodType
68
- >
69
- ? TypeOfZodSchema<Inner>
70
- : S extends z.core.$ZodDefault<
71
- infer Default extends z.core.$ZodType
54
+ : S extends z.core.$ZodArray<infer Item extends z.core.$ZodType>
55
+ ? TypeOfZodSchema<Item>[]
56
+ : S extends z.core.$ZodTuple<
57
+ infer Items extends readonly z.core.$ZodType[]
58
+ >
59
+ ? {
60
+ [key in keyof Items]: TypeOfZodSchema<Items[key]>;
61
+ }
62
+ : S extends z.core.$ZodString
63
+ ? string
64
+ : S extends z.core.$ZodNumber
65
+ ? number
66
+ : S extends z.core.$ZodBoolean
67
+ ? boolean
68
+ : S extends z.core.$ZodLiteral<infer Literal>
69
+ ? Literal
70
+ : S extends z.core.$ZodDate
71
+ ? Date
72
+ : S extends z.core.$ZodEnum<infer Enum>
73
+ ? Enum[keyof Enum]
74
+ : S extends z.core.$ZodTemplateLiteral<
75
+ infer pattern
72
76
  >
73
- ? TypeOfZodSchema<Default>
74
- : S extends z.core.$ZodCodec<
75
- any,
76
- infer Out extends z.core.$ZodType
77
+ ? pattern
78
+ : S extends z.core.$ZodReadonly<
79
+ infer Inner extends z.core.$ZodType
77
80
  >
78
- ? Out["_zod"]["output"]
79
- : S extends z.core.$ZodCatch<
80
- infer Catch extends z.core.$ZodType
81
+ ? TypeOfZodSchema<Inner>
82
+ : S extends z.core.$ZodDefault<
83
+ infer Default extends z.core.$ZodType
81
84
  >
82
- ? TypeOfZodSchema<Catch>
83
- : never;
85
+ ? TypeOfZodSchema<Default>
86
+ : S extends z.core.$ZodCodec<
87
+ any,
88
+ infer Out extends z.core.$ZodType
89
+ >
90
+ ? Out["_zod"]["output"]
91
+ : S extends z.core.$ZodCatch<
92
+ infer Catch extends
93
+ z.core.$ZodType
94
+ >
95
+ ? TypeOfZodSchema<Catch>
96
+ : never;
@@ -32,8 +32,8 @@ export {
32
32
  int32,
33
33
  union,
34
34
  discriminatedUnion,
35
- // record,
36
- // intersection,
35
+ record,
36
+ intersection,
37
37
  int,
38
38
  codec,
39
39
  optional,
@@ -220,6 +220,9 @@ export class CoValueCoreSubscription {
220
220
 
221
221
  emit(value: RawCoValue | "unavailable"): void {
222
222
  if (this.unsubscribed) return;
223
+ if (!isReadyForEmit(value)) {
224
+ return;
225
+ }
223
226
 
224
227
  this.listener(value);
225
228
  }
@@ -234,3 +237,17 @@ export class CoValueCoreSubscription {
234
237
  this._unsubscribe();
235
238
  }
236
239
  }
240
+
241
+ /**
242
+ * This is true if the value is unavailable, or if the value is a binary coValue or a completely downloaded coValue.
243
+ */
244
+ function isReadyForEmit(value: RawCoValue | "unavailable") {
245
+ if (value === "unavailable") {
246
+ return true;
247
+ }
248
+
249
+ return (
250
+ value.core.verified?.header.meta?.type === "binary" ||
251
+ value.core.isCompletelyDownloaded()
252
+ );
253
+ }