@ng-org/orm 0.1.2-alpha.4 → 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.
- package/README.md +1 -1
- package/dist/connector/applyPatches.d.ts +6 -11
- package/dist/connector/applyPatches.d.ts.map +1 -1
- package/dist/connector/applyPatches.js +48 -19
- package/dist/connector/discrete/discreteOrmConnectionHandler.d.ts +45 -0
- package/dist/connector/discrete/discreteOrmConnectionHandler.d.ts.map +1 -0
- package/dist/connector/discrete/discreteOrmConnectionHandler.js +186 -0
- package/dist/connector/getObjects.d.ts +10 -0
- package/dist/connector/getObjects.d.ts.map +1 -0
- package/dist/connector/getObjects.js +25 -0
- package/dist/connector/insertObject.d.ts +8 -0
- package/dist/connector/insertObject.d.ts.map +1 -0
- package/dist/connector/{createSignalObjectForShape.js → insertObject.js} +10 -11
- package/dist/connector/ormConnectionHandler.d.ts +14 -13
- package/dist/connector/ormConnectionHandler.d.ts.map +1 -1
- package/dist/connector/ormConnectionHandler.js +74 -40
- package/dist/connector/utils.d.ts +14 -0
- package/dist/connector/utils.d.ts.map +1 -0
- package/dist/connector/utils.js +65 -0
- package/dist/frontendAdapters/react/index.d.ts +2 -1
- package/dist/frontendAdapters/react/index.d.ts.map +1 -1
- package/dist/frontendAdapters/react/index.js +2 -1
- package/dist/frontendAdapters/react/useDiscrete.d.ts +84 -0
- package/dist/frontendAdapters/react/useDiscrete.d.ts.map +1 -0
- package/dist/frontendAdapters/react/useDiscrete.js +127 -0
- package/dist/frontendAdapters/react/useShape.d.ts +64 -5
- package/dist/frontendAdapters/react/useShape.d.ts.map +1 -1
- package/dist/frontendAdapters/react/useShape.js +84 -14
- package/dist/frontendAdapters/svelte/index.d.ts +2 -1
- package/dist/frontendAdapters/svelte/index.d.ts.map +1 -1
- package/dist/frontendAdapters/svelte/index.js +2 -1
- package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts +85 -0
- package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts.map +1 -0
- package/dist/frontendAdapters/svelte/useDiscrete.svelte.js +124 -0
- package/dist/frontendAdapters/svelte/useShape.svelte.d.ts +65 -3
- package/dist/frontendAdapters/svelte/useShape.svelte.d.ts.map +1 -1
- package/dist/frontendAdapters/svelte/useShape.svelte.js +68 -6
- package/dist/frontendAdapters/vue/index.d.ts +2 -1
- package/dist/frontendAdapters/vue/index.d.ts.map +1 -1
- package/dist/frontendAdapters/vue/index.js +2 -1
- package/dist/frontendAdapters/vue/useDiscrete.d.ts +92 -0
- package/dist/frontendAdapters/vue/useDiscrete.d.ts.map +1 -0
- package/dist/frontendAdapters/vue/useDiscrete.js +111 -0
- package/dist/frontendAdapters/vue/useShape.d.ts +76 -1
- package/dist/frontendAdapters/vue/useShape.d.ts.map +1 -1
- package/dist/frontendAdapters/vue/useShape.js +79 -5
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -6
- package/dist/types.d.ts +48 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/package.json +48 -31
- package/dist/connector/applyPatches.test.d.ts +0 -2
- package/dist/connector/applyPatches.test.d.ts.map +0 -1
- package/dist/connector/applyPatches.test.js +0 -772
- package/dist/connector/createSignalObjectForShape.d.ts +0 -14
- 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
|
-
});
|