jazz-tools 0.13.30 → 0.14.0

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 (163) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/CHANGELOG.md +23 -3
  3. package/dist/auth/DemoAuth.d.ts.map +1 -1
  4. package/dist/auth/PassphraseAuth.d.ts +1 -3
  5. package/dist/auth/PassphraseAuth.d.ts.map +1 -1
  6. package/dist/{chunk-LMV6J7GN.js → chunk-2ASOGEYA.js} +3531 -3269
  7. package/dist/chunk-2ASOGEYA.js.map +1 -0
  8. package/dist/coValues/CoValueBase.d.ts +22 -0
  9. package/dist/coValues/CoValueBase.d.ts.map +1 -0
  10. package/dist/coValues/account.d.ts +12 -12
  11. package/dist/coValues/account.d.ts.map +1 -1
  12. package/dist/coValues/coFeed.d.ts +24 -25
  13. package/dist/coValues/coFeed.d.ts.map +1 -1
  14. package/dist/coValues/coList.d.ts +10 -13
  15. package/dist/coValues/coList.d.ts.map +1 -1
  16. package/dist/coValues/coMap.d.ts +32 -35
  17. package/dist/coValues/coMap.d.ts.map +1 -1
  18. package/dist/coValues/coPlainText.d.ts.map +1 -1
  19. package/dist/coValues/deepLoading.d.ts +28 -22
  20. package/dist/coValues/deepLoading.d.ts.map +1 -1
  21. package/dist/coValues/extensions/imageDef.d.ts +12 -11
  22. package/dist/coValues/extensions/imageDef.d.ts.map +1 -1
  23. package/dist/coValues/group.d.ts +5 -9
  24. package/dist/coValues/group.d.ts.map +1 -1
  25. package/dist/coValues/inbox.d.ts +2 -3
  26. package/dist/coValues/inbox.d.ts.map +1 -1
  27. package/dist/coValues/interfaces.d.ts +8 -34
  28. package/dist/coValues/interfaces.d.ts.map +1 -1
  29. package/dist/coValues/profile.d.ts +4 -14
  30. package/dist/coValues/profile.d.ts.map +1 -1
  31. package/dist/coValues/registeredSchemas.d.ts +1 -3
  32. package/dist/coValues/registeredSchemas.d.ts.map +1 -1
  33. package/dist/coValues/schemaUnion.d.ts +6 -6
  34. package/dist/exports.d.ts +12 -16
  35. package/dist/exports.d.ts.map +1 -1
  36. package/dist/implementation/ContextManager.d.ts +1 -1
  37. package/dist/implementation/ContextManager.d.ts.map +1 -1
  38. package/dist/implementation/activeAccountContext.d.ts +1 -1
  39. package/dist/implementation/activeAccountContext.d.ts.map +1 -1
  40. package/dist/implementation/createContext.d.ts +10 -10
  41. package/dist/implementation/createContext.d.ts.map +1 -1
  42. package/dist/implementation/invites.d.ts +6 -6
  43. package/dist/implementation/invites.d.ts.map +1 -1
  44. package/dist/implementation/refs.d.ts +2 -2
  45. package/dist/implementation/refs.d.ts.map +1 -1
  46. package/dist/implementation/schema.d.ts +21 -28
  47. package/dist/implementation/schema.d.ts.map +1 -1
  48. package/dist/implementation/zodSchema/runtimeConverters/zodFieldToCoFieldDef.d.ts +9 -0
  49. package/dist/implementation/zodSchema/runtimeConverters/zodFieldToCoFieldDef.d.ts.map +1 -0
  50. package/dist/implementation/zodSchema/runtimeConverters/zodSchemaToCoSchema.d.ts +28 -0
  51. package/dist/implementation/zodSchema/runtimeConverters/zodSchemaToCoSchema.d.ts.map +1 -0
  52. package/dist/implementation/zodSchema/schemaTypes/AccountSchema.d.ts +65 -0
  53. package/dist/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -0
  54. package/dist/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts +28 -0
  55. package/dist/implementation/zodSchema/schemaTypes/CoFeedSchema.d.ts.map +1 -0
  56. package/dist/implementation/zodSchema/schemaTypes/CoListSchema.d.ts +24 -0
  57. package/dist/implementation/zodSchema/schemaTypes/CoListSchema.d.ts.map +1 -0
  58. package/dist/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts +41 -0
  59. package/dist/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -0
  60. package/dist/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts +35 -0
  61. package/dist/implementation/zodSchema/schemaTypes/CoRecordSchema.d.ts.map +1 -0
  62. package/dist/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts +9 -0
  63. package/dist/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts.map +1 -0
  64. package/dist/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts +20 -0
  65. package/dist/implementation/zodSchema/schemaTypes/PlainTextSchema.d.ts.map +1 -0
  66. package/dist/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts +18 -0
  67. package/dist/implementation/zodSchema/schemaTypes/RichTextSchema.d.ts.map +1 -0
  68. package/dist/implementation/zodSchema/typeConverters/InstanceOfSchema.d.ts +24 -0
  69. package/dist/implementation/zodSchema/typeConverters/InstanceOfSchema.d.ts.map +1 -0
  70. package/dist/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.d.ts +21 -0
  71. package/dist/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.d.ts.map +1 -0
  72. package/dist/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchema.d.ts +29 -0
  73. package/dist/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchema.d.ts.map +1 -0
  74. package/dist/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.d.ts +29 -0
  75. package/dist/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.d.ts.map +1 -0
  76. package/dist/implementation/zodSchema/unionUtils.d.ts +6 -0
  77. package/dist/implementation/zodSchema/unionUtils.d.ts.map +1 -0
  78. package/dist/implementation/zodSchema/zodCo.d.ts +35 -0
  79. package/dist/implementation/zodSchema/zodCo.d.ts.map +1 -0
  80. package/dist/implementation/zodSchema/zodSchema.d.ts +38 -0
  81. package/dist/implementation/zodSchema/zodSchema.d.ts.map +1 -0
  82. package/dist/index.js +295 -10
  83. package/dist/index.js.map +1 -1
  84. package/dist/internal.d.ts +34 -0
  85. package/dist/internal.d.ts.map +1 -1
  86. package/dist/subscribe/SubscriptionScope.d.ts +2 -2
  87. package/dist/subscribe/SubscriptionScope.d.ts.map +1 -1
  88. package/dist/subscribe/utils.d.ts +2 -2
  89. package/dist/subscribe/utils.d.ts.map +1 -1
  90. package/dist/testing.d.ts +10 -8
  91. package/dist/testing.d.ts.map +1 -1
  92. package/dist/testing.js +1 -1
  93. package/dist/testing.js.map +1 -1
  94. package/dist/tests/utils.d.ts +6 -2
  95. package/dist/tests/utils.d.ts.map +1 -1
  96. package/dist/types.d.ts +1 -7
  97. package/dist/types.d.ts.map +1 -1
  98. package/package.json +3 -2
  99. package/src/auth/DemoAuth.ts +1 -2
  100. package/src/auth/PassphraseAuth.ts +1 -1
  101. package/src/coValues/CoValueBase.ts +83 -0
  102. package/src/coValues/account.ts +53 -43
  103. package/src/coValues/coFeed.ts +65 -83
  104. package/src/coValues/coList.ts +28 -21
  105. package/src/coValues/coMap.ts +54 -38
  106. package/src/coValues/coPlainText.ts +4 -1
  107. package/src/coValues/deepLoading.ts +46 -36
  108. package/src/coValues/extensions/imageDef.ts +21 -19
  109. package/src/coValues/group.ts +37 -38
  110. package/src/coValues/inbox.ts +24 -11
  111. package/src/coValues/interfaces.ts +29 -93
  112. package/src/coValues/profile.ts +12 -13
  113. package/src/coValues/registeredSchemas.ts +1 -3
  114. package/src/coValues/schemaUnion.ts +6 -6
  115. package/src/exports.ts +47 -25
  116. package/src/implementation/activeAccountContext.ts +1 -1
  117. package/src/implementation/createContext.ts +39 -24
  118. package/src/implementation/invites.ts +15 -12
  119. package/src/implementation/refs.ts +6 -4
  120. package/src/implementation/schema.ts +22 -34
  121. package/src/implementation/zodSchema/runtimeConverters/zodFieldToCoFieldDef.ts +101 -0
  122. package/src/implementation/zodSchema/runtimeConverters/zodSchemaToCoSchema.ts +191 -0
  123. package/src/implementation/zodSchema/schemaTypes/AccountSchema.ts +102 -0
  124. package/src/implementation/zodSchema/schemaTypes/CoFeedSchema.ts +70 -0
  125. package/src/implementation/zodSchema/schemaTypes/CoListSchema.ts +59 -0
  126. package/src/implementation/zodSchema/schemaTypes/CoMapSchema.ts +126 -0
  127. package/src/implementation/zodSchema/schemaTypes/CoRecordSchema.ts +98 -0
  128. package/src/implementation/zodSchema/schemaTypes/FileStreamSchema.ts +9 -0
  129. package/src/implementation/zodSchema/schemaTypes/PlainTextSchema.ts +27 -0
  130. package/src/implementation/zodSchema/schemaTypes/RichTextSchema.ts +25 -0
  131. package/src/implementation/zodSchema/typeConverters/InstanceOfSchema.ts +61 -0
  132. package/src/implementation/zodSchema/typeConverters/InstanceOfSchemaCoValuesNullable.ts +77 -0
  133. package/src/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchema.ts +90 -0
  134. package/src/implementation/zodSchema/typeConverters/InstanceOrPrimitiveOfSchemaCoValuesNullable.ts +103 -0
  135. package/src/implementation/zodSchema/unionUtils.ts +139 -0
  136. package/src/implementation/zodSchema/zodCo.ts +409 -0
  137. package/src/implementation/zodSchema/zodSchema.ts +116 -0
  138. package/src/internal.ts +38 -0
  139. package/src/subscribe/SubscriptionScope.ts +30 -3
  140. package/src/subscribe/utils.ts +7 -2
  141. package/src/testing.ts +14 -16
  142. package/src/tests/ContextManager.test.ts +73 -47
  143. package/src/tests/DemoAuth.test.ts +1 -1
  144. package/src/tests/account.test.ts +6 -9
  145. package/src/tests/coFeed.test.ts +102 -63
  146. package/src/tests/coList.test.ts +82 -95
  147. package/src/tests/coMap.record.test.ts +53 -87
  148. package/src/tests/coMap.test.ts +297 -312
  149. package/src/tests/coPlainText.test.ts +19 -39
  150. package/src/tests/createContext.test.ts +33 -15
  151. package/src/tests/deepLoading.test.ts +397 -131
  152. package/src/tests/groupsAndAccounts.test.ts +81 -72
  153. package/src/tests/imageDef.test.ts +22 -13
  154. package/src/tests/inbox.test.ts +36 -29
  155. package/src/tests/load.test.ts +10 -10
  156. package/src/tests/patterns/requestToJoin.test.ts +31 -31
  157. package/src/tests/schema.test.ts +37 -38
  158. package/src/tests/schemaUnion.test.ts +54 -64
  159. package/src/tests/subscribe.test.ts +118 -116
  160. package/src/tests/testing.test.ts +33 -33
  161. package/src/tests/utils.ts +3 -2
  162. package/src/types.ts +1 -8
  163. package/dist/chunk-LMV6J7GN.js.map +0 -1
