jazz-tools 0.18.27 → 0.18.29

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 (51) hide show
  1. package/.turbo/turbo-build.log +47 -47
  2. package/CHANGELOG.md +23 -0
  3. package/dist/{chunk-ZIAN4UY5.js → chunk-F55R554M.js} +171 -120
  4. package/dist/chunk-F55R554M.js.map +1 -0
  5. package/dist/index.js +3 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/inspector/{custom-element-A7UAELEG.js → custom-element-35MDW4SW.js} +1840 -1837
  8. package/dist/inspector/{custom-element-A7UAELEG.js.map → custom-element-35MDW4SW.js.map} +1 -1
  9. package/dist/inspector/custom-element.d.ts.map +1 -1
  10. package/dist/inspector/index.d.ts +1 -1
  11. package/dist/inspector/index.d.ts.map +1 -1
  12. package/dist/inspector/index.js +0 -1
  13. package/dist/inspector/index.js.map +1 -1
  14. package/dist/inspector/register-custom-element.js +1 -1
  15. package/dist/react-core/hooks.d.ts +4 -0
  16. package/dist/react-core/hooks.d.ts.map +1 -1
  17. package/dist/react-core/index.js +5 -0
  18. package/dist/react-core/index.js.map +1 -1
  19. package/dist/testing.js +1 -1
  20. package/dist/tools/coValues/coList.d.ts +11 -3
  21. package/dist/tools/coValues/coList.d.ts.map +1 -1
  22. package/dist/tools/coValues/coMap.d.ts +21 -5
  23. package/dist/tools/coValues/coMap.d.ts.map +1 -1
  24. package/dist/tools/coValues/group.d.ts +2 -2
  25. package/dist/tools/coValues/group.d.ts.map +1 -1
  26. package/dist/tools/coValues/inbox.d.ts.map +1 -1
  27. package/dist/tools/coValues/interfaces.d.ts +17 -1
  28. package/dist/tools/coValues/interfaces.d.ts.map +1 -1
  29. package/dist/tools/exports.d.ts +1 -1
  30. package/dist/tools/exports.d.ts.map +1 -1
  31. package/dist/tools/tests/coList.unique.test.d.ts +2 -0
  32. package/dist/tools/tests/coList.unique.test.d.ts.map +1 -0
  33. package/dist/tools/tests/coMap.unique.test.d.ts +2 -0
  34. package/dist/tools/tests/coMap.unique.test.d.ts.map +1 -0
  35. package/package.json +5 -5
  36. package/src/inspector/custom-element.tsx +4 -0
  37. package/src/inspector/index.tsx +0 -2
  38. package/src/react-core/hooks.ts +8 -0
  39. package/src/react-core/tests/useAccount.test.ts +61 -1
  40. package/src/react-core/tests/usePassPhraseAuth.test.ts +74 -2
  41. package/src/tools/coValues/coList.ts +38 -35
  42. package/src/tools/coValues/coMap.ts +38 -38
  43. package/src/tools/coValues/group.ts +5 -1
  44. package/src/tools/coValues/inbox.ts +4 -3
  45. package/src/tools/coValues/interfaces.ts +119 -0
  46. package/src/tools/exports.ts +1 -0
  47. package/src/tools/tests/coList.test.ts +0 -190
  48. package/src/tools/tests/coList.unique.test.ts +244 -0
  49. package/src/tools/tests/coMap.test.ts +0 -433
  50. package/src/tools/tests/coMap.unique.test.ts +579 -0
  51. package/dist/chunk-ZIAN4UY5.js.map +0 -1
