cojson 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 (52) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +15 -0
  3. package/dist/coValues/coList.d.ts +10 -4
  4. package/dist/coValues/coList.d.ts.map +1 -1
  5. package/dist/coValues/coList.js +30 -4
  6. package/dist/coValues/coList.js.map +1 -1
  7. package/dist/coValues/coMap.d.ts +3 -2
  8. package/dist/coValues/coMap.d.ts.map +1 -1
  9. package/dist/coValues/coMap.js +8 -10
  10. package/dist/coValues/coMap.js.map +1 -1
  11. package/dist/coValues/coPlainText.d.ts +2 -1
  12. package/dist/coValues/coPlainText.d.ts.map +1 -1
  13. package/dist/coValues/coPlainText.js +5 -2
  14. package/dist/coValues/coPlainText.js.map +1 -1
  15. package/dist/coValues/group.d.ts +7 -3
  16. package/dist/coValues/group.d.ts.map +1 -1
  17. package/dist/coValues/group.js +70 -24
  18. package/dist/coValues/group.js.map +1 -1
  19. package/dist/storage/sqlite/client.d.ts +3 -3
  20. package/dist/storage/sqlite/client.d.ts.map +1 -1
  21. package/dist/storage/sqlite/client.js +1 -1
  22. package/dist/storage/sqlite/client.js.map +1 -1
  23. package/dist/storage/sqliteAsync/client.d.ts +3 -3
  24. package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
  25. package/dist/storage/sqliteAsync/client.js +1 -1
  26. package/dist/storage/sqliteAsync/client.js.map +1 -1
  27. package/dist/storage/storageAsync.d.ts.map +1 -1
  28. package/dist/storage/storageAsync.js +7 -7
  29. package/dist/storage/storageAsync.js.map +1 -1
  30. package/dist/storage/storageSync.d.ts.map +1 -1
  31. package/dist/storage/storageSync.js +7 -7
  32. package/dist/storage/storageSync.js.map +1 -1
  33. package/dist/storage/types.d.ts +18 -14
  34. package/dist/storage/types.d.ts.map +1 -1
  35. package/dist/tests/coList.test.js +28 -1
  36. package/dist/tests/coList.test.js.map +1 -1
  37. package/dist/tests/group.parentGroupCache.test.d.ts +2 -0
  38. package/dist/tests/group.parentGroupCache.test.d.ts.map +1 -0
  39. package/dist/tests/group.parentGroupCache.test.js +216 -0
  40. package/dist/tests/group.parentGroupCache.test.js.map +1 -0
  41. package/package.json +3 -3
  42. package/src/coValues/coList.ts +41 -8
  43. package/src/coValues/coMap.ts +15 -11
  44. package/src/coValues/coPlainText.ts +6 -2
  45. package/src/coValues/group.ts +99 -24
  46. package/src/storage/sqlite/client.ts +6 -3
  47. package/src/storage/sqliteAsync/client.ts +8 -3
  48. package/src/storage/storageAsync.ts +8 -5
  49. package/src/storage/storageSync.ts +8 -5
  50. package/src/storage/types.ts +43 -37
  51. package/src/tests/coList.test.ts +36 -3
  52. package/src/tests/group.parentGroupCache.test.ts +293 -0