@@ -1,42 +1,43 @@
1
- import { cojsonInternals } from "cojson";
1
+ import { Profile, cojsonInternals } from "cojson";
2
2
  import { WasmCrypto } from "cojson/crypto/WasmCrypto";
3
3
  import { assert, describe, expect, expectTypeOf, test, vi } from "vitest";
4
4
  import {
5
- Account,
6
- CoFeed,
7
- CoList,
8
- CoMap,
9
5
  Group,
10
6
  ID,
11
- Profile,
12
7
  SessionID,
13
- co,
14
8
  createJazzContextFromExistingCredentials,
15
9
  isControlledAccount,
10
+ z,
16
11
  } from "../index.js";
17
- import { randomSessionProvider } from "../internal.js";
12
+ import {
13
+ Account,
14
+ CoListSchema,
15
+ Loaded,
16
+ co,
17
+ randomSessionProvider,
18
+ } from "../internal.js";
18
19
  import { createJazzTestAccount, linkAccounts } from "../testing.js";
19
20
  import { waitFor } from "./utils.js";
20
21
 
21
22
  const Crypto = await WasmCrypto.create();
22
23
  const { connectedPeers } = cojsonInternals;
23
24
 
24
- class TestMap extends CoMap {
25
- list = co.ref(TestList);
26
- optionalRef = co.ref(InnermostMap, { optional: true });
27
- }
25
+ const InnermostMap = co.map({
26
+ value: z.string(),
27
+ });
28
28
 