@@ -0,0 +1,579 @@
1
+ import { cojsonInternals } from "cojson";
2
+ import { assert, beforeEach, describe, expect, test } from "vitest";
3
+ import {
4
+ setupJazzTestSync,
5
+ createJazzTestAccount,
6
+ runWithoutActiveAccount,
7
+ } from "../testing";
8
+ import {
9
+ Group,
10
+ co,
11
+ activeAccountContext,
12
+ unstable_loadUnique,
13
+ } from "../internal";
14
+ import { z } from "../exports";
15
+
16
+ beforeEach(async () => {
17
+ cojsonInternals.CO_VALUE_LOADING_CONFIG.RETRY_DELAY = 1000;
18
+
19
+ await setupJazzTestSync();
20
+
21
+ await createJazzTestAccount({
22
+ isCurrentActiveAccount: true,
23
+ creationProps: { name: "Hermes Puggington" },
24
+ });
25
+ });
26
+
27
+ describe("Creating and finding unique CoMaps", async () => {
28
+ test("Creating and finding unique CoMaps", async () => {
29
+ const group = Group.create();
30
+
31
+ const Person = co.map({
32
+ name: z.string(),
33
+ _height: z.number(),
34
+ birthday: z.date(),
35
+ color: z.string(),
36
+ });
37
+
38
+ const alice = Person.create(
39
+ {
40
+ name: "Alice",
41
+ _height: 100,
42
+ birthday: new Date("1990-01-01"),
43
+ color: "red",
44
+ },
45
+ { owner: group, unique: { name: "Alice" } },
46
+ );
47
+
48
+ const foundAlice = await Person.loadUnique(
49
+ { name: "Alice" },
50
+ group.$jazz.id,
51
+ );
52
+ expect(foundAlice).toEqual(alice);
53
+ });
54
+
55
+ test("should work with unstable_loadUnique", async () => {
56
+ const group = Group.create();
57
+
58
+ const Person = co.map({
59
+ name: z.string(),
60
+ _height: z.number(),
61
+ birthday: z.date(),
62
+ color: z.string(),
63
+ });
64
+
65
+ const alice = Person.create(
66
+ {
67
+ name: "Alice",
68
+ _height: 100,
69
+ birthday: new Date("1990-01-01"),
70
+ color: "red",
71
+ },
72
+ { owner: group, unique: { name: "Alice" } },
73
+ );
74
+
75
+ const foundAlice = await unstable_loadUnique(Person, {
76
+ unique: { name: "Alice" },
77
+ owner: group,
78
+ });
79
+ expect(foundAlice).toEqual(alice);
80
+ });
81
+
82
+ test("should upsert with unstable_loadUnique", async () => {
83
+ const group = Group.create();
84
+
85
+ const Person = co.map({
86
+ name: z.string(),
87
+ _height: z.number(),
88
+ birthday: z.date(),
89
+ color: z.string(),
90
+ });
91
+
92
+ const alice = await unstable_loadUnique(Person, {
93
+ unique: { name: "Alice" },
94
+ onCreateWhenMissing: () => {
95
+ Person.create(
96
+ {
97
+ name: "Alice",
98
+ _height: 100,
99
+ birthday: new Date("1990-01-01"),
100
+ color: "red",
101
+ },
102
+ { owner: group, unique: { name: "Alice" } },
103
+ );
104
+ },
105
+ owner: group,
106
+ });
107
+ expect(alice?.name).toEqual("Alice");
108
+ });
109
+
110
+ test("manual upserting pattern", async () => {
111
+ // Schema
112
+ const Event = co.map({
113
+ title: z.string(),
114
+ identifier: z.string(),
115
+ external_id: z.string(),
116
+ });
117
+
118
+ // Data
119
+ const sourceData = {
120
+ title: "Test Event Title",
121
+ identifier: "test-event-identifier",
122
+ _id: "test-event-external-id",
123
+ };
124
+ const workspace = Group.create();
125
+
126
+ // Pattern
127
+ let activeEvent = await Event.loadUnique(
128
+ { identifier: sourceData.identifier },
129
+ workspace.$jazz.id,
130
+ );
131
+ if (!activeEvent) {
132
+ activeEvent = Event.create(
133
+ {
134
+ title: sourceData.title,
135
+ identifier: sourceData.identifier,
136
+ external_id: sourceData._id,
137
+ },
138
+ workspace,
139
+ );
140
+ } else {
141
+ activeEvent.$jazz.applyDiff({
142
+ title: sourceData.title,
143
+ identifier: sourceData.identifier,
144
+ external_id: sourceData._id,
145
+ });
146
+ }
147
+ expect(activeEvent).toEqual({
148
+ title: sourceData.title,
149
+ identifier: sourceData.identifier,
150
+ external_id: sourceData._id,
151
+ });
152
+ });
153
+
154
+ test("upserting a non-existent value", async () => {
155
+ // Schema
156
+ const Event = co.map({
157
+ title: z.string(),
158
+ identifier: z.string(),
159
+ external_id: z.string(),
160
+ });
161
+
162
+ // Data
163
+ const sourceData = {
164
+ title: "Test Event Title",
165
+ identifier: "test-event-identifier",
166
+ _id: "test-event-external-id",
167
+ };
168
+ const workspace = Group.create();
169
+
170
+ // Upserting
171
+ const activeEvent = await Event.upsertUnique({
172
+ value: {
173
+ title: sourceData.title,
174
+ identifier: sourceData.identifier,
175
+ external_id: sourceData._id,
176
+ },
177
+ unique: sourceData.identifier,
178
+ owner: workspace,
179
+ });
180
+ expect(activeEvent).toEqual({
181
+ title: sourceData.title,
182
+ identifier: sourceData.identifier,
183
+ external_id: sourceData._id,
184
+ });
185
+ });
186
+
187
+ test("upserting a existent value without enough permissions should not throw", async () => {
188
+ const Event = co.map({
189
+ title: z.string(),
190
+ identifier: z.string(),
191
+ external_id: z.string(),
192
+ });
193
+
194
+ const sourceData = {
195
+ title: "Test Event Title",
196
+ identifier: "test-event-identifier",
197
+ _id: "test-event-external-id",
198
+ };
199
+ const workspace = Group.create();
200
+
201
+ const initialEvent = await Event.upsertUnique({
202
+ value: {
203
+ title: sourceData.title,
204
+ identifier: sourceData.identifier,
205
+ external_id: sourceData._id,
206
+ },
207
+ unique: sourceData.identifier,
208
+ owner: workspace,
209
+ });
210
+
211
+ const alice = await createJazzTestAccount({
212
+ isCurrentActiveAccount: true,
213
+ });
214
+
215
+ const workspaceOnAlice = await Group.load(workspace.$jazz.id, {
216
+ loadAs: alice,
217
+ });
218
+ assert(workspaceOnAlice);
219
+
220
+ const eventOnAlice = await Event.upsertUnique({
221
+ value: {
222
+ title: sourceData.title,
223
+ identifier: sourceData.identifier,
224
+ external_id: sourceData._id,
225
+ },
226
+ unique: sourceData.identifier,
227
+ owner: workspaceOnAlice,
228
+ });
229
+ expect(eventOnAlice).toBeNull();
230
+ });
231
+
232
+ test("upserting without an active account", async () => {
233
+ const account = activeAccountContext.get();
234
+
235
+ // Schema
236
+ const Event = co.map({
237
+ title: z.string(),
238
+ identifier: z.string(),
239
+ external_id: z.string(),
240
+ });
241
+
242
+ // Data
243
+ const sourceData = {
244
+ title: "Test Event Title",
245
+ identifier: "test-event-identifier",
246
+ _id: "test-event-external-id",
247
+ };
248
+
249
+ const activeEvent = await runWithoutActiveAccount(() => {
250
+ return Event.upsertUnique({
251
+ value: {
252
+ title: sourceData.title,
253
+ identifier: sourceData.identifier,
254
+ external_id: sourceData._id,
255
+ },
256
+ unique: sourceData.identifier,
257
+ owner: account,
258
+ });
259
+ });
260
+
261
+ expect(activeEvent).toEqual({
262
+ title: sourceData.title,
263
+ identifier: sourceData.identifier,
264
+ external_id: sourceData._id,
265
+ });
266
+
267
+ assert(activeEvent);
268
+
269
+ expect(activeEvent.$jazz.owner).toEqual(account);
270
+ });
271
+
272
+ test("upserting an existing value", async () => {
273
+ // Schema
274
+ const Event = co.map({
275
+ title: z.string(),
276
+ identifier: z.string(),
277
+ external_id: z.string(),
278
+ });
279
+
280
+ // Data
281
+ const oldSourceData = {
282
+ title: "Old Event Title",
283
+ identifier: "test-event-identifier",
284
+ _id: "test-event-external-id",
285
+ };
286
+ const newSourceData = {
287
+ title: "New Event Title",
288
+ identifier: "test-event-identifier",
289
+ _id: "test-event-external-id",
290
+ };
291
+ expect(oldSourceData.identifier).toEqual(newSourceData.identifier);
292
+ const workspace = Group.create();
293
+ const oldActiveEvent = Event.create(
294
+ {
295
+ title: oldSourceData.title,
296
+ identifier: oldSourceData.identifier,
297
+ external_id: oldSourceData._id,
298
+ },
299
+ workspace,
300
+ );
301
+
302
+ // Upserting
303
+ const activeEvent = await Event.upsertUnique({
304
+ value: {
305
+ title: newSourceData.title,
306
+ identifier: newSourceData.identifier,
307
+ external_id: newSourceData._id,
308
+ },
309
+ unique: newSourceData.identifier,
310
+ owner: workspace,
311
+ });
312
+ expect(activeEvent).toEqual({
313
+ title: newSourceData.title,
314
+ identifier: newSourceData.identifier,
315
+ external_id: newSourceData._id,
316
+ });
317
+ expect(activeEvent).not.toEqual(oldActiveEvent);
318
+ });
319
+
320
+ test("upserting a non-existent value with resolve", async () => {
321
+ const Project = co.map({
322
+ name: z.string(),
323
+ });
324
+ const Organisation = co.map({
325
+ name: z.string(),
326
+ projects: co.list(Project),
327
+ });
328
+ const workspace = Group.create();
329
+
330
+ const myOrg = await Organisation.upsertUnique({
331
+ value: {
332
+ name: "My organisation",
333
+ projects: co.list(Project).create(
334
+ [
335
+ Project.create(
336
+ {
337
+ name: "My project",
338
+ },
339
+ workspace,
340
+ ),
341
+ ],
342
+ workspace,
343
+ ),
344
+ },
345
+ unique: { name: "My organisation" },
346
+ owner: workspace,
347
+ resolve: {
348
+ projects: {
349
+ $each: true,
350
+ },
351
+ },
352
+ });
353
+ assert(myOrg);
354
+ expect(myOrg).not.toBeNull();
355
+ expect(myOrg.name).toEqual("My organisation");
356
+ expect(myOrg.projects.length).toBe(1);
357
+ expect(myOrg.projects[0]).toMatchObject({
358
+ name: "My project",
359
+ });
360
+ });
361
+
362
+ test("upserting an existing value with resolve", async () => {
363
+ const Project = co.map({
364
+ name: z.string(),
365
+ });
366
+ const Organisation = co.map({
367
+ name: z.string(),
368
+ projects: co.list(Project),
369
+ });
370
+ const workspace = Group.create();
371
+ const initialProject = await Project.upsertUnique({
372
+ value: {
373
+ name: "My project",
374
+ },
375
+ unique: { unique: "First project" },
376
+ owner: workspace,
377
+ });
378
+ assert(initialProject);
379
+ expect(initialProject).not.toBeNull();
380
+ expect(initialProject.name).toEqual("My project");
381
+
382
+ const myOrg = await Organisation.upsertUnique({
383
+ value: {
384
+ name: "My organisation",
385
+ projects: co.list(Project).create([initialProject], workspace),
386
+ },
387
+ unique: { name: "My organisation" },
388
+ owner: workspace,
389
+ resolve: {
390
+ projects: {
391
+ $each: true,
392
+ },
393
+ },
394
+ });
395
+ assert(myOrg);
396
+ expect(myOrg).not.toBeNull();
397
+ expect(myOrg.name).toEqual("My organisation");
398
+ expect(myOrg.projects.length).toBe(1);
399
+ expect(myOrg.projects.at(0)?.name).toEqual("My project");
400
+
401
+ const updatedProject = await Project.upsertUnique({
402
+ value: {
403
+ name: "My updated project",
404
+ },
405
+ unique: { unique: "First project" },
406
+ owner: workspace,
407
+ });
408
+
409
+ assert(updatedProject);
410
+ expect(updatedProject).not.toBeNull();
411
+ expect(updatedProject).toEqual(initialProject);
412
+ expect(updatedProject.name).toEqual("My updated project");
413
+ expect(myOrg.projects.length).toBe(1);
414
+ expect(myOrg.projects.at(0)?.name).toEqual("My updated project");
415
+ });
416
+
417
+ test("upserting a partially loaded value on an new value with resolve", async () => {
418
+ const Project = co.map({
419
+ name: z.string(),
420
+ });
421
+ const Organisation = co.map({
422
+ name: z.string(),
423
+ projects: co.list(Project),
424
+ });
425
+ const publicAccess = Group.create();
426
+ publicAccess.addMember("everyone", "writer");
427
+
428
+ const initialProject = await Project.upsertUnique({
429
+ value: {
430
+ name: "My project",
431
+ },
432
+ unique: { unique: "First project" },
433
+ owner: publicAccess,
434
+ });
435
+ assert(initialProject);
436
+ expect(initialProject).not.toBeNull();
437
+ expect(initialProject.name).toEqual("My project");
438
+
439
+ const fullProjectList = co
440
+ .list(Project)
441
+ .create([initialProject], publicAccess);
442
+
443
+ const account = await createJazzTestAccount({
444
+ isCurrentActiveAccount: true,
445
+ });
446
+
447
+ const shallowProjectList = await co
448
+ .list(Project)
449
+ .load(fullProjectList.$jazz.id, {
450
+ loadAs: account,
451
+ });
452
+ assert(shallowProjectList);
453
+
454
+ const publicAccessAsNewAccount = await Group.load(publicAccess.$jazz.id, {
455
+ loadAs: account,
456
+ });
457
+ assert(publicAccessAsNewAccount);
458
+
459
+ const updatedOrg = await Organisation.upsertUnique({
460
+ value: {
461
+ name: "My organisation",
462
+ projects: shallowProjectList,
463
+ },
464
+ unique: { name: "My organisation" },
465
+ owner: publicAccessAsNewAccount,
466
+ resolve: {
467
+ projects: {
468
+ $each: true,
469
+ },
470
+ },
471
+ });
472
+
473
+ assert(updatedOrg);
474
+
475
+ expect(updatedOrg.projects.$jazz.id).toEqual(fullProjectList.$jazz.id);
476
+ expect(updatedOrg.projects.length).toBe(1);
477
+ expect(updatedOrg.projects.at(0)?.name).toEqual("My project");
478
+ });
479
+
480
+ test("upserting a partially loaded value on an existing value with resolve", async () => {
481
+ const Project = co.map({
482
+ name: z.string(),
483
+ });
484
+ const Organisation = co.map({
485
+ name: z.string(),
486
+ projects: co.list(Project),
487
+ });
488
+ const publicAccess = Group.create();
489
+ publicAccess.addMember("everyone", "writer");
490
+
491
+ const initialProject = await Project.upsertUnique({
492
+ value: {
493
+ name: "My project",
494
+ },
495
+ unique: { unique: "First project" },
496
+ owner: publicAccess,
497
+ });
498
+ assert(initialProject);
499
+
500
+ const myOrg = await Organisation.upsertUnique({
501
+ value: {
502
+ name: "My organisation",
503
+ projects: co.list(Project).create([], publicAccess),
504
+ },
505
+ unique: { name: "My organisation" },
506
+ owner: publicAccess,
507
+ resolve: {
508
+ projects: {
509
+ $each: true,
510
+ },
511
+ },
512
+ });
513
+ assert(myOrg);
514
+
515
+ const fullProjectList = co
516
+ .list(Project)
517
+ .create([initialProject], publicAccess);
518
+
519
+ const account = await createJazzTestAccount({
520
+ isCurrentActiveAccount: true,
521
+ });
522
+
523
+ const shallowProjectList = await co
524
+ .list(Project)
525
+ .load(fullProjectList.$jazz.id, {
526
+ loadAs: account,
527
+ });
528
+ assert(shallowProjectList);
529
+
530
+ const publicAccessAsNewAccount = await Group.load(publicAccess.$jazz.id, {
531
+ loadAs: account,
532
+ });
533
+ assert(publicAccessAsNewAccount);
534
+
535
+ const updatedOrg = await Organisation.upsertUnique({
536
+ value: {
537
+ name: "My organisation",
538
+ projects: shallowProjectList,
539
+ },
540
+ unique: { name: "My organisation" },
541
+ owner: publicAccessAsNewAccount,
542
+ resolve: {
543
+ projects: {
544
+ $each: true,
545
+ },
546
+ },
547
+ });
548
+
549
+ assert(updatedOrg);
550
+
551
+ expect(updatedOrg.projects.$jazz.id).toEqual(fullProjectList.$jazz.id);
552
+ expect(updatedOrg.projects.length).toBe(1);
553
+ expect(updatedOrg.projects.at(0)?.name).toEqual("My project");
554
+ expect(updatedOrg.$jazz.id).toEqual(myOrg.$jazz.id);
555
+ });
556
+
557
+ test("concurrently upserting the same value", async () => {
558
+ const Project = co.map({
559
+ name: z.string(),
560
+ });
561
+
562
+ const owner = Group.create();
563
+
564
+ const promises = Array.from({ length: 3 }, (_, i) =>
565
+ Project.upsertUnique({
566
+ owner,
567
+ unique: "concurrent",
568
+ value: { name: `Project ${i}` },
569
+ }),
570
+ );
571
+
572
+ await Promise.all(promises);
573
+
574
+ const result = await Project.loadUnique("concurrent", owner.$jazz.id);
575
+ assert(result);
576
+
577
+ expect(result.name).toBe(`Project 2`);
578
+ });
579
+ });