jazz-tools 0.13.16 → 0.13.18

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 (83) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/CHANGELOG.md +19 -0
  3. package/dist/{chunk-GIZWJXSR.js → chunk-ITSHLDQB.js} +731 -600
  4. package/dist/chunk-ITSHLDQB.js.map +1 -0
  5. package/dist/coValues/account.d.ts +5 -4
  6. package/dist/coValues/account.d.ts.map +1 -1
  7. package/dist/coValues/coFeed.d.ts +3 -3
  8. package/dist/coValues/coFeed.d.ts.map +1 -1
  9. package/dist/coValues/coList.d.ts +1 -0
  10. package/dist/coValues/coList.d.ts.map +1 -1
  11. package/dist/coValues/coMap.d.ts +4 -2
  12. package/dist/coValues/coMap.d.ts.map +1 -1
  13. package/dist/coValues/coPlainText.d.ts +2 -2
  14. package/dist/coValues/coPlainText.d.ts.map +1 -1
  15. package/dist/coValues/deepLoading.d.ts +1 -9
  16. package/dist/coValues/deepLoading.d.ts.map +1 -1
  17. package/dist/coValues/extensions/imageDef.d.ts.map +1 -1
  18. package/dist/coValues/group.d.ts.map +1 -1
  19. package/dist/coValues/inbox.d.ts.map +1 -1
  20. package/dist/coValues/interfaces.d.ts +4 -1
  21. package/dist/coValues/interfaces.d.ts.map +1 -1
  22. package/dist/implementation/createContext.d.ts.map +1 -1
  23. package/dist/implementation/refs.d.ts +5 -10
  24. package/dist/implementation/refs.d.ts.map +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/internal.d.ts +1 -1
  27. package/dist/internal.d.ts.map +1 -1
  28. package/dist/subscribe/CoValueCoreSubscription.d.ts +14 -0
  29. package/dist/subscribe/CoValueCoreSubscription.d.ts.map +1 -0
  30. package/dist/subscribe/JazzError.d.ts +16 -0
  31. package/dist/subscribe/JazzError.d.ts.map +1 -0
  32. package/dist/subscribe/SubscriptionScope.d.ts +42 -0
  33. package/dist/subscribe/SubscriptionScope.d.ts.map +1 -0
  34. package/dist/subscribe/index.d.ts +21 -0
  35. package/dist/subscribe/index.d.ts.map +1 -0
  36. package/dist/subscribe/types.d.ts +12 -0
  37. package/dist/subscribe/types.d.ts.map +1 -0
  38. package/dist/subscribe/utils.d.ts +10 -0
  39. package/dist/subscribe/utils.d.ts.map +1 -0
  40. package/dist/testing.js +2 -2
  41. package/dist/testing.js.map +1 -1
  42. package/dist/tests/coMap.record.test.d.ts +2 -0
  43. package/dist/tests/coMap.record.test.d.ts.map +1 -0
  44. package/dist/tests/utils.d.ts +2 -2
  45. package/dist/tests/utils.d.ts.map +1 -1
  46. package/package.json +2 -2
  47. package/src/coValues/account.ts +43 -31
  48. package/src/coValues/coFeed.ts +28 -13
  49. package/src/coValues/coList.ts +13 -17
  50. package/src/coValues/coMap.ts +72 -80
  51. package/src/coValues/coPlainText.ts +13 -2
  52. package/src/coValues/deepLoading.ts +4 -277
  53. package/src/coValues/extensions/imageDef.ts +1 -7
  54. package/src/coValues/group.ts +7 -6
  55. package/src/coValues/inbox.ts +4 -11
  56. package/src/coValues/interfaces.ts +54 -111
  57. package/src/implementation/createContext.ts +3 -4
  58. package/src/implementation/invites.ts +2 -2
  59. package/src/implementation/refs.ts +30 -121
  60. package/src/internal.ts +1 -2
  61. package/src/subscribe/CoValueCoreSubscription.ts +71 -0
  62. package/src/subscribe/JazzError.ts +48 -0
  63. package/src/subscribe/SubscriptionScope.ts +523 -0
  64. package/src/subscribe/index.ts +82 -0
  65. package/src/subscribe/types.ts +7 -0
  66. package/src/subscribe/utils.ts +36 -0
  67. package/src/testing.ts +1 -1
  68. package/src/tests/ContextManager.test.ts +13 -9
  69. package/src/tests/coFeed.test.ts +104 -4
  70. package/src/tests/coList.test.ts +304 -115
  71. package/src/tests/coMap.record.test.ts +325 -0
  72. package/src/tests/coMap.test.ts +718 -645
  73. package/src/tests/coPlainText.test.ts +2 -2
  74. package/src/tests/createContext.test.ts +8 -8
  75. package/src/tests/deepLoading.test.ts +8 -34
  76. package/src/tests/groupsAndAccounts.test.ts +6 -4
  77. package/src/tests/subscribe.test.ts +357 -42
  78. package/src/tests/utils.ts +8 -6
  79. package/tsconfig.json +2 -1
  80. package/dist/chunk-GIZWJXSR.js.map +0 -1
  81. package/dist/implementation/subscriptionScope.d.ts +0 -34
  82. package/dist/implementation/subscriptionScope.d.ts.map +0 -1
  83. package/src/implementation/subscriptionScope.ts +0 -165