@@ -0,0 +1,293 @@
1
+ import { beforeEach, describe, expect, test } from "vitest";
2
+ import {
3
+ setupTestAccount,
4
+ setupTestNode,
5
+ hotSleep,
6
+ loadCoValueOrFail,
7
+ } from "./testUtils";
8
+
9
+ let jazzCloud: ReturnType<typeof setupTestNode>;
10
+
11
+ beforeEach(async () => {
12
+ jazzCloud = setupTestNode({ isSyncServer: true });
13
+ });
14
+
15
+ describe("Parent Group Cache", () => {
16
+ describe("Property 1: Parent group cache update correctness", () => {
17
+ test("cache contains correct entries after processing parent group reference transactions", async () => {
18
+ const account = await setupTestAccount({
19
+ connected: true,
20
+ });
21
+ const parentGroup = account.node.createGroup();
22
+ const childGroup = account.node.createGroup();
23
+
24
+ // Initially no parent groups
25
+ expect(childGroup.getParentGroups()).toEqual([]);
26
+
27
+ // Extend parent group
28
+ childGroup.extend(parentGroup);
29
+ expect(childGroup.getParentGroups()).toEqual([parentGroup]);
30
+ });
31
+
32
+ test("cache handles multiple updates to same parent group", async () => {
33
+ const account = await setupTestAccount({
34
+ connected: true,
35
+ });
36
+ const parentGroup = account.node.createGroup();
37
+ const childGroup = account.node.createGroup();
38
+
39
+ childGroup.extend(parentGroup);
40
+ expect(childGroup.getParentGroups()).toEqual([parentGroup]);
41
+
42
+ // Revoke and re-extend
43
+ childGroup.revokeExtend(parentGroup);
44
+ expect(childGroup.getParentGroups()).toEqual([]);
45
+
46
+ childGroup.extend(parentGroup, "admin");
47
+ expect(childGroup.getParentGroups()).toEqual([parentGroup]);
48
+ });
49
+
50
+ test("cache correctly handles empty state (no parent groups)", async () => {
51
+ const account = await setupTestAccount({
52
+ connected: true,
53
+ });
54
+ const group = account.node.createGroup();
55
+
56
+ expect(group.getParentGroups()).toEqual([]);
57
+ });
58
+
59
+ test("cache only contains parent groups that have reference transactions", async () => {
60
+ const account = await setupTestAccount({
61
+ connected: true,
62
+ });
63
+ const parentGroup1 = account.node.createGroup();
64
+ const parentGroup2 = account.node.createGroup();
65
+ const childGroup = account.node.createGroup();
66
+
67
+ // Only extend one parent group
68
+ childGroup.extend(parentGroup1);
69
+
70
+ const parentGroups = childGroup.getParentGroups();
71
+ expect(parentGroups).toHaveLength(1);
72
+ expect(parentGroups[0]?.id).toEqual(parentGroup1.id);
73
+ });
74
+
75
+ test("cache handles multiple parent groups", async () => {
76
+ const account = await setupTestAccount({
77
+ connected: true,
78
+ });
79
+ const parentGroup1 = account.node.createGroup();
80
+ const parentGroup2 = account.node.createGroup();
81
+ const parentGroup3 = account.node.createGroup();
82
+ const childGroup = account.node.createGroup();
83
+
84
+ childGroup.extend(parentGroup1);
85
+ childGroup.extend(parentGroup2);
86
+ childGroup.extend(parentGroup3);
87
+
88
+ const parentGroups = childGroup.getParentGroups();
89
+ expect(parentGroups).toHaveLength(3);
90
+ expect(parentGroups.map((g) => g.id).sort()).toEqual(
91
+ [parentGroup1.id, parentGroup2.id, parentGroup3.id].sort(),
92
+ );
93
+ });
94
+ });
95
+
96
+ describe("Property 2: Parent group cache chronological ordering", () => {
97
+ test("multiple parent group changes are stored in chronological order", async () => {
98
+ const account = await setupTestAccount({
99
+ connected: true,
100
+ });
101
+ const parentGroup = account.node.createGroup();
102
+ const childGroup = account.node.createGroup();
103
+
104
+ const t1 = hotSleep(10);
105
+ childGroup.extend(parentGroup, "reader");
106
+ const t2 = hotSleep(10);
107
+ childGroup.extend(parentGroup, "writer");
108
+ const t3 = hotSleep(10);
109
+ childGroup.extend(parentGroup, "admin");
110
+
111
+ // Check time travel queries return correct historical states
112
+ expect(childGroup.atTime(t1).getParentGroups()).toEqual([]);
113
+ expect(childGroup.atTime(t2).getParentGroups()).toEqual([
114
+ parentGroup.atTime(t2),
115
+ ]);
116
+ expect(childGroup.atTime(t3).getParentGroups()).toEqual([
117
+ parentGroup.atTime(t3),
118
+ ]);
119
+
120
+ // Current state should have admin role
121
+ expect(childGroup.getParentGroups()).toEqual([parentGroup]);
122
+ });
123
+
124
+ test("out-of-order transaction processing maintains chronological order", async () => {
125
+ const account = await setupTestAccount({
126
+ connected: true,
127
+ });
128
+ const parentGroup = account.node.createGroup();
129
+ const childGroup = account.node.createGroup();
130
+
131
+ // Create transactions with different timestamps
132
+ const t1 = hotSleep(10);
133
+ const t2 = hotSleep(10);
134
+
135
+ childGroup.core.makeTransaction(
136
+ [
137
+ {
138
+ op: "set",
139
+ key: `parent_${parentGroup.id}`,
140
+ value: "revoked",
141
+ },
142
+ ],
143
+ "private",
144
+ undefined,
145
+ t2,
146
+ );
147
+
148
+ childGroup.core.makeTransaction(
149
+ [
150
+ {
151
+ op: "set",
152
+ key: `parent_${parentGroup.id}`,
153
+ value: "extend",
154
+ },
155
+ ],
156
+ "private",
157
+ undefined,
158
+ t1,
159
+ );
160
+ // Verify chronological ordering through time travel
161
+ const groupAtT1 = childGroup.atTime(t1);
162
+ const groupAtT2 = childGroup.atTime(t2);
163
+
164
+ expect(groupAtT1.getParentGroups()).toEqual([parentGroup.atTime(t1)]);
165
+ expect(groupAtT2.getParentGroups()).toEqual([]);
166
+ });
167
+
168
+ test("chronological ordering after rebuild", async () => {
169
+ const account = await setupTestAccount({
170
+ connected: true,
171
+ });
172
+ const parentGroup = account.node.createGroup();
173
+ const childGroup = account.node.createGroup();
174
+
175
+ const t1 = hotSleep(10);
176
+ childGroup.extend(parentGroup, "reader");
177
+ const t2 = hotSleep(10);
178
+ childGroup.extend(parentGroup, "writer");
179
+
180
+ // Rebuild should maintain chronological order
181
+ childGroup.rebuildFromCore();
182
+
183
+ const groupAtT1 = childGroup.atTime(t1);
184
+ const groupAtT2 = childGroup.atTime(t2);
185
+
186
+ expect(groupAtT1.getParentGroups()).toEqual([]);
187
+ expect(groupAtT2.getParentGroups()).toEqual([parentGroup.atTime(t2)]);
188
+ expect(childGroup.getParentGroups()).toEqual([parentGroup]);
189
+ });
190
+ });
191
+
192
+ describe("Property 3: Rebuild round-trip", () => {
193
+ test("cache is cleared after rebuildFromCore", async () => {
194
+ const account = await setupTestAccount({
195
+ connected: true,
196
+ });
197
+ const parentGroup = account.node.createGroup();
198
+ const childGroup = account.node.createGroup();
199
+
200
+ childGroup.extend(parentGroup);
201
+ expect(childGroup.getParentGroups()).toEqual([parentGroup]);
202
+
203
+ // Rebuild should clear cache and rebuild it
204
+ childGroup.rebuildFromCore();
205
+
206
+ // Cache should be rebuilt and still work
207
+ expect(childGroup.getParentGroups()).toEqual([parentGroup]);
208
+ });
209
+
210
+ test("cache is rebuilt correctly after clear", async () => {
211
+ const account = await setupTestAccount({
212
+ connected: true,
213
+ });
214
+ const parentGroup1 = account.node.createGroup();
215
+ const parentGroup2 = account.node.createGroup();
216
+ const childGroup = account.node.createGroup();
217
+
218
+ childGroup.extend(parentGroup1);
219
+ childGroup.extend(parentGroup2);
220
+
221
+ expect(childGroup.getParentGroups()).toHaveLength(2);
222
+
223
+ // Rebuild
224
+ childGroup.rebuildFromCore();
225
+
226
+ // Cache should be rebuilt with all parent groups
227
+ const parentGroups = childGroup.getParentGroups();
228
+ expect(parentGroups).toHaveLength(2);
229
+ expect(parentGroups.map((g) => g.id).sort()).toEqual(
230
+ [parentGroup1.id, parentGroup2.id].sort(),
231
+ );
232
+ });
233
+
234
+ test("parent group lookups work correctly after rebuild", async () => {
235
+ const alice = await setupTestAccount({
236
+ connected: true,
237
+ });
238
+ const bob = await setupTestAccount({
239
+ connected: true,
240
+ });
241
+ const parentGroup = alice.node.createGroup();
242
+ const childGroup = alice.node.createGroup();
243
+
244
+ parentGroup.addMember(
245
+ await loadCoValueOrFail(alice.node, bob.accountID),
246
+ "writer",
247
+ );
248
+
249
+ childGroup.extend(parentGroup);
250
+
251
+ // Check role inheritance before rebuild
252
+ expect(childGroup.roleOf(bob.accountID)).toEqual("writer");
253
+
254
+ // Rebuild
255
+ childGroup.rebuildFromCore();
256
+
257
+ // Role inheritance should still work after rebuild
258
+ expect(childGroup.roleOf(bob.accountID)).toEqual("writer");
259
+ expect(childGroup.getParentGroups()).toEqual([parentGroup]);
260
+ });
261
+
262
+ test("rebuild maintains chronological ordering", async () => {
263
+ const alice = await setupTestAccount({
264
+ connected: true,
265
+ });
266
+ const bob = await setupTestAccount({
267
+ connected: true,
268
+ });
269
+ const parentGroup = alice.node.createGroup();
270
+ const childGroup = alice.node.createGroup();
271
+
272
+ parentGroup.addMember(
273
+ await loadCoValueOrFail(alice.node, bob.accountID),
274
+ "writer",
275
+ );
276
+
277
+ const t1 = hotSleep(10);
278
+ childGroup.extend(parentGroup, "reader");
279
+ const t2 = hotSleep(10);
280
+ childGroup.extend(parentGroup, "writer");
281
+
282
+ // Rebuild
283
+ childGroup.rebuildFromCore();
284
+
285
+ // Chronological ordering should be maintained
286
+ expect(childGroup.atTime(t1).roleOf(bob.accountID)).toEqual(undefined);
287
+ expect(childGroup.atTime(t2).roleOf(bob.accountID)).toEqual("reader");
288
+ expect(childGroup.atTime(Date.now()).roleOf(bob.accountID)).toEqual(
289
+ "writer",
290
+ );
291
+ });
292
+ });
293
+ });