29
- class TestList extends CoList.Of(co.ref(() => InnerMap)) {}
29
+ const TestFeed = co.feed(InnermostMap);
30
30
 
31
- class InnerMap extends CoMap {
32
- stream = co.ref(TestStream);
33
- }
31
+ const InnerMap = co.map({
32
+ stream: TestFeed,
33
+ });
34
34
 
35
- class TestStream extends CoFeed.Of(co.ref(() => InnermostMap)) {}
35
+ const TestList = co.list(InnerMap);
36
36
 
37
- class InnermostMap extends CoMap {
38
- value = co.string;
39
- }
37
+ const TestMap = co.map({
38
+ list: TestList,
39
+ optionalRef: z.optional(InnermostMap),
40
+ });
40
41
 
41
42
  describe("Deep loading with depth arg", async () => {
42
43
  const me = await Account.create({
@@ -71,7 +72,7 @@ describe("Deep loading with depth arg", async () => {
71
72
  [
72
73
  InnerMap.create(
73
74
  {
74
- stream: TestStream.create(
75
+ stream: TestFeed.create(
75
76
  [InnermostMap.create({ value: "hello" }, ownership)],
76
77
  ownership,
77
78
  ),
@@ -87,7 +88,7 @@ describe("Deep loading with depth arg", async () => {
87
88
 
88
89
  test("load without resolve", async () => {
89
90
  const map1 = await TestMap.load(map.id, { loadAs: meOnSecondPeer });
90
- expectTypeOf(map1).toEqualTypeOf<TestMap | null>();
91
+ expectTypeOf(map1).branded.toEqualTypeOf<Loaded<typeof TestMap> | null>();
91
92
 
92
93
  assert(map1, "map1 is null");
93
94
 
@@ -99,9 +100,9 @@ describe("Deep loading with depth arg", async () => {
99
100
  loadAs: meOnSecondPeer,
100
101
  resolve: { list: true },
101
102
  });
102
- expectTypeOf(map2).toEqualTypeOf<
103
- | (TestMap & {
104
- list: TestList;
103
+ expectTypeOf(map2).branded.toEqualTypeOf<
104
+ | (Loaded<typeof TestMap> & {
105
+ list: Loaded<typeof TestList>;
105
106
  })
106
107
  | null
107
108
  >();
@@ -115,9 +116,9 @@ describe("Deep loading with depth arg", async () => {
115
116
  loadAs: meOnSecondPeer,
116
117
  resolve: { list: { $each: true } },
117
118
  });
118
- expectTypeOf(map3).toEqualTypeOf<
119
- | (TestMap & {
120
- list: TestList & InnerMap[];
119
+ expectTypeOf(map3).branded.toEqualTypeOf<
120
+ | (Loaded<typeof TestMap> & {
121
+ list: Loaded<typeof TestList> & Loaded<typeof InnerMap>[];
121
122
  })
122
123
  | null
123
124
  >();
@@ -131,9 +132,9 @@ describe("Deep loading with depth arg", async () => {
131
132
  loadAs: meOnSecondPeer,
132
133
  resolve: { optionalRef: true } as const,
133
134
  });
134
- expectTypeOf(map3a).toEqualTypeOf<
135
- | (TestMap & {
136
- optionalRef: InnermostMap | undefined;
135
+ expectTypeOf(map3a).branded.toEqualTypeOf<
136
+ | (Loaded<typeof TestMap> & {
137
+ optionalRef: Loaded<typeof InnermostMap> | undefined;
137
138
  })
138
139
  | null
139
140
  >();
@@ -146,15 +147,16 @@ describe("Deep loading with depth arg", async () => {
146
147
  loadAs: meOnSecondPeer,
147
148
  resolve: { list: { $each: { stream: true } } },
148
149
  });
149
- expectTypeOf(map4).toEqualTypeOf<
150
- | (TestMap & {
151
- list: TestList & (InnerMap & { stream: TestStream })[];
150
+ expectTypeOf(map4).branded.toEqualTypeOf<
151
+ | (Loaded<typeof TestMap> & {
152
+ list: Loaded<typeof TestList> &
153
+ (Loaded<typeof InnerMap> & { stream: Loaded<typeof TestFeed> })[];
152
154
  })
153
155
  | null
154
156
  >();
155
157
  assert(map4, "map4 is null");
156
158
  expect(map4.list[0]?.stream).toBeTruthy();
157
- expect(map4.list[0]?.stream?.[me.id]).toBeTruthy();
159
+ expect(map4.list[0]?.stream?.perAccount[me.id]).toBeTruthy();
158
160
  expect(map4.list[0]?.stream?.byMe?.value).toBe(null);
159
161
  });
160
162
 
@@ -164,73 +166,76 @@ describe("Deep loading with depth arg", async () => {
164
166
  resolve: { list: { $each: { stream: { $each: true } } } },
165
167
  });
166
168
  type ExpectedMap5 =
167
- | (TestMap & {
168
- list: TestList &
169
- (InnerMap & {
170
- stream: TestStream & {
171
- byMe?: { value: InnermostMap };
172
- inCurrentSession?: { value: InnermostMap };
169
+ | (Loaded<typeof TestMap> & {
170
+ list: Loaded<typeof TestList> &
171
+ (Loaded<typeof InnerMap> & {
172
+ stream: Loaded<typeof TestFeed> & {
173
+ byMe?: { value: Loaded<typeof InnermostMap> };
174
+ inCurrentSession?: { value: Loaded<typeof InnermostMap> };
173
175
  perSession: {
174
176
  [sessionID: SessionID]: {
175
- value: InnermostMap;
177
+ value: Loaded<typeof InnermostMap>;
176
178
  };
177
179
  };
178
180
  } & {
179
- [key: ID<Account>]: { value: InnermostMap };
181
+ [key: ID<Account>]: { value: Loaded<typeof InnermostMap> };
180
182
  };
181
183
  })[];
182
184
  })
183
185
  | null;
184
- expectTypeOf(map5).toEqualTypeOf<ExpectedMap5>();
186
+ expectTypeOf(map5).branded.toEqualTypeOf<ExpectedMap5>();
185
187
  assert(map5, "map5 is null");
186
188
 
187
- expect(map5.list[0]?.stream?.[me.id]?.value).toBeTruthy();
189
+ expect(map5.list[0]?.stream?.perAccount[me.id]?.value).toBeTruthy();
188
190
  expect(map5.list[0]?.stream?.byMe?.value).toBeTruthy();
189
191
  });
190
192
  });
191
193
 
192
- class CustomProfile extends Profile {
193
- stream = co.ref(TestStream);
194
- }
195
-
196
- class CustomAccount extends Account {
197
- profile = co.ref(CustomProfile);
198
- root = co.ref(TestMap);
194
+ const CustomProfile = co.profile({
195
+ name: z.string(),
196
+ stream: TestFeed,
197
+ });
199
198
 
200
- async migrate(
201
- this: CustomAccount,
202
- creationProps?: { name: string } | undefined,
203
- ) {
199
+ const CustomAccount = co
200
+ .account({
201
+ profile: CustomProfile,
202
+ root: TestMap,
203
+ })
204
+ .withMigration(async (account, creationProps) => {
204
205
  if (creationProps) {
205
- const profileGroup = Group.create(this);
206
- this.profile = CustomProfile.create(
206
+ const profileGroup = Group.create(account);
207
+ account.profile = CustomProfile.create(
207
208
  {
208
209
  name: creationProps.name,
209
- stream: TestStream.create([], this),
210
+ stream: TestFeed.create([], account),
210
211
  },
211
212
  profileGroup,
212
213
  );
213
- this.root = TestMap.create({ list: TestList.create([], this) }, this);
214
+ account.root = TestMap.create(
215
+ { list: TestList.create([], account) },
216
+ account,
217
+ );
214
218
  }
215
219
 
216
- const thisLoaded = await this.ensureLoaded({
220
+ const accountLoaded = await account.ensureLoaded({
217
221
  resolve: {
218
222
  profile: { stream: true },
219
223
  root: { list: true },
220
224
  },
221
225
  });
222
- expectTypeOf(thisLoaded).toEqualTypeOf<
223
- CustomAccount & {
224
- profile: CustomProfile & {
225
- stream: TestStream;
226
- };
227
- root: TestMap & {
228
- list: TestList;
229
- };
230
- }
231
- >();
232
- }
233
- }
226
+
227
+ // using assignment to check type compatibility
228
+ const _T:
229
+ | (Loaded<typeof CustomAccount> & {
230
+ profile: Loaded<typeof CustomProfile> & {
231
+ stream: Loaded<typeof TestFeed>;
232
+ };
233
+ root: Loaded<typeof TestMap> & {
234
+ list: Loaded<typeof TestList>;
235
+ };
236
+ })
237
+ | null = accountLoaded;
238
+ });
234
239
 
235
240
  test("Deep loading within account", async () => {
236
241
  const me = await CustomAccount.create({
@@ -244,22 +249,24 @@ test("Deep loading within account", async () => {
244
249
  root: { list: true },
245
250
  },
246
251
  });
247
- expectTypeOf(meLoaded).toEqualTypeOf<
248
- CustomAccount & {
249
- profile: CustomProfile & {
250
- stream: TestStream;
251
- };
252
- root: TestMap & {
253
- list: TestList;
254
- };
255
- }
256
- >();
252
+
253
+ // using assignment to check type compatibility
254
+ const _T:
255
+ | (Loaded<typeof CustomAccount> & {
256
+ profile: Loaded<typeof CustomProfile> & {
257
+ stream: Loaded<typeof TestFeed>;
258
+ };
259
+ root: Loaded<typeof TestMap> & {
260
+ list: Loaded<typeof TestList>;
261
+ };
262
+ })
263
+ | null = meLoaded;
257
264
 
258
265
  expect(meLoaded.profile.stream).toBeTruthy();
259
266
  expect(meLoaded.root.list).toBeTruthy();
260
267
  });
261
268
 
262
- class RecordLike extends CoMap.Record(co.ref(TestMap)) {}
269
+ const RecordLike = co.record(z.string(), TestMap);
263
270
 
264
271
  test("Deep loading a record-like coMap", async () => {
265
272
  const me = await Account.create({
@@ -309,9 +316,9 @@ test("Deep loading a record-like coMap", async () => {
309
316
  },
310
317
  });
311
318
  expectTypeOf(recordLoaded).toEqualTypeOf<
312
- | (RecordLike & {
313
- [key: string]: TestMap & {
314
- list: TestList & InnerMap[];
319
+ | (Loaded<typeof RecordLike> & {
320
+ [key: string]: Loaded<typeof TestMap> & {
321
+ list: Loaded<typeof TestList> & Loaded<typeof InnerMap>[];
315
322
  };
316
323
  })
317
324
  | null
@@ -356,18 +363,19 @@ test("The resolve type doesn't accept extra keys", async () => {
356
363
  },
357
364
  });
358
365
 
359
- expectTypeOf(meLoaded).toEqualTypeOf<
360
- CustomAccount & {
361
- profile: CustomProfile & {
362
- stream: TestStream;
363
- extraKey: never;
364
- };
365
- root: TestMap & {
366
- list: TestList;
367
- extraKey: never;
368
- };
369
- }
370
- >();
366
+ // using assignment to check type compatibility
367
+ const _T:
368
+ | (Loaded<typeof CustomAccount> & {
369
+ profile: Loaded<typeof CustomProfile> & {
370
+ stream: Loaded<typeof TestFeed>;
371
+ extraKey: never;
372
+ };
373
+ root: Loaded<typeof TestMap> & {
374
+ list: Loaded<typeof TestList>;
375
+ extraKey: never;
376
+ };
377
+ })
378
+ | null = meLoaded;
371
379
  } catch (e) {
372
380
  expect(e).toBeInstanceOf(Error);
373
381
  }
@@ -438,7 +446,7 @@ describe("Deep loading with unauthorized account", async () => {
438
446
  [
439
447
  InnerMap.create(
440
448
  {
441
- stream: TestStream.create([], group),
449
+ stream: TestFeed.create([], group),
442
450
  },
443
451
  onlyBob,
444
452
  ),
@@ -524,7 +532,7 @@ describe("Deep loading with unauthorized account", async () => {
524
532
  [
525
533
  InnerMap.create(
526
534
  {
527
- stream: TestStream.create([], onlyBob),
535
+ stream: TestFeed.create([], onlyBob),
528
536
  },
529
537
  group,
530
538
  ),
@@ -560,7 +568,7 @@ describe("Deep loading with unauthorized account", async () => {
560
568
  [
561
569
  InnerMap.create(
562
570
  {
563
- stream: TestStream.create([value], group),
571
+ stream: TestFeed.create([value], group),
564
572
  },
565
573
  group,
566
574
  ),
@@ -585,25 +593,25 @@ describe("Deep loading with unauthorized account", async () => {
585
593
  errorSpy.mockReset();
586
594
  });
587
595
 
588
- test("setting null via proxy", async () => {
589
- class Lv1 extends CoMap {
590
- lv2 = co.ref(Lv2);
591
- }
596
+ test("setting undefined via proxy", async () => {
597
+ const Lv3 = co.map({
598
+ string: z.string(),
599
+ });
592
600
 
593
- class Lv2 extends CoMap {
594
- lv3 = co.optional.ref(Lv3);
595
- }
601
+ const Lv2 = co.map({
602
+ lv3: z.optional(Lv3),
603
+ });
596
604
 
597
- class Lv3 extends CoMap {
598
- string = co.string;
599
- }
605
+ const Lv1 = co.map({
606
+ lv2: Lv2,
607
+ });
600
608
 
601
609
  const map = Lv1.create(
602
610
  { lv2: Lv2.create({ lv3: Lv3.create({ string: "hello" }, bob) }, bob) },
603
611
  bob,
604
612
  );
605
613
 
606
- map.lv2!.lv3 = null;
614
+ map.lv2!.lv3 = undefined;
607
615
 
608
616
  const loadedMap = await Lv1.load(map.id, {
609
617
  resolve: { lv2: { lv3: true } },
@@ -612,18 +620,273 @@ describe("Deep loading with unauthorized account", async () => {
612
620
 
613
621
  expect(loadedMap?.id).toBe(map.id);
614
622
  });
623
+
624
+ test("unaccessible record element with $onError", async () => {
625
+ const Person = co.map({
626
+ name: z.string(),
627
+ });
628
+ const Friends = co.record(z.string(), Person);
629
+
630
+ const map = Friends.create(
631
+ {
632
+ jane: Person.create({ name: "Jane" }, onlyBob),
633
+ alice: Person.create({ name: "Alice" }, group),
634
+ },
635
+ group,
636
+ );
637
+
638
+ const friendsOnAlice = await Friends.load(map.id, {
639
+ resolve: { $each: { $onError: null } },
640
+ loadAs: alice,
641
+ });
642
+
643
+ assert(friendsOnAlice, "friendsOnAlice is null");
644
+
645
+ expect(friendsOnAlice.jane).toBeNull();
646
+ expect(friendsOnAlice.alice).not.toBeNull();
647
+ });
648
+
649
+ test("unaccessible nested record element with $onError", async () => {
650
+ const Person = co.map({
651
+ name: z.string(),
652
+ });
653
+ const Friends = co.record(z.string(), Person);
654
+
655
+ const User = co.map({
656
+ name: z.string(),
657
+ friends: Friends,
658
+ });
659
+
660
+ const map = User.create(
661
+ {
662
+ name: "John",
663
+ friends: Friends.create(
664
+ {
665
+ jane: Person.create({ name: "Jane" }, onlyBob),
666
+ alice: Person.create({ name: "Alice" }, group),
667
+ },
668
+ group,
669
+ ),
670
+ },
671
+ group,
672
+ );
673
+
674
+ const user = await User.load(map.id, {
675
+ resolve: { friends: { $each: { $onError: null } } },
676
+ loadAs: alice,
677
+ });
678
+
679
+ assert(user, "user is null");
680
+
681
+ expect(user.friends.jane).toBeNull();
682
+ expect(user.friends.alice).not.toBeNull();
683
+ });
684
+
685
+ test("unaccessible element down the chain with $onError on a record", async () => {
686
+ const Dog = co.map({
687
+ name: z.string(),
688
+ });
689
+
690
+ const Person = co.map({
691
+ name: z.string(),
692
+ dog: Dog,
693
+ });
694
+
695
+ const Friends = co.record(z.string(), Person);
696
+
697
+ const User = co.map({
698
+ name: z.string(),
699
+ friends: Friends,
700
+ });
701
+
702
+ const map = User.create(
703
+ {
704
+ name: "John",
705
+ friends: Friends.create(
706
+ {
707
+ jane: Person.create(
708
+ {
709
+ name: "Jane",
710
+ dog: Dog.create({ name: "Rex" }, onlyBob), // Jane dog is inaccessible
711
+ },
712
+ group,
713
+ ),
714
+ alice: Person.create(
715
+ { name: "Alice", dog: Dog.create({ name: "Giggino" }, group) },
716
+ group,
717
+ ),
718
+ },
719
+ group,
720
+ ),
721
+ },
722
+ group,
723
+ );
724
+
725
+ const user = await User.load(map.id, {
726
+ resolve: { friends: { $each: { dog: true, $onError: null } } },
727
+ loadAs: alice,
728
+ });
729
+
730
+ assert(user);
731
+
732
+ expect(user.friends.jane).toBeNull(); // jane is null because her dog is inaccessible
733
+ expect(user.friends.alice?.dog).not.toBeNull(); // alice is not null because we have read access to her and her dog
734
+ });
735
+
736
+ test("unaccessible list element with $onError and $each with depth", async () => {
737
+ const Person = co.map({
738
+ name: z.string(),
739
+ get friends(): z.ZodOptional<typeof Friends> {
740
+ return z.optional(Friends);
741
+ },
742
+ });
743
+ const Friends: CoListSchema<typeof Person> = co.list(Person); // TODO: annoying that we have to annotate
744
+
745
+ const list = Friends.create(
746
+ [
747
+ Person.create(
748
+ {
749
+ name: "Jane",
750
+ friends: Friends.create(
751
+ [Person.create({ name: "Bob" }, onlyBob)],
752
+ group,
753
+ ),
754
+ },
755
+ group,
756
+ ),
757
+ Person.create(
758
+ {
759
+ name: "Alice",
760
+ friends: Friends.create(
761
+ [Person.create({ name: "Bob" }, group)],
762
+ group,
763
+ ),
764
+ },
765
+ group,
766
+ ),
767
+ ],
768
+ group,
769
+ );
770
+
771
+ // The error List -> Jane -> Bob should be propagated to the list element Jane
772
+ // and we should have [null, Alice]
773
+ const listOnAlice = await Friends.load(list.id, {
774
+ resolve: { $each: { friends: { $each: true }, $onError: null } },
775
+ loadAs: alice,
776
+ });
777
+
778
+ assert(listOnAlice, "listOnAlice is null");
779
+
780
+ expect(listOnAlice[0]).toBeNull();
781
+ expect(listOnAlice[1]).not.toBeNull();
782
+ expect(listOnAlice[1]?.name).toBe("Alice");
783
+ expect(listOnAlice[1]?.friends).not.toBeNull();
784
+ expect(listOnAlice[1]?.friends?.[0]?.name).toBe("Bob");
785
+ expect(listOnAlice).toHaveLength(2);
786
+ });
787
+
788
+ test("unaccessible record element with $onError", async () => {
789
+ const Person = co.map({
790
+ name: z.string(),
791
+ });
792
+ const Friend = co.record(z.string(), Person);
793
+
794
+ const map = Friend.create(
795
+ {
796
+ jane: Person.create({ name: "Jane" }, onlyBob),
797
+ alice: Person.create({ name: "Alice" }, group),
798
+ },
799
+ group,
800
+ );
801
+
802
+ const friendsOnAlice = await Friend.load(map.id, {
803
+ resolve: { $each: { $onError: null } },
804
+ loadAs: alice,
805
+ });
806
+
807
+ assert(friendsOnAlice, "friendsOnAlice is null");
808
+
809
+ expect(friendsOnAlice.jane).toBeNull();
810
+ expect(friendsOnAlice.alice).not.toBeNull();
811
+ });
812
+
813
+ test("unaccessible ref catched with $onError", async () => {
814
+ const Dog = co.map({
815
+ name: z.string(),
816
+ });
817
+
818
+ const Person = co.map({
819
+ name: z.string(),
820
+ dog: Dog,
821
+ });
822
+
823
+ const Friends = co.record(z.string(), Person);
824
+
825
+ const User = co.map({
826
+ name: z.string(),
827
+ friends: Friends,
828
+ });
829
+
830
+ const map = User.create(
831
+ {
832
+ name: "John",
833
+ friends: Friends.create(
834
+ {
835
+ jane: Person.create(
836
+ {
837
+ name: "Jane",
838
+ dog: Dog.create({ name: "Rex" }, onlyBob), // Jane dog is inaccessible
839
+ },
840
+ group,
841
+ ),
842
+ alice: Person.create(
843
+ { name: "Alice", dog: Dog.create({ name: "Giggino" }, group) },
844
+ group,
845
+ ),
846
+ },
847
+ group,
848
+ ),
849
+ },
850
+ group,
851
+ );
852
+
853
+ const user = await User.load(map.id, {
854
+ resolve: { friends: { $each: { dog: { $onError: null } } } },
855
+ loadAs: alice,
856
+ });
857
+
858
+ assert(user);
859
+
860
+ expect(user.friends.jane?.dog).toBeNull(); // jane is null because her dog is inaccessible
861
+ expect(user.friends.alice?.dog?.name).toBe("Giggino"); // alice is not null because we have read access to her and her dog
862
+ });
863
+
864
+ test("using $onError on the resolve root", async () => {
865
+ const Person = co.map({
866
+ name: z.string(),
867
+ });
868
+
869
+ const map = Person.create({ name: "John" }, onlyBob);
870
+ const user = await Person.load(map.id, {
871
+ resolve: { $onError: null },
872
+ loadAs: alice,
873
+ });
874
+
875
+ expect(user).toBeNull();
876
+ });
615
877
  });
616
878
 
617
879
  test("doesn't break on Map.Record key deletion when the key is referenced in the depth", async () => {
618
- class JazzProfile extends CoMap {
619
- firstName = co.string;
620
- }
880
+ const JazzProfile = co.map({
881
+ name: z.string(),
882
+ firstName: z.string(),
883
+ });
621
884
 
622
- class JazzySnapStore extends CoMap.Record(co.ref(JazzProfile)) {}
885
+ const JazzySnapStore = co.record(z.string(), JazzProfile);
623
886
 
624
887
  const snapStore = JazzySnapStore.create({
625
- profile1: JazzProfile.create({ firstName: "John" }),
626
- profile2: JazzProfile.create({ firstName: "John" }),
888
+ profile1: JazzProfile.create({ name: "John", firstName: "John" }),
889
+ profile2: JazzProfile.create({ name: "John", firstName: "John" }),
627
890
  });
628
891
 
629
892
  const spy = vi.fn();
@@ -651,13 +914,14 @@ test("doesn't break on Map.Record key deletion when the key is referenced in the
651
914
  });
652
915
 
653
916
  test("throw when calling ensureLoaded on a ref that's required but missing", async () => {
654
- class JazzProfile extends CoMap {
655
- firstName = co.string;
656
- }
917
+ const JazzProfile = co.map({
918
+ name: z.string(),
919
+ firstName: z.string(),
920
+ });
657
921
 
658
- class JazzRoot extends CoMap {
659
- profile = co.ref(JazzProfile);
660
- }
922
+ const JazzRoot = co.map({
923
+ profile: JazzProfile,
924
+ });
661
925
 
662
926
  const me = await Account.create({
663
927
  creationProps: { name: "Tester McTesterson" },
@@ -678,7 +942,7 @@ test("throw when calling ensureLoaded on a ref that's required but missing", asy
678
942
  });
679
943
 
680
944
  test("throw when calling ensureLoaded on a ref that is not defined in the schema", async () => {
681
- class JazzRoot extends CoMap {}
945
+ const JazzRoot = co.map({});
682
946
 
683
947
  const me = await Account.create({
684
948
  creationProps: { name: "Tester McTesterson" },
@@ -689,17 +953,19 @@ test("throw when calling ensureLoaded on a ref that is not defined in the schema
689
953
 
690
954
  await expect(
691
955
  root.ensureLoaded({
956
+ // @ts-expect-error missing required ref
692
957
  resolve: { profile: true },
693
958
  }),
694
959
  ).rejects.toThrow("Failed to deeply load CoValue " + root.id);
695
960
  });
696
961
 
697
962
  test("should not throw when calling ensureLoaded a record with a deleted ref", async () => {
698
- class JazzProfile extends CoMap {
699
- firstName = co.string;
700
- }
963
+ const JazzProfile = co.map({
964
+ name: z.string(),
965
+ firstName: z.string(),
966
+ });
701
967
 
702
- class JazzySnapStore extends CoMap.Record(co.ref(JazzProfile)) {}
968
+ const JazzySnapStore = co.record(z.string(), JazzProfile);
703
969
 
704
970
  const me = await Account.create({
705
971
  creationProps: { name: "Tester McTesterson" },
@@ -708,7 +974,7 @@ test("should not throw when calling ensureLoaded a record with a deleted ref", a
708
974
 
709
975
  const root = JazzySnapStore.create(
710
976
  {
711
- profile: JazzProfile.create({ firstName: "John" }, me),
977
+ profile: JazzProfile.create({ name: "John", firstName: "John" }, me),
712
978
  },
713
979
  me,
714
980
  );