@ng-org/orm 0.1.2-alpha.5 → 0.1.2-alpha.6

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 (58) hide show
  1. package/README.md +1 -1
  2. package/dist/connector/applyPatches.d.ts +6 -11
  3. package/dist/connector/applyPatches.d.ts.map +1 -1
  4. package/dist/connector/applyPatches.js +48 -19
  5. package/dist/connector/discrete/discreteOrmConnectionHandler.d.ts +45 -0
  6. package/dist/connector/discrete/discreteOrmConnectionHandler.d.ts.map +1 -0
  7. package/dist/connector/discrete/discreteOrmConnectionHandler.js +186 -0
  8. package/dist/connector/getObjects.d.ts +10 -0
  9. package/dist/connector/getObjects.d.ts.map +1 -0
  10. package/dist/connector/getObjects.js +25 -0
  11. package/dist/connector/insertObject.d.ts +8 -0
  12. package/dist/connector/insertObject.d.ts.map +1 -0
  13. package/dist/connector/{createSignalObjectForShape.js → insertObject.js} +10 -11
  14. package/dist/connector/ormConnectionHandler.d.ts +14 -13
  15. package/dist/connector/ormConnectionHandler.d.ts.map +1 -1
  16. package/dist/connector/ormConnectionHandler.js +74 -40
  17. package/dist/connector/utils.d.ts +14 -0
  18. package/dist/connector/utils.d.ts.map +1 -0
  19. package/dist/connector/utils.js +65 -0
  20. package/dist/frontendAdapters/react/index.d.ts +2 -1
  21. package/dist/frontendAdapters/react/index.d.ts.map +1 -1
  22. package/dist/frontendAdapters/react/index.js +2 -1
  23. package/dist/frontendAdapters/react/useDiscrete.d.ts +84 -0
  24. package/dist/frontendAdapters/react/useDiscrete.d.ts.map +1 -0
  25. package/dist/frontendAdapters/react/useDiscrete.js +127 -0
  26. package/dist/frontendAdapters/react/useShape.d.ts +64 -5
  27. package/dist/frontendAdapters/react/useShape.d.ts.map +1 -1
  28. package/dist/frontendAdapters/react/useShape.js +84 -14
  29. package/dist/frontendAdapters/svelte/index.d.ts +2 -1
  30. package/dist/frontendAdapters/svelte/index.d.ts.map +1 -1
  31. package/dist/frontendAdapters/svelte/index.js +2 -1
  32. package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts +85 -0
  33. package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts.map +1 -0
  34. package/dist/frontendAdapters/svelte/useDiscrete.svelte.js +124 -0
  35. package/dist/frontendAdapters/svelte/useShape.svelte.d.ts +65 -3
  36. package/dist/frontendAdapters/svelte/useShape.svelte.d.ts.map +1 -1
  37. package/dist/frontendAdapters/svelte/useShape.svelte.js +68 -6
  38. package/dist/frontendAdapters/vue/index.d.ts +2 -1
  39. package/dist/frontendAdapters/vue/index.d.ts.map +1 -1
  40. package/dist/frontendAdapters/vue/index.js +2 -1
  41. package/dist/frontendAdapters/vue/useDiscrete.d.ts +92 -0
  42. package/dist/frontendAdapters/vue/useDiscrete.d.ts.map +1 -0
  43. package/dist/frontendAdapters/vue/useDiscrete.js +111 -0
  44. package/dist/frontendAdapters/vue/useShape.d.ts +76 -1
  45. package/dist/frontendAdapters/vue/useShape.d.ts.map +1 -1
  46. package/dist/frontendAdapters/vue/useShape.js +79 -5
  47. package/dist/index.d.ts +9 -6
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +9 -6
  50. package/dist/types.d.ts +48 -11
  51. package/dist/types.d.ts.map +1 -1
  52. package/dist/types.js +1 -1
  53. package/package.json +40 -24
  54. package/dist/connector/applyPatches.test.d.ts +0 -2
  55. package/dist/connector/applyPatches.test.d.ts.map +0 -1
  56. package/dist/connector/applyPatches.test.js +0 -772
  57. package/dist/connector/createSignalObjectForShape.d.ts +0 -14
  58. package/dist/connector/createSignalObjectForShape.d.ts.map +0 -1