@@ -0,0 +1,325 @@
1
+ import { WasmCrypto } from "cojson/crypto/WasmCrypto";
2
+ import {
3
+ assert,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ expectTypeOf,
8
+ it,
9
+ test,
10
+ vi,
11
+ } from "vitest";
12
+ import { RefsToResolve } from "../coValues/deepLoading.js";
13
+ import { ID } from "../coValues/interfaces.js";
14
+ import { Group, Resolved, subscribeToCoValue } from "../exports.js";
15
+ import { Account, CoMap, co, cojsonInternals } from "../index.js";
16
+ import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
17
+ import { waitFor } from "./utils.js";
18
+
19
+ const { connectedPeers } = cojsonInternals;
20
+
21
+ const Crypto = await WasmCrypto.create();
22
+
23
+ beforeEach(async () => {
24
+ await setupJazzTestSync();
25
+
26
+ await createJazzTestAccount({
27
+ isCurrentActiveAccount: true,
28
+ creationProps: { name: "Hermes Puggington" },
29
+ });
30
+ });
31
+
32
+ describe("CoMap.Record", async () => {
33
+ describe("init", () => {
34
+ test("create a Record with basic property access", () => {
35
+ class Person extends CoMap.Record(co.string) {}
36
+
37
+ const person = Person.create({
38
+ name: "John",
39
+ age: "20",
40
+ });
41
+
42
+ expect(person.name).toEqual("John");
43
+ expect(person.age).toEqual("20");
44
+ expect(Object.keys(person)).toEqual(["name", "age"]);
45
+ });
46
+
47
+ test("property existence", () => {
48
+ class Person extends CoMap.Record(co.string) {}
49
+
50
+ const person = Person.create({ name: "John" });
51
+
52
+ expect("name" in person).toEqual(true);
53
+ expect("age" in person).toEqual(false);
54
+ });
55
+
56
+ test("create a Record with an account as owner", () => {
57
+ class Person extends CoMap.Record(co.string) {}
58
+
59
+ const person = Person.create({ name: "John" }, Account.getMe());
60
+
61
+ expect(person.name).toEqual("John");
62
+ expect(person._raw.get("name")).toEqual("John");
63
+ });
64
+
65
+ test("create a Record with a group as owner", () => {
66
+ class Person extends CoMap.Record(co.string) {}
67
+
68
+ const person = Person.create({ name: "John" }, Group.create());
69
+
70
+ expect(person.name).toEqual("John");
71
+ expect(person._raw.get("name")).toEqual("John");
72
+ });
73
+
74
+ test("Empty schema", () => {
75
+ class EmptyRecord extends CoMap.Record(co.string) {}
76
+ const emptyRecord = EmptyRecord.create({});
77
+
78
+ expect(Object.keys(emptyRecord)).toEqual([]);
79
+ });
80
+
81
+ test("Record with reference", () => {
82
+ class Dog extends CoMap {
83
+ name = co.string;
84
+ breed = co.string;
85
+ }
86
+
87
+ class Person
88
+ extends CoMap.Record(co.ref(Dog, { optional: true }))
89
+ implements Record<string, Dog | null | undefined> {}
90
+
91
+ const person = Person.create({
92
+ pet1: Dog.create({ name: "Rex", breed: "Labrador" }),
93
+ pet2: Dog.create({ name: "Fido", breed: "Poodle" }),
94
+ });
95
+
96
+ expect(person.pet1?.name).toEqual("Rex");
97
+ expect(person.pet1?.breed).toEqual("Labrador");
98
+ expect(person.pet2?.name).toEqual("Fido");
99
+ expect(person.pet2?.breed).toEqual("Poodle");
100
+ });
101
+ });
102
+
103
+ describe("Mutation", () => {
104
+ test("change a primitive value", () => {
105
+ class Person extends CoMap.Record(co.string) {}
106
+
107
+ const person = Person.create({ name: "John" });
108
+
109
+ person.name = "Jane";
110
+
111
+ expect(person.name).toEqual("Jane");
112
+ });
113
+
114
+ test("delete a value", () => {
115
+ class Person extends CoMap.Record(co.string) {}
116
+
117
+ const person = Person.create({ name: "John", age: "20" });
118
+
119
+ delete person.age;
120
+
121
+ expect(person.name).toEqual("John");
122
+ expect("age" in person).toEqual(false);
123
+
124
+ expect(person.toJSON()).toEqual({
125
+ _type: "CoMap",
126
+ id: person.id,
127
+ name: "John",
128
+ });
129
+ });
130
+
131
+ test("update a reference", () => {
132
+ class Dog extends CoMap {
133
+ name = co.string;
134
+ }
135
+
136
+ class Person
137
+ extends CoMap.Record(co.ref(Dog, { optional: true }))
138
+ implements Record<string, Dog | null | undefined> {}
139
+
140
+ const person = Person.create({
141
+ pet1: Dog.create({ name: "Rex" }),
142
+ });
143
+
144
+ person.pet1 = Dog.create({ name: "Fido" });
145
+
146
+ expect(person.pet1?.name).toEqual("Fido");
147
+ });
148
+
149
+ test("changes should be listed in _edits", () => {
150
+ class Person extends CoMap.Record(co.string) {}
151
+
152
+ const person = Person.create({ name: "John" });
153
+
154
+ const me = Account.getMe();
155
+
156
+ person.name = "Jane";
157
+
158
+ const edits = person._edits.name?.all;
159
+ expect(edits).toEqual([
160
+ {
161
+ by: expect.objectContaining({ _type: "Account", id: me.id }),
162
+ value: "John",
163
+ key: "name",
164
+ ref: undefined,
165
+ madeAt: expect.any(Date),
166
+ },
167
+ {
168
+ by: expect.objectContaining({ _type: "Account", id: me.id }),
169
+ value: "Jane",
170
+ key: "name",
171
+ ref: undefined,
172
+ madeAt: expect.any(Date),
173
+ },
174
+ ]);
175
+ });
176
+ });
177
+
178
+ describe("Record resolution", async () => {
179
+ test("loading a locally available record with deep resolve", async () => {
180
+ class Dog extends CoMap {
181
+ name = co.string;
182
+ breed = co.string;
183
+ }
184
+
185
+ class Person
186
+ extends CoMap.Record(co.ref(Dog, { optional: true }))
187
+ implements Record<string, Dog | null | undefined> {}
188
+
189
+ const person = Person.create({
190
+ pet1: Dog.create({ name: "Rex", breed: "Labrador" }),
191
+ pet2: Dog.create({ name: "Fido", breed: "Poodle" }),
192
+ });
193
+
194
+ const loadedPerson = await Person.load(person.id as ID<Person>, {
195
+ resolve: {
196
+ $each: true,
197
+ },
198
+ });
199
+
200
+ assert(loadedPerson);
201
+ expect(loadedPerson.pet1?.name).toEqual("Rex");
202
+ expect(loadedPerson.pet2?.name).toEqual("Fido");
203
+ });
204
+
205
+ test("loading a locally available record using autoload for the refs", async () => {
206
+ class Dog extends CoMap {
207
+ name = co.string;
208
+ breed = co.string;
209
+ }
210
+
211
+ class Person
212
+ extends CoMap.Record(co.ref(Dog, { optional: true }))
213
+ implements Record<string, Dog | null | undefined> {}
214
+
215
+ const person = Person.create({
216
+ pet1: Dog.create({ name: "Rex", breed: "Labrador" }),
217
+ pet2: Dog.create({ name: "Fido", breed: "Poodle" }),
218
+ });
219
+
220
+ const loadedPerson = await Person.load(person.id as ID<Person>);
221
+
222
+ assert(loadedPerson);
223
+ expect(loadedPerson.pet1?.name).toEqual("Rex");
224
+ expect(loadedPerson.pet2?.name).toEqual("Fido");
225
+ });
226
+
227
+ test("subscription on a locally available record with deep resolve", async () => {
228
+ class Dog extends CoMap {
229
+ name = co.string;
230
+ breed = co.string;
231
+ }
232
+
233
+ class Person
234
+ extends CoMap.Record(co.ref(Dog, { optional: true }))
235
+ implements Record<string, Dog | null | undefined> {}
236
+
237
+ const person = Person.create({
238
+ pet1: Dog.create({ name: "Rex", breed: "Labrador" }),
239
+ });
240
+
241
+ const updates: Resolved<Person, { $each: true }>[] = [];
242
+ const spy = vi.fn((person) => updates.push(person));
243
+
244
+ Person.subscribe(
245
+ person.id as ID<Person>,
246
+ {
247
+ resolve: {
248
+ $each: true,
249
+ },
250
+ },
251
+ spy,
252
+ );
253
+
254
+ expect(spy).not.toHaveBeenCalled();
255
+
256
+ await waitFor(() => expect(spy).toHaveBeenCalled());
257
+
258
+ expect(spy).toHaveBeenCalledTimes(1);
259
+
260
+ expect(updates[0]?.pet1?.name).toEqual("Rex");
261
+
262
+ person.pet1 = Dog.create({ name: "Fido", breed: "Poodle" });
263
+
264
+ await waitFor(() => expect(spy).toHaveBeenCalledTimes(2));
265
+
266
+ expect(updates[1]?.pet1?.name).toEqual("Fido");
267
+
268
+ expect(spy).toHaveBeenCalledTimes(2);
269
+ });
270
+ });
271
+
272
+ describe("Record Typescript validation", async () => {
273
+ const me = await Account.create({
274
+ creationProps: { name: "Hermes Puggington" },
275
+ crypto: Crypto,
276
+ });
277
+
278
+ class NestedRecord extends CoMap {
279
+ value = co.string;
280
+ }
281
+
282
+ test("Is not ok to pass null into a required ref", () => {
283
+ class TestRecord
284
+ extends CoMap.Record(co.ref(NestedRecord))
285
+ implements Record<string, NestedRecord | null> {}
286
+
287
+ expectTypeOf<typeof TestRecord.create>().toBeCallableWith(
288
+ {
289
+ key1: NestedRecord.create({ value: "" }, { owner: me }),
290
+ key2: NestedRecord.create({ value: "" }, { owner: me }),
291
+ },
292
+ { owner: me },
293
+ );
294
+
295
+ expectTypeOf<typeof TestRecord.create>().toBeCallableWith(
296
+ {
297
+ key1: NestedRecord.create({ value: "" }, { owner: me }),
298
+ key2: null,
299
+ },
300
+ { owner: me },
301
+ );
302
+ });
303
+
304
+ test("Is ok to omit optional fields", () => {
305
+ class TestRecord
306
+ extends CoMap.Record(co.ref(NestedRecord, { optional: true }))
307
+ implements Record<string, NestedRecord | null | undefined> {}
308
+
309
+ expectTypeOf<typeof TestRecord.create>().toBeCallableWith(
310
+ {
311
+ key1: NestedRecord.create({ value: "" }, { owner: me }),
312
+ },
313
+ { owner: me },
314
+ );
315
+
316
+ expectTypeOf<typeof TestRecord.create>().toBeCallableWith(
317
+ {
318
+ key1: NestedRecord.create({ value: "" }, { owner: me }),
319
+ key2: null,
320
+ },
321
+ { owner: me },
322
+ );
323
+ });
324
+ });
325
+ });