@ng-org/orm 0.1.2-alpha.1 → 0.1.2-alpha.3

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