@ng-org/alien-deepsignals 0.1.2-alpha.3 → 0.1.2-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deepSignal.d.ts.map +1 -1
- package/dist/deepSignal.js +168 -16
- package/dist/hooks/svelte/useDeepSignal.svelte.d.ts +3 -7
- package/dist/hooks/svelte/useDeepSignal.svelte.d.ts.map +1 -1
- package/dist/hooks/svelte/useDeepSignal.svelte.js +34 -18
- package/dist/hooks/vue/useDeepSignal.d.ts +4 -3
- package/dist/hooks/vue/useDeepSignal.d.ts.map +1 -1
- package/dist/hooks/vue/useDeepSignal.js +52 -24
- package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.d.ts +4 -0
- package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.d.ts.map +1 -0
- package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.js +225 -0
- package/dist/test/frontend/astro-app/src/components/ReactPanel.d.ts +4 -0
- package/dist/test/frontend/astro-app/src/components/ReactPanel.d.ts.map +1 -0
- package/dist/test/frontend/astro-app/src/components/ReactPanel.js +227 -0
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.d.ts +4 -0
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.d.ts.map +1 -0
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.js +150 -0
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.d.ts +4 -0
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.d.ts.map +1 -0
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.js +184 -0
- package/dist/test/frontend/playwright/crossFrameworkHooks.spec.d.ts +2 -0
- package/dist/test/frontend/playwright/crossFrameworkHooks.spec.d.ts.map +1 -0
- package/dist/test/frontend/playwright/crossFrameworkHooks.spec.js +171 -0
- package/dist/test/frontend/playwright/perfSuite.spec.d.ts +2 -0
- package/dist/test/frontend/playwright/perfSuite.spec.d.ts.map +1 -0
- package/dist/test/frontend/playwright/perfSuite.spec.js +128 -0
- package/dist/test/frontend/utils/mockData.d.ts +53 -0
- package/dist/test/frontend/utils/mockData.d.ts.map +1 -0
- package/dist/test/frontend/utils/mockData.js +78 -0
- package/dist/test/frontend/utils/paths.d.ts +4 -0
- package/dist/test/frontend/utils/paths.d.ts.map +1 -0
- package/dist/test/frontend/utils/paths.js +28 -0
- package/dist/test/frontend/utils/perfScenarios.d.ts +15 -0
- package/dist/test/frontend/utils/perfScenarios.d.ts.map +1 -0
- package/dist/test/frontend/utils/perfScenarios.js +287 -0
- package/dist/test/frontend/utils/renderMetrics.d.ts +13 -0
- package/dist/test/frontend/utils/renderMetrics.d.ts.map +1 -0
- package/dist/test/frontend/utils/renderMetrics.js +45 -0
- package/dist/test/frontend/utils/state.d.ts +57 -0
- package/dist/test/frontend/utils/state.d.ts.map +1 -0
- package/dist/test/frontend/utils/state.js +79 -0
- package/dist/test/lib/core.test.d.ts +2 -0
- package/dist/test/lib/core.test.d.ts.map +1 -0
- package/dist/test/lib/core.test.js +53 -0
- package/dist/test/lib/deepSignalOptions.test.d.ts +2 -0
- package/dist/test/lib/deepSignalOptions.test.d.ts.map +1 -0
- package/dist/test/lib/deepSignalOptions.test.js +230 -0
- package/dist/test/lib/index.test.d.ts +2 -0
- package/dist/test/lib/index.test.d.ts.map +1 -0
- package/dist/test/lib/index.test.js +807 -0
- package/dist/test/lib/misc.test.d.ts +2 -0
- package/dist/test/lib/misc.test.d.ts.map +1 -0
- package/dist/test/lib/misc.test.js +140 -0
- package/dist/test/lib/watch.test.d.ts +2 -0
- package/dist/test/lib/watch.test.d.ts.map +1 -0
- package/dist/test/lib/watch.test.js +1280 -0
- package/package.json +1 -2
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2025 Laurin Weger, Par le Peuple, NextGraph.org developers
|
|
3
|
+
// All rights reserved.
|
|
4
|
+
// Licensed under the Apache License, Version 2.0
|
|
5
|
+
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
|
6
|
+
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
|
7
|
+
// at your option. All files in the project carrying such
|
|
8
|
+
// notice may not be copied, modified, or distributed except
|
|
9
|
+
// according to those terms.
|
|
10
|
+
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const vitest_1 = require("vitest");
|
|
13
|
+
const deepSignal_1 = require("../../deepSignal");
|
|
14
|
+
const watch_1 = require("../../watch");
|
|
15
|
+
(0, vitest_1.describe)("deepSignal options", () => {
|
|
16
|
+
(0, vitest_1.describe)("custom ID generator", () => {
|
|
17
|
+
(0, vitest_1.it)("uses custom ID generator for objects without @id", async () => {
|
|
18
|
+
let counter = 1000;
|
|
19
|
+
const options = {
|
|
20
|
+
propGenerator: () => ({ syntheticId: `custom-${counter++}` }),
|
|
21
|
+
syntheticIdPropertyName: "@id",
|
|
22
|
+
};
|
|
23
|
+
const state = (0, deepSignal_1.deepSignal)({ data: {} }, options);
|
|
24
|
+
const patches = [];
|
|
25
|
+
const { stopListening: stop } = (0, watch_1.watch)(state, ({ patches: batch }) => patches.push(batch));
|
|
26
|
+
state.data.user = { name: "Alice" };
|
|
27
|
+
await Promise.resolve();
|
|
28
|
+
// Check that @id was assigned
|
|
29
|
+
(0, vitest_1.expect)(state.data.user["@id"]).toBe("custom-1000");
|
|
30
|
+
// Check that patch was emitted for @id
|
|
31
|
+
const flat = patches.flat().map((p) => p.path.join("."));
|
|
32
|
+
(0, vitest_1.expect)(flat).toContain("data.user.@id");
|
|
33
|
+
stop();
|
|
34
|
+
});
|
|
35
|
+
(0, vitest_1.it)("respects existing @id on objects", async () => {
|
|
36
|
+
const options = {
|
|
37
|
+
propGenerator: () => ({ syntheticId: "should-not-be-used" }),
|
|
38
|
+
syntheticIdPropertyName: "@id",
|
|
39
|
+
};
|
|
40
|
+
const state = (0, deepSignal_1.deepSignal)({ items: [] }, options);
|
|
41
|
+
state.items.push({ "@id": "existing-123", value: 42 });
|
|
42
|
+
// Should use the existing @id
|
|
43
|
+
(0, vitest_1.expect)(state.items[0]["@id"]).toBe("existing-123");
|
|
44
|
+
});
|
|
45
|
+
(0, vitest_1.it)("uses @id property from objects added to Sets", async () => {
|
|
46
|
+
const options = {
|
|
47
|
+
propGenerator: ({ object }) => ({
|
|
48
|
+
syntheticId: object["@id"] || "fallback-id",
|
|
49
|
+
}),
|
|
50
|
+
syntheticIdPropertyName: "@id",
|
|
51
|
+
};
|
|
52
|
+
const state = (0, deepSignal_1.deepSignal)({ s: new Set() }, options);
|
|
53
|
+
const patches = [];
|
|
54
|
+
const { stopListening: stop } = (0, watch_1.watch)(state, ({ patches: batch }) => patches.push(batch));
|
|
55
|
+
const obj = { "@id": "set-entry-1", data: "test" };
|
|
56
|
+
state.s.add(obj);
|
|
57
|
+
await Promise.resolve();
|
|
58
|
+
const flat = patches.flat().map((p) => p.path.join("."));
|
|
59
|
+
// Path should use the @id as synthetic key
|
|
60
|
+
(0, vitest_1.expect)(flat.some((p) => p.startsWith("s.set-entry-1"))).toBe(true);
|
|
61
|
+
stop();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
(0, vitest_1.describe)("syntheticIdPropertyName option", () => {
|
|
65
|
+
(0, vitest_1.it)("adds @id to all nested objects when enabled", async () => {
|
|
66
|
+
let counter = 100;
|
|
67
|
+
const options = {
|
|
68
|
+
propGenerator: () => ({ syntheticId: `auto-${counter++}` }),
|
|
69
|
+
syntheticIdPropertyName: "@id",
|
|
70
|
+
};
|
|
71
|
+
const state = (0, deepSignal_1.deepSignal)({ root: {} }, options);
|
|
72
|
+
const patches = [];
|
|
73
|
+
const { stopListening: stop } = (0, watch_1.watch)(state, ({ patches: batch }) => patches.push(batch));
|
|
74
|
+
state.root.level1 = {
|
|
75
|
+
level2: {
|
|
76
|
+
level3: { value: "deep" },
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
await Promise.resolve();
|
|
80
|
+
// Check all levels have @id
|
|
81
|
+
(0, vitest_1.expect)(state.root.level1["@id"]).toBeDefined();
|
|
82
|
+
(0, vitest_1.expect)(state.root.level1.level2["@id"]).toBeDefined();
|
|
83
|
+
(0, vitest_1.expect)(state.root.level1.level2.level3["@id"]).toBeDefined();
|
|
84
|
+
// Check patches were emitted for all @id fields
|
|
85
|
+
const flat = patches.flat().map((p) => p.path.join("."));
|
|
86
|
+
(0, vitest_1.expect)(flat).toContain("root.level1.@id");
|
|
87
|
+
(0, vitest_1.expect)(flat).toContain("root.level1.level2.@id");
|
|
88
|
+
(0, vitest_1.expect)(flat).toContain("root.level1.level2.level3.@id");
|
|
89
|
+
stop();
|
|
90
|
+
});
|
|
91
|
+
(0, vitest_1.it)("does not add @id when option is false", () => {
|
|
92
|
+
const state = (0, deepSignal_1.deepSignal)({ data: { nested: {} } });
|
|
93
|
+
// Should not have @id
|
|
94
|
+
(0, vitest_1.expect)("@id" in state.data).toBe(false);
|
|
95
|
+
(0, vitest_1.expect)("@id" in state.data.nested).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
(0, vitest_1.it)("adds @id to objects in arrays", async () => {
|
|
98
|
+
let counter = 200;
|
|
99
|
+
const options = {
|
|
100
|
+
propGenerator: () => ({ syntheticId: `arr-${counter++}` }),
|
|
101
|
+
syntheticIdPropertyName: "@id",
|
|
102
|
+
};
|
|
103
|
+
const state = (0, deepSignal_1.deepSignal)({ items: [] }, options);
|
|
104
|
+
const patches = [];
|
|
105
|
+
const { stopListening: stop } = (0, watch_1.watch)(state, ({ patches: batch }) => patches.push(batch));
|
|
106
|
+
state.items.push({ name: "Item 1" }, { name: "Item 2" });
|
|
107
|
+
await Promise.resolve();
|
|
108
|
+
// Both items should have @id
|
|
109
|
+
(0, vitest_1.expect)(state.items[0]["@id"]).toBeDefined();
|
|
110
|
+
(0, vitest_1.expect)(state.items[1]["@id"]).toBeDefined();
|
|
111
|
+
// Check patches
|
|
112
|
+
const flat = patches.flat().map((p) => p.path.join("."));
|
|
113
|
+
(0, vitest_1.expect)(flat).toContain("items.0.@id");
|
|
114
|
+
(0, vitest_1.expect)(flat).toContain("items.1.@id");
|
|
115
|
+
stop();
|
|
116
|
+
});
|
|
117
|
+
(0, vitest_1.it)("adds @id to objects in Sets", async () => {
|
|
118
|
+
const options = {
|
|
119
|
+
propGenerator: () => ({
|
|
120
|
+
syntheticId: `gen-${Math.random().toString(36).substr(2, 9)}`,
|
|
121
|
+
}),
|
|
122
|
+
syntheticIdPropertyName: "@id",
|
|
123
|
+
};
|
|
124
|
+
const state = (0, deepSignal_1.deepSignal)({ s: new Set() }, options);
|
|
125
|
+
const patches = [];
|
|
126
|
+
const { stopListening: stop } = (0, watch_1.watch)(state, ({ patches: batch }) => patches.push(batch));
|
|
127
|
+
const obj1 = { value: 1 };
|
|
128
|
+
const obj2 = { value: 2 };
|
|
129
|
+
state.s.add(obj1);
|
|
130
|
+
state.s.add(obj2);
|
|
131
|
+
await Promise.resolve();
|
|
132
|
+
// Get proxied objects from Set
|
|
133
|
+
const proxiedObjs = Array.from(state.s);
|
|
134
|
+
(0, vitest_1.expect)(proxiedObjs[0]["@id"]).toBeDefined();
|
|
135
|
+
(0, vitest_1.expect)(proxiedObjs[1]["@id"]).toBeDefined();
|
|
136
|
+
// @id should be used as synthetic key in paths
|
|
137
|
+
const flat = patches.flat().map((p) => p.path.join("."));
|
|
138
|
+
const obj1Id = proxiedObjs[0]["@id"];
|
|
139
|
+
const obj2Id = proxiedObjs[1]["@id"];
|
|
140
|
+
(0, vitest_1.expect)(flat.some((p) => p.startsWith(`s.${obj1Id}`))).toBe(true);
|
|
141
|
+
(0, vitest_1.expect)(flat.some((p) => p.startsWith(`s.${obj2Id}`))).toBe(true);
|
|
142
|
+
stop();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
(0, vitest_1.describe)("@id property behavior", () => {
|
|
146
|
+
(0, vitest_1.it)("makes @id readonly", () => {
|
|
147
|
+
const options = {
|
|
148
|
+
syntheticIdPropertyName: "@id",
|
|
149
|
+
readOnlyProps: ["@id"],
|
|
150
|
+
};
|
|
151
|
+
const state = (0, deepSignal_1.deepSignal)({ obj: {} }, options);
|
|
152
|
+
state.obj.data = { value: 1 };
|
|
153
|
+
// Attempting to modify @id should throw
|
|
154
|
+
(0, vitest_1.expect)(() => {
|
|
155
|
+
state.obj.data["@id"] = "new-id";
|
|
156
|
+
}).toThrow("Cannot modify readonly property '@id'");
|
|
157
|
+
});
|
|
158
|
+
(0, vitest_1.it)("makes @id enumerable", () => {
|
|
159
|
+
let counter = 300;
|
|
160
|
+
const options = {
|
|
161
|
+
propGenerator: () => ({ syntheticId: `enum-${counter++}` }),
|
|
162
|
+
syntheticIdPropertyName: "@id",
|
|
163
|
+
};
|
|
164
|
+
const state = (0, deepSignal_1.deepSignal)({ obj: {} }, options);
|
|
165
|
+
state.obj.data = { value: 1 };
|
|
166
|
+
// @id should show up in Object.keys()
|
|
167
|
+
const keys = Object.keys(state.obj.data);
|
|
168
|
+
(0, vitest_1.expect)(keys).toContain("@id");
|
|
169
|
+
});
|
|
170
|
+
(0, vitest_1.it)("emits patches for @id even on objects with existing @id", async () => {
|
|
171
|
+
const options = {
|
|
172
|
+
syntheticIdPropertyName: "@id",
|
|
173
|
+
};
|
|
174
|
+
const state = (0, deepSignal_1.deepSignal)({ container: {} }, options);
|
|
175
|
+
const patches = [];
|
|
176
|
+
const { stopListening: stop } = (0, watch_1.watch)(state, ({ patches: batch }) => patches.push(batch));
|
|
177
|
+
// Object already has @id before being added
|
|
178
|
+
const objWithId = { "@id": "pre-existing", data: "test" };
|
|
179
|
+
state.container.item = objWithId;
|
|
180
|
+
await Promise.resolve();
|
|
181
|
+
const flat = patches.flat().map((p) => p.path.join("."));
|
|
182
|
+
// Patch should still be emitted for @id
|
|
183
|
+
(0, vitest_1.expect)(flat).toContain("container.item.@id");
|
|
184
|
+
// Verify the value in the patch
|
|
185
|
+
const idPatch = patches
|
|
186
|
+
.flat()
|
|
187
|
+
.find((p) => p.path.join(".") === "container.item.@id");
|
|
188
|
+
(0, vitest_1.expect)(idPatch.value).toBe("pre-existing");
|
|
189
|
+
stop();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
(0, vitest_1.describe)("options inheritance", () => {
|
|
193
|
+
(0, vitest_1.it)("child objects inherit options from root", async () => {
|
|
194
|
+
let idCounter = 5000;
|
|
195
|
+
const options = {
|
|
196
|
+
propGenerator: () => ({
|
|
197
|
+
syntheticId: `inherited-${idCounter++}`,
|
|
198
|
+
}),
|
|
199
|
+
syntheticIdPropertyName: "@id",
|
|
200
|
+
};
|
|
201
|
+
const state = (0, deepSignal_1.deepSignal)({ root: {} }, options);
|
|
202
|
+
// Add nested structure
|
|
203
|
+
state.root.child = {
|
|
204
|
+
grandchild: {
|
|
205
|
+
value: "nested",
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
// All should have IDs generated by the custom generator
|
|
209
|
+
(0, vitest_1.expect)(state.root.child["@id"]).toMatch(/^inherited-/);
|
|
210
|
+
(0, vitest_1.expect)(state.root.child.grandchild["@id"]).toMatch(/^inherited-/);
|
|
211
|
+
});
|
|
212
|
+
(0, vitest_1.it)("objects added to Sets inherit options", async () => {
|
|
213
|
+
let counter = 9000;
|
|
214
|
+
const options = {
|
|
215
|
+
propGenerator: () => ({
|
|
216
|
+
syntheticId: `set-child-${counter++}`,
|
|
217
|
+
}),
|
|
218
|
+
syntheticIdPropertyName: "@id",
|
|
219
|
+
};
|
|
220
|
+
const state = (0, deepSignal_1.deepSignal)({ s: new Set() }, options);
|
|
221
|
+
const obj = { nested: { value: 1 } };
|
|
222
|
+
state.s.add(obj);
|
|
223
|
+
// Iterate to get proxied object
|
|
224
|
+
const proxied = Array.from(state.s)[0];
|
|
225
|
+
// Object and nested object should have custom IDs
|
|
226
|
+
(0, vitest_1.expect)(proxied["@id"]).toMatch(/^set-child-/);
|
|
227
|
+
(0, vitest_1.expect)(proxied.nested["@id"]).toMatch(/^set-child-/);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../src/test/lib/index.test.ts"],"names":[],"mappings":""}
|