@@ -1,772 +0,0 @@
1
- // Copyright (c) 2025 Laurin Weger, Par le Peuple, NextGraph.org developers
2
- // All rights reserved.
3
- // Licensed under the Apache License, Version 2.0
4
- // <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
5
- // or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6
- // at your option. All files in the project carrying such
7
- // notice may not be copied, modified, or distributed except
8
- // according to those terms.
9
- // SPDX-License-Identifier: Apache-2.0 OR MIT
10
- import { describe, test, expect } from "vitest";
11
- import { applyPatches } from "../index.js";
12
- /**
13
- * Build a patch path string from segments (auto-prefix /)
14
- */
15
- function p(...segs) {
16
- return "/" + segs.map(String).join("/");
17
- }
18
- describe("applyDiff - set operations (primitives)", () => {
19
- test("add single primitive into new set", () => {
20
- const state = {};
21
- const diff = [
22
- { op: "add", valType: "set", path: p("tags"), value: "a" },
23
- ];
24
- applyPatches(state, diff);
25
- expect(state.tags).toBeInstanceOf(Set);
26
- expect([...state.tags]).toEqual(["a"]);
27
- });
28
- test("add multiple primitives into new set", () => {
29
- const state = {};
30
- const diff = [
31
- { op: "add", valType: "set", path: p("nums"), value: [1, 2, 3] },
32
- ];
33
- applyPatches(state, diff);
34
- expect([...state.nums]).toEqual([1, 2, 3]);
35
- });
36
- test("add primitives merging into existing set", () => {
37
- const state = { nums: new Set([1]) };
38
- const diff = [
39
- { op: "add", valType: "set", path: p("nums"), value: [2, 3] },
40
- ];
41
- applyPatches(state, diff);
42
- expect([...state.nums].sort()).toEqual([1, 2, 3]);
43
- });
44
- test("remove single primitive from set", () => {
45
- const state = { tags: new Set(["a", "b"]) };
46
- const diff = [
47
- { op: "remove", valType: "set", path: p("tags"), value: "a" },
48
- ];
49
- applyPatches(state, diff);
50
- expect([...state.tags]).toEqual(["b"]);
51
- });
52
- test("remove multiple primitives from set", () => {
53
- const state = { nums: new Set([1, 2, 3, 4]) };
54
- const diff = [
55
- { op: "remove", valType: "set", path: p("nums"), value: [2, 4] },
56
- ];
57
- applyPatches(state, diff);
58
- expect([...state.nums].sort()).toEqual([1, 3]);
59
- });
60
- });
61
- describe("applyDiff - multi-valued objects (Set-based)", () => {
62
- test("create multi-object container (Set) without @id", () => {
63
- const state = { "urn:person1": {} };
64
- const diff = [
65
- {
66
- op: "add",
67
- valType: "object",
68
- path: p("urn:person1", "children"),
69
- },
70
- ];
71
- applyPatches(state, diff);
72
- expect(state["urn:person1"].children).toBeInstanceOf(Set);
73
- });
74
- test("add object to Set with @id", () => {
75
- const state = { "urn:person1": { children: new Set() } };
76
- const diff = [
77
- // First patch creates the object in the Set
78
- {
79
- op: "add",
80
- valType: "object",
81
- path: p("urn:person1", "children", "urn:child1"),
82
- },
83
- // Second patch adds the @graph property (optional, for context)
84
- {
85
- op: "add",
86
- path: p("urn:person1", "children", "urn:child1", "@graph"),
87
- value: "urn:graph1",
88
- },
89
- // Third patch adds the @id property
90
- {
91
- op: "add",
92
- path: p("urn:person1", "children", "urn:child1", "@id"),
93
- value: "urn:child1",
94
- },
95
- ];
96
- applyPatches(state, diff);
97
- const children = state["urn:person1"].children;
98
- expect(children).toBeInstanceOf(Set);
99
- expect(children.size).toBe(1);
100
- const child = [...children][0];
101
- expect(child["@id"]).toBe("urn:child1");
102
- expect(child["@graph"]).toBeDefined();
103
- });
104
- test("add object to root Set", () => {
105
- const state = new Set();
106
- const diff = [
107
- // First patch creates the object in the Set
108
- {
109
- op: "add",
110
- valType: "object",
111
- path: p("urn:graph1|urn:root1"),
112
- },
113
- // Second patch adds the @graph property (optional, for context)
114
- {
115
- op: "add",
116
- path: p("urn:graph1|urn:root1", "@graph"),
117
- value: "urn:graph1",
118
- },
119
- // Third patch adds the @id property
120
- {
121
- op: "add",
122
- path: p("urn:graph1|urn:root1", "@id"),
123
- value: "urn:root1",
124
- },
125
- ];
126
- applyPatches(state, diff);
127
- const children = [...state];
128
- expect(children.length).toBe(1);
129
- const child = children[0];
130
- expect(child).toBeTypeOf("object");
131
- expect(child["@id"]).toBe("urn:root1");
132
- expect(child["@graph"]).toBe("urn:graph1");
133
- });
134
- test("add properties to object in Set", () => {
135
- const obj = { "@id": "urn:child1", "@graph": "urn:graph1" };
136
- const state = { "urn:person1": { children: new Set([obj]) } };
137
- const diff = [
138
- {
139
- op: "add",
140
- path: p("urn:person1", "children", "urn:child1", "name"),
141
- value: "Alice",
142
- },
143
- {
144
- op: "add",
145
- path: p("urn:person1", "children", "urn:child1", "age"),
146
- value: 10,
147
- },
148
- ];
149
- applyPatches(state, diff);
150
- const child = [...state["urn:person1"].children][0];
151
- expect(child.name).toBe("Alice");
152
- expect(child.age).toBe(10);
153
- });
154
- test("remove object from Set by @id", () => {
155
- const obj1 = {
156
- "@id": "urn:child1",
157
- "@graph": "urn:graph1",
158
- name: "Alice",
159
- };
160
- const obj2 = {
161
- "@id": "urn:child2",
162
- "@graph": "urn:graph2",
163
- name: "Bob",
164
- };
165
- const state = {
166
- "urn:person1": { children: new Set([obj1, obj2]) },
167
- };
168
- const diff = [
169
- { op: "remove", path: p("urn:person1", "children", "urn:child1") },
170
- ];
171
- applyPatches(state, diff);
172
- const children = state["urn:person1"].children;
173
- expect(children.size).toBe(1);
174
- const remaining = [...children][0];
175
- expect(remaining["@id"]).toBe("urn:child2");
176
- });
177
- test("remove object from root set", () => {
178
- const obj1 = {
179
- "@id": "urn:child1",
180
- "@graph": "urn:graph1",
181
- name: "Alice",
182
- };
183
- const obj2 = {
184
- "@id": "urn:child2",
185
- "@graph": "urn:graph2",
186
- name: "Bob",
187
- };
188
- const state = new Set([
189
- { "@id": "urn:person1", "@graph": "urn:graph3", children: [obj1] },
190
- { "@id": "urn:person2", "@graph": "urn:graph4", children: [obj2] },
191
- ]);
192
- const diff = [{ op: "remove", path: p("urn:person1") }];
193
- applyPatches(state, diff);
194
- expect(state.size).toBe(1);
195
- });
196
- test("create nested Set (multi-valued property within object in Set)", () => {
197
- const parent = { "@id": "urn:parent1", "@graph": "urn:graph0" };
198
- const state = { root: { parents: new Set([parent]) } };
199
- const diff = [
200
- {
201
- op: "add",
202
- valType: "object",
203
- path: p("root", "parents", "urn:parent1", "children"),
204
- },
205
- {
206
- op: "add",
207
- valType: "object",
208
- path: p("root", "parents", "urn:parent1", "children", "urn:child1"),
209
- },
210
- {
211
- op: "add",
212
- path: p("root", "parents", "urn:parent1", "children", "urn:child1", "@graph"),
213
- value: "urn:graph1",
214
- },
215
- {
216
- op: "add",
217
- path: p("root", "parents", "urn:parent1", "children", "urn:child1", "@id"),
218
- value: "urn:child1",
219
- },
220
- ];
221
- applyPatches(state, diff);
222
- const nestedChildren = parent.children;
223
- expect(nestedChildren).toBeInstanceOf(Set);
224
- expect(nestedChildren.size).toBe(1);
225
- });
226
- });
227
- describe("applyDiff - object & literal operations", () => {
228
- test("create single object (with @id)", () => {
229
- const state = { "urn:person1": {} };
230
- const diff = [
231
- { op: "add", path: p("urn:person1", "address"), valType: "object" },
232
- {
233
- op: "add",
234
- path: p("urn:person1", "address", "@graph"),
235
- value: "urn:graph1",
236
- },
237
- {
238
- op: "add",
239
- path: p("urn:person1", "address", "@id"),
240
- value: "urn:addr1",
241
- },
242
- ];
243
- applyPatches(state, diff);
244
- expect(state["urn:person1"].address["@id"]).toBe("urn:addr1");
245
- expect(state["urn:person1"].address["@graph"]).toBeDefined();
246
- expect(state["urn:person1"].address).not.toBeInstanceOf(Set);
247
- });
248
- test("create multi-object container (without @id) -> Set", () => {
249
- const state = { "urn:person1": {} };
250
- const diff = [
251
- {
252
- op: "add",
253
- path: p("urn:person1", "addresses"),
254
- valType: "object",
255
- },
256
- ];
257
- applyPatches(state, diff);
258
- expect(state["urn:person1"].addresses).toBeInstanceOf(Set);
259
- });
260
- test("add object (create empty object with @id)", () => {
261
- const state = {};
262
- const diff = [
263
- { op: "add", path: p("address"), valType: "object" },
264
- { op: "add", path: p("address", "@graph"), value: "urn:graph1" },
265
- { op: "add", path: p("address", "@id"), value: "urn:addr1" },
266
- ];
267
- applyPatches(state, diff);
268
- expect(state.address["@id"]).toBe("urn:addr1");
269
- expect(state.address["@graph"]).toBeDefined();
270
- expect(state.address).not.toBeInstanceOf(Set);
271
- });
272
- test("add nested object path with ensurePathExists and @id", () => {
273
- const state = {};
274
- const diff = [
275
- { op: "add", path: p("a", "b", "c"), valType: "object" },
276
- {
277
- op: "add",
278
- path: p("a", "b", "c", "@graph"),
279
- value: "urn:graph1",
280
- },
281
- { op: "add", path: p("a", "b", "c", "@id"), value: "urn:c1" },
282
- ];
283
- applyPatches(state, diff, true);
284
- expect(state.a.b.c["@id"]).toBe("urn:c1");
285
- expect(state.a.b.c["@graph"]).toBeDefined();
286
- expect(state.a.b.c).not.toBeInstanceOf(Set);
287
- });
288
- test("add primitive value", () => {
289
- const state = { address: {} };
290
- const diff = [
291
- { op: "add", path: p("address", "street"), value: "1st" },
292
- ];
293
- applyPatches(state, diff);
294
- expect(state.address.street).toBe("1st");
295
- });
296
- test("overwrite primitive value", () => {
297
- const state = { address: { street: "old" } };
298
- const diff = [
299
- { op: "add", path: p("address", "street"), value: "new" },
300
- ];
301
- applyPatches(state, diff);
302
- expect(state.address.street).toBe("new");
303
- });
304
- test("remove primitive", () => {
305
- const state = { address: { street: "1st", country: "Greece" } };
306
- const diff = [{ op: "remove", path: p("address", "street") }];
307
- applyPatches(state, diff);
308
- expect(state.address.street).toBeUndefined();
309
- expect(state.address.country).toBe("Greece");
310
- });
311
- test("remove object branch", () => {
312
- const state = { address: { street: "1st" }, other: 1 };
313
- const diff = [{ op: "remove", path: p("address") }];
314
- applyPatches(state, diff);
315
- expect(state.address).toBeUndefined();
316
- expect(state.other).toBe(1);
317
- });
318
- });
319
- describe("applyDiff - multiple mixed patches in a single diff", () => {
320
- test("sequence of mixed set/object/literal add & remove", () => {
321
- const state = {
322
- "urn:person1": {},
323
- tags: new Set(["old"]),
324
- };
325
- const diff = [
326
- // Create multi-object Set
327
- {
328
- op: "add",
329
- valType: "object",
330
- path: p("urn:person1", "addresses"),
331
- },
332
- {
333
- op: "add",
334
- valType: "object",
335
- path: p("urn:person1", "addresses", "urn:addr1"),
336
- },
337
- {
338
- op: "add",
339
- path: p("urn:person1", "addresses", "urn:addr1", "@graph"),
340
- value: "urn:graph1",
341
- },
342
- {
343
- op: "add",
344
- path: p("urn:person1", "addresses", "urn:addr1", "@id"),
345
- value: "urn:addr1",
346
- },
347
- {
348
- op: "add",
349
- path: p("urn:person1", "addresses", "urn:addr1", "street"),
350
- value: "Main St",
351
- },
352
- // Create single object
353
- { op: "add", path: p("profile"), valType: "object" },
354
- { op: "add", path: p("profile", "@graph"), value: "urn:graph2" },
355
- { op: "add", path: p("profile", "@id"), value: "urn:profile1" },
356
- { op: "add", path: p("profile", "name"), value: "Alice" },
357
- // Primitive set operations
358
- { op: "add", valType: "set", path: p("tags"), value: ["new"] },
359
- { op: "remove", valType: "set", path: p("tags"), value: "old" },
360
- ];
361
- applyPatches(state, diff); // Enable ensurePathExists for nested object creation
362
- expect(state["urn:person1"].addresses).toBeInstanceOf(Set);
363
- expect(state["urn:person1"].addresses.size).toBe(1);
364
- const addr = [...state["urn:person1"].addresses][0];
365
- expect(addr["@id"]).toBe("urn:addr1");
366
- expect(addr["@graph"]).toBeDefined();
367
- expect(addr.street).toBe("Main St");
368
- expect(state.profile["@id"]).toBe("urn:profile1");
369
- expect(state.profile["@graph"]).toBeDefined();
370
- expect(state.profile.name).toBe("Alice");
371
- expect([...state.tags]).toEqual(["new"]);
372
- });
373
- test("complex nested path creation and mutations with ensurePathExists", () => {
374
- const state = {};
375
- const diff = [
376
- // Create b as a single object (with @id)
377
- { op: "add", path: p("a", "b"), valType: "object" },
378
- { op: "add", path: p("a", "b", "@graph"), value: "urn:graph1" },
379
- { op: "add", path: p("a", "b", "@id"), value: "urn:b1" },
380
- { op: "add", path: p("a", "b", "c"), value: 1 },
381
- // Create a primitive set
382
- {
383
- op: "add",
384
- valType: "set",
385
- path: p("a", "nums"),
386
- value: [1, 2, 3],
387
- },
388
- { op: "remove", valType: "set", path: p("a", "nums"), value: 2 },
389
- { op: "add", path: p("a", "b", "d"), value: 2 },
390
- { op: "remove", path: p("a", "b", "c") },
391
- ];
392
- applyPatches(state, diff, true);
393
- expect(state.a.b["@id"]).toBe("urn:b1");
394
- expect(state.a.b["@graph"]).toBeDefined();
395
- expect(state.a.b.c).toBeUndefined();
396
- expect(state.a.b.d).toBe(2);
397
- expect(state.a.nums).toBeInstanceOf(Set);
398
- expect([...state.a.nums].sort()).toEqual([1, 3]);
399
- });
400
- });
401
- describe("applyDiff - complete workflow example", () => {
402
- test("full example: create person with single address and multiple children", () => {
403
- const state = {};
404
- const diff = [
405
- // Create root person object
406
- { op: "add", path: p("urn:person1"), valType: "object" },
407
- {
408
- op: "add",
409
- path: p("urn:person1", "@graph"),
410
- value: "urn:graph1",
411
- },
412
- { op: "add", path: p("urn:person1", "@id"), value: "urn:person1" },
413
- { op: "add", path: p("urn:person1", "name"), value: "John" },
414
- // Add single address object
415
- { op: "add", path: p("urn:person1", "address"), valType: "object" },
416
- {
417
- op: "add",
418
- path: p("urn:person1", "address", "@graph"),
419
- value: "urn:graph2",
420
- },
421
- {
422
- op: "add",
423
- path: p("urn:person1", "address", "@id"),
424
- value: "urn:addr1",
425
- },
426
- {
427
- op: "add",
428
- path: p("urn:person1", "address", "street"),
429
- value: "1st Street",
430
- },
431
- {
432
- op: "add",
433
- path: p("urn:person1", "address", "country"),
434
- value: "Greece",
435
- },
436
- // Create multi-valued children Set
437
- {
438
- op: "add",
439
- path: p("urn:person1", "children"),
440
- valType: "object",
441
- },
442
- // Add first child
443
- {
444
- op: "add",
445
- path: p("urn:person1", "children", "urn:child1"),
446
- valType: "object",
447
- },
448
- {
449
- op: "add",
450
- path: p("urn:person1", "children", "urn:child1", "@graph"),
451
- value: "urn:graph3",
452
- },
453
- {
454
- op: "add",
455
- path: p("urn:person1", "children", "urn:child1", "@id"),
456
- value: "urn:child1",
457
- },
458
- {
459
- op: "add",
460
- path: p("urn:person1", "children", "urn:child1", "name"),
461
- value: "Alice",
462
- },
463
- // Add second child
464
- {
465
- op: "add",
466
- path: p("urn:person1", "children", "urn:child2"),
467
- valType: "object",
468
- },
469
- {
470
- op: "add",
471
- path: p("urn:person1", "children", "urn:child2", "@graph"),
472
- value: "urn:graph4",
473
- },
474
- {
475
- op: "add",
476
- path: p("urn:person1", "children", "urn:child2", "@id"),
477
- value: "urn:child2",
478
- },
479
- {
480
- op: "add",
481
- path: p("urn:person1", "children", "urn:child2", "name"),
482
- value: "Bob",
483
- },
484
- // Add primitive set (tags)
485
- {
486
- op: "add",
487
- valType: "set",
488
- path: p("urn:person1", "tags"),
489
- value: ["developer", "parent"],
490
- },
491
- ];
492
- applyPatches(state, diff); // Enable ensurePathExists to create nested objects
493
- // Verify person
494
- expect(state["urn:person1"]["@id"]).toBe("urn:person1");
495
- expect(state["urn:person1"]["@graph"]).toBeDefined();
496
- expect(state["urn:person1"].name).toBe("John");
497
- // Verify single address (plain object)
498
- expect(state["urn:person1"].address).not.toBeInstanceOf(Set);
499
- expect(state["urn:person1"].address["@id"]).toBe("urn:addr1");
500
- expect(state["urn:person1"].address["@graph"]).toBeDefined();
501
- expect(state["urn:person1"].address.street).toBe("1st Street");
502
- expect(state["urn:person1"].address.country).toBe("Greece");
503
- // Verify children Set
504
- const children = state["urn:person1"].children;
505
- expect(children).toBeInstanceOf(Set);
506
- expect(children.size).toBe(2);
507
- const childrenArray = [...children];
508
- const alice = childrenArray.find((c) => c["@id"] === "urn:child1");
509
- const bob = childrenArray.find((c) => c["@id"] === "urn:child2");
510
- expect(alice["@graph"]).toBeDefined();
511
- expect(alice.name).toBe("Alice");
512
- expect(bob["@graph"]).toBeDefined();
513
- expect(bob.name).toBe("Bob");
514
- // Verify primitive set
515
- expect(state["urn:person1"].tags).toBeInstanceOf(Set);
516
- expect([...state["urn:person1"].tags].sort()).toEqual([
517
- "developer",
518
- "parent",
519
- ]);
520
- });
521
- test("update and remove operations on complex structure", () => {
522
- // Start with pre-existing structure
523
- const child1 = {
524
- "@id": "urn:child1",
525
- "@graph": "urn:graph3",
526
- name: "Alice",
527
- };
528
- const child2 = {
529
- "@id": "urn:child2",
530
- "@graph": "urn:graph4",
531
- name: "Bob",
532
- };
533
- const state = {
534
- "urn:person1": {
535
- "@id": "urn:person1",
536
- "@graph": "urn:graph1",
537
- name: "John",
538
- address: {
539
- "@id": "urn:addr1",
540
- "@graph": "urn:graph2",
541
- street: "1st Street",
542
- country: "Greece",
543
- },
544
- children: new Set([child1, child2]),
545
- tags: new Set(["developer", "parent"]),
546
- },
547
- };
548
- const diff = [
549
- // Update address property
550
- {
551
- op: "add",
552
- path: p("urn:person1", "address", "street"),
553
- value: "2nd Street",
554
- },
555
- // Remove one child
556
- { op: "remove", path: p("urn:person1", "children", "urn:child1") },
557
- // Update child property
558
- {
559
- op: "add",
560
- path: p("urn:person1", "children", "urn:child2", "age"),
561
- value: 12,
562
- },
563
- // Remove tag
564
- {
565
- op: "remove",
566
- valType: "set",
567
- path: p("urn:person1", "tags"),
568
- value: "developer",
569
- },
570
- ];
571
- applyPatches(state, diff);
572
- expect(state["urn:person1"].address.street).toBe("2nd Street");
573
- expect(state["urn:person1"].children.size).toBe(1);
574
- expect([...state["urn:person1"].children][0]["@id"]).toBe("urn:child2");
575
- expect([...state["urn:person1"].children][0].age).toBe(12);
576
- expect([...state["urn:person1"].tags]).toEqual(["parent"]);
577
- });
578
- });
579
- describe("applyDiff - ensurePathExists with Set detection", () => {
580
- test("auto-detect and create Set when next segment is IRI (contains |)", () => {
581
- const state = {};
582
- const diff = [
583
- // Path: /parent/children/urn:graph1|urn:child1
584
- // When creating "children", next segment is "urn:graph1|urn:child1" (contains |)
585
- // So "children" should be created as a Set
586
- {
587
- op: "add",
588
- valType: "object",
589
- path: p("parent", "children", "urn:graph1|urn:child1"),
590
- },
591
- {
592
- op: "add",
593
- path: p("parent", "children", "urn:graph1|urn:child1", "@graph"),
594
- value: "urn:graph1",
595
- },
596
- {
597
- op: "add",
598
- path: p("parent", "children", "urn:graph1|urn:child1", "@id"),
599
- value: "urn:child1",
600
- },
601
- {
602
- op: "add",
603
- path: p("parent", "children", "urn:graph1|urn:child1", "name"),
604
- value: "Alice",
605
- },
606
- ];
607
- applyPatches(state, diff, true);
608
- // Verify parent was created
609
- expect(state.parent).toBeDefined();
610
- // Verify children was created as a Set (not a plain object)
611
- expect(state.parent.children).toBeInstanceOf(Set);
612
- expect(state.parent.children.size).toBe(1);
613
- // Verify the child object inside the Set
614
- const child = [...state.parent.children][0];
615
- expect(child["@id"]).toBe("urn:child1");
616
- expect(child["@graph"]).toBe("urn:graph1");
617
- expect(child.name).toBe("Alice");
618
- });
619
- test("auto-detect and create plain object when next segment is NOT an IRI", () => {
620
- const state = {};
621
- const diff = [
622
- // Path: /parent/address/street
623
- // When creating "address", next segment is "street" (no |)
624
- // So "address" should be created as a plain object
625
- { op: "add", path: p("parent", "address", "street"), value: "Main St" },
626
- ];
627
- applyPatches(state, diff, true);
628
- // Verify parent was created
629
- expect(state.parent).toBeDefined();
630
- // Verify address was created as a plain object (not a Set)
631
- expect(state.parent.address).toBeTypeOf("object");
632
- expect(state.parent.address).not.toBeInstanceOf(Set);
633
- expect(state.parent.address.street).toBe("Main St");
634
- });
635
- test("deeply nested path with mixed Set and object creation", () => {
636
- const state = {};
637
- const diff = [
638
- // Create: /org/departments/urn:g1|dept1/employees/urn:g2|emp1/name
639
- // - org: plain object (next is "departments")
640
- // - departments: Set (next is "urn:g1|dept1" - contains |)
641
- // - dept1: object in Set (next is "employees")
642
- // - employees: Set (next is "urn:g2|emp1" - contains |)
643
- // - emp1: object in Set (next is "name")
644
- {
645
- op: "add",
646
- valType: "object",
647
- path: p("org", "departments", "urn:g1|dept1"),
648
- },
649
- {
650
- op: "add",
651
- path: p("org", "departments", "urn:g1|dept1", "@graph"),
652
- value: "urn:g1",
653
- },
654
- {
655
- op: "add",
656
- path: p("org", "departments", "urn:g1|dept1", "@id"),
657
- value: "dept1",
658
- },
659
- {
660
- op: "add",
661
- valType: "object",
662
- path: p("org", "departments", "urn:g1|dept1", "employees", "urn:g2|emp1"),
663
- },
664
- {
665
- op: "add",
666
- path: p("org", "departments", "urn:g1|dept1", "employees", "urn:g2|emp1", "@graph"),
667
- value: "urn:g2",
668
- },
669
- {
670
- op: "add",
671
- path: p("org", "departments", "urn:g1|dept1", "employees", "urn:g2|emp1", "@id"),
672
- value: "emp1",
673
- },
674
- {
675
- op: "add",
676
- path: p("org", "departments", "urn:g1|dept1", "employees", "urn:g2|emp1", "name"),
677
- value: "John",
678
- },
679
- ];
680
- applyPatches(state, diff, true);
681
- // Verify org is plain object
682
- expect(state.org).toBeTypeOf("object");
683
- expect(state.org).not.toBeInstanceOf(Set);
684
- // Verify departments is a Set
685
- expect(state.org.departments).toBeInstanceOf(Set);
686
- expect(state.org.departments.size).toBe(1);
687
- // Get the department
688
- const dept = [...state.org.departments][0];
689
- expect(dept["@id"]).toBe("dept1");
690
- expect(dept["@graph"]).toBe("urn:g1");
691
- // Verify employees is a Set
692
- expect(dept.employees).toBeInstanceOf(Set);
693
- expect(dept.employees.size).toBe(1);
694
- // Get the employee
695
- const emp = [...dept.employees][0];
696
- expect(emp["@id"]).toBe("emp1");
697
- expect(emp["@graph"]).toBe("urn:g2");
698
- expect(emp.name).toBe("John");
699
- });
700
- test("add multiple objects to auto-created Set", () => {
701
- const state = {};
702
- const diff = [
703
- // First child
704
- {
705
- op: "add",
706
- valType: "object",
707
- path: p("family", "children", "urn:g1|child1"),
708
- },
709
- {
710
- op: "add",
711
- path: p("family", "children", "urn:g1|child1", "@graph"),
712
- value: "urn:g1",
713
- },
714
- {
715
- op: "add",
716
- path: p("family", "children", "urn:g1|child1", "@id"),
717
- value: "child1",
718
- },
719
- {
720
- op: "add",
721
- path: p("family", "children", "urn:g1|child1", "name"),
722
- value: "Alice",
723
- },
724
- // Second child
725
- {
726
- op: "add",
727
- valType: "object",
728
- path: p("family", "children", "urn:g1|child2"),
729
- },
730
- {
731
- op: "add",
732
- path: p("family", "children", "urn:g1|child2", "@graph"),
733
- value: "urn:g1",
734
- },
735
- {
736
- op: "add",
737
- path: p("family", "children", "urn:g1|child2", "@id"),
738
- value: "child2",
739
- },
740
- {
741
- op: "add",
742
- path: p("family", "children", "urn:g1|child2", "name"),
743
- value: "Bob",
744
- },
745
- ];
746
- applyPatches(state, diff, true);
747
- // Verify children is a Set with 2 items
748
- expect(state.family.children).toBeInstanceOf(Set);
749
- expect(state.family.children.size).toBe(2);
750
- const children = [...state.family.children];
751
- const alice = children.find((c) => c["@id"] === "child1");
752
- const bob = children.find((c) => c["@id"] === "child2");
753
- expect(alice.name).toBe("Alice");
754
- expect(bob.name).toBe("Bob");
755
- });
756
- });
757
- describe("applyDiff - ignored / invalid scenarios", () => {
758
- test("skip patch with non-leading slash path", () => {
759
- const state = {};
760
- const diff = [
761
- { op: "add", path: "address/street", value: "x" },
762
- ];
763
- applyPatches(state, diff);
764
- expect(state).toEqual({});
765
- });
766
- test("missing parent without ensurePathExists -> patch skipped and no mutation", () => {
767
- const state = {};
768
- const diff = [{ op: "add", path: p("a", "b", "c"), value: 1 }];
769
- applyPatches(state, diff, false);
770
- expect(state).toEqual({});
771
- });
772
- });