@ng-org/alien-deepsignals 0.1.2-alpha.2 → 0.1.2-alpha.4
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 +244 -100
- package/dist/hooks/react/useDeepSignal.d.ts +4 -4
- package/dist/hooks/react/useDeepSignal.d.ts.map +1 -1
- package/dist/hooks/react/useDeepSignal.js +22 -15
- 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/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +36 -19
- package/src/index.ts +5 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerScenarioAdapter = registerScenarioAdapter;
|
|
4
|
+
exports.registerSharedStateAdapter = registerSharedStateAdapter;
|
|
5
|
+
exports.registerPerfRunners = registerPerfRunners;
|
|
6
|
+
exports.runScenarioImmediately = runScenarioImmediately;
|
|
7
|
+
const state_1 = require("./state");
|
|
8
|
+
const renderMetrics_1 = require("./renderMetrics");
|
|
9
|
+
const frameworks = ["react", "vue", "svelte"];
|
|
10
|
+
const DEFAULT_COUNTS = {
|
|
11
|
+
primitives: 200,
|
|
12
|
+
nested: 120,
|
|
13
|
+
arrays: 80,
|
|
14
|
+
sets: 64,
|
|
15
|
+
objectSet: 80,
|
|
16
|
+
warmupRuns: 0,
|
|
17
|
+
repeatRuns: 1,
|
|
18
|
+
};
|
|
19
|
+
const adapters = {};
|
|
20
|
+
const now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
21
|
+
const settle = () => new Promise((resolve) => {
|
|
22
|
+
if (typeof window !== "undefined" &&
|
|
23
|
+
typeof window.requestAnimationFrame === "function") {
|
|
24
|
+
window.requestAnimationFrame(() => resolve());
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
setTimeout(() => resolve(), 16);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const withDefaults = (counts) => ({
|
|
31
|
+
primitives: counts?.primitives ?? DEFAULT_COUNTS.primitives,
|
|
32
|
+
nested: counts?.nested ?? DEFAULT_COUNTS.nested,
|
|
33
|
+
arrays: counts?.arrays ?? DEFAULT_COUNTS.arrays,
|
|
34
|
+
sets: counts?.sets ?? DEFAULT_COUNTS.sets,
|
|
35
|
+
objectSet: counts?.objectSet ?? DEFAULT_COUNTS.objectSet,
|
|
36
|
+
warmupRuns: counts?.warmupRuns ?? DEFAULT_COUNTS.warmupRuns,
|
|
37
|
+
repeatRuns: counts?.repeatRuns ?? DEFAULT_COUNTS.repeatRuns,
|
|
38
|
+
});
|
|
39
|
+
const diffNumericMap = (after, before) => {
|
|
40
|
+
const keys = new Set([...Object.keys(after), ...Object.keys(before)]);
|
|
41
|
+
const result = {};
|
|
42
|
+
for (const key of keys) {
|
|
43
|
+
result[key] = (after[key] ?? 0) - (before[key] ?? 0);
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
const diffObjectRenderCounts = (after, before) => {
|
|
48
|
+
const scope = new Set([...Object.keys(after), ...Object.keys(before)]);
|
|
49
|
+
const result = {};
|
|
50
|
+
for (const framework of scope) {
|
|
51
|
+
const afterEntries = after[framework] ?? {};
|
|
52
|
+
const beforeEntries = before[framework] ?? {};
|
|
53
|
+
const ids = new Set([
|
|
54
|
+
...Object.keys(afterEntries),
|
|
55
|
+
...Object.keys(beforeEntries),
|
|
56
|
+
]);
|
|
57
|
+
const diff = {};
|
|
58
|
+
for (const id of ids) {
|
|
59
|
+
diff[id] = (afterEntries[id] ?? 0) - (beforeEntries[id] ?? 0);
|
|
60
|
+
}
|
|
61
|
+
result[framework] = diff;
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
};
|
|
65
|
+
const measureBlock = async (name, action) => {
|
|
66
|
+
const beforeRenders = (0, renderMetrics_1.snapshotRenderCounts)();
|
|
67
|
+
const beforeObjectRenders = (0, renderMetrics_1.snapshotObjectRenderCounts)();
|
|
68
|
+
const start = now();
|
|
69
|
+
await action();
|
|
70
|
+
await settle();
|
|
71
|
+
const duration = now() - start;
|
|
72
|
+
const afterRenders = (0, renderMetrics_1.snapshotRenderCounts)();
|
|
73
|
+
const afterObjectRenders = (0, renderMetrics_1.snapshotObjectRenderCounts)();
|
|
74
|
+
return {
|
|
75
|
+
name,
|
|
76
|
+
duration,
|
|
77
|
+
renderCounts: diffNumericMap(afterRenders, beforeRenders),
|
|
78
|
+
objectRenderCounts: diffObjectRenderCounts(afterObjectRenders, beforeObjectRenders),
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
const accumulateNumericMap = (target, delta) => {
|
|
82
|
+
for (const [key, value] of Object.entries(delta)) {
|
|
83
|
+
target[key] = (target[key] ?? 0) + value;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const accumulateObjectRenderCounts = (target, delta) => {
|
|
87
|
+
for (const [scope, entries] of Object.entries(delta)) {
|
|
88
|
+
const targetEntries = (target[scope] ??= {});
|
|
89
|
+
for (const [id, value] of Object.entries(entries)) {
|
|
90
|
+
targetEntries[id] = (targetEntries[id] ?? 0) + value;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const averageNumericMap = (source, divisor) => {
|
|
95
|
+
const result = {};
|
|
96
|
+
for (const [key, value] of Object.entries(source)) {
|
|
97
|
+
result[key] = Math.round(value / divisor);
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
};
|
|
101
|
+
const averageObjectRenderCounts = (source, divisor) => {
|
|
102
|
+
const result = {};
|
|
103
|
+
for (const [scope, entries] of Object.entries(source)) {
|
|
104
|
+
const averaged = {};
|
|
105
|
+
for (const [id, value] of Object.entries(entries)) {
|
|
106
|
+
averaged[id] = Math.round(value / divisor);
|
|
107
|
+
}
|
|
108
|
+
result[scope] = averaged;
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
};
|
|
112
|
+
const getAdapter = (framework, variant) => adapters[framework]?.[variant];
|
|
113
|
+
const executeScenarioRun = async (framework, variant, adapter, resolved, runIndex) => {
|
|
114
|
+
await adapter.reset();
|
|
115
|
+
await settle();
|
|
116
|
+
const blocks = {};
|
|
117
|
+
blocks.mutateExisting = await measureBlock("mutateExisting", () => adapter.mutateExisting(resolved.objectSet));
|
|
118
|
+
blocks.bulkMutate = await measureBlock("bulkMutate", () => adapter.bulkMutate(resolved.primitives));
|
|
119
|
+
blocks.batchAddRemove = await measureBlock("batchAddRemove", () => adapter.batchAddRemove(resolved.sets));
|
|
120
|
+
const totalDuration = Object.values(blocks).reduce((sum, block) => sum + block.duration, 0);
|
|
121
|
+
const run = {
|
|
122
|
+
runIndex,
|
|
123
|
+
totalDuration,
|
|
124
|
+
blocks,
|
|
125
|
+
};
|
|
126
|
+
return run;
|
|
127
|
+
};
|
|
128
|
+
async function runScenarioWithAdapter(framework, variant, adapter, counts) {
|
|
129
|
+
const resolved = withDefaults(counts);
|
|
130
|
+
const warmups = Math.max(0, resolved.warmupRuns);
|
|
131
|
+
const repeats = Math.max(1, resolved.repeatRuns);
|
|
132
|
+
for (let i = 0; i < warmups; i += 1) {
|
|
133
|
+
await executeScenarioRun(framework, variant, adapter, resolved, i);
|
|
134
|
+
}
|
|
135
|
+
const runs = [];
|
|
136
|
+
for (let i = 0; i < repeats; i += 1) {
|
|
137
|
+
runs.push(await executeScenarioRun(framework, variant, adapter, resolved, warmups + i));
|
|
138
|
+
}
|
|
139
|
+
const aggregatedBlocks = {};
|
|
140
|
+
const blockSums = {};
|
|
141
|
+
for (const run of runs) {
|
|
142
|
+
Object.entries(run.blocks).forEach(([name, block]) => {
|
|
143
|
+
const accumulator = (blockSums[name] ??= {
|
|
144
|
+
name,
|
|
145
|
+
duration: 0,
|
|
146
|
+
renderCounts: {},
|
|
147
|
+
objectRenderCounts: {},
|
|
148
|
+
});
|
|
149
|
+
accumulator.duration += block.duration;
|
|
150
|
+
accumulateNumericMap(accumulator.renderCounts, block.renderCounts);
|
|
151
|
+
accumulateObjectRenderCounts(accumulator.objectRenderCounts, block.objectRenderCounts);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
Object.entries(blockSums).forEach(([name, block]) => {
|
|
155
|
+
aggregatedBlocks[name] = {
|
|
156
|
+
name,
|
|
157
|
+
duration: block.duration / repeats,
|
|
158
|
+
renderCounts: averageNumericMap(block.renderCounts, repeats),
|
|
159
|
+
objectRenderCounts: averageObjectRenderCounts(block.objectRenderCounts, repeats),
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
const averageTotalDuration = runs.reduce((sum, run) => sum + run.totalDuration, 0) / repeats;
|
|
163
|
+
const result = {
|
|
164
|
+
framework,
|
|
165
|
+
variant,
|
|
166
|
+
totalDuration: averageTotalDuration,
|
|
167
|
+
blocks: aggregatedBlocks,
|
|
168
|
+
runCount: repeats,
|
|
169
|
+
warmupCount: warmups,
|
|
170
|
+
runs,
|
|
171
|
+
completedAt: Date.now(),
|
|
172
|
+
};
|
|
173
|
+
if (typeof window !== "undefined") {
|
|
174
|
+
window.perfSuite?.publishResult?.(result);
|
|
175
|
+
}
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
function registerScenarioAdapter(framework, variant, adapter) {
|
|
179
|
+
(adapters[framework] ??= {})[variant] = adapter;
|
|
180
|
+
registerPerfRunners();
|
|
181
|
+
return () => {
|
|
182
|
+
if (adapters[framework]?.[variant] === adapter) {
|
|
183
|
+
delete adapters[framework][variant];
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const createSharedStateAdapter = () => {
|
|
188
|
+
if (typeof window !== "undefined") {
|
|
189
|
+
window.__adapterSharedIdentity =
|
|
190
|
+
state_1.sharedState === window.sharedState;
|
|
191
|
+
}
|
|
192
|
+
let syntheticCounter = 0;
|
|
193
|
+
const generateId = () => `perf-object-${++syntheticCounter}`;
|
|
194
|
+
const bumpGlobalNumericFields = () => {
|
|
195
|
+
state_1.sharedState.numValue += 2;
|
|
196
|
+
state_1.sharedState.hiddenValue += 2;
|
|
197
|
+
state_1.sharedState.objectValue.nestedNum += 2;
|
|
198
|
+
state_1.sharedState.count += 2;
|
|
199
|
+
const baseArray = state_1.sharedState.arrayValue;
|
|
200
|
+
for (let i = 0; i < baseArray.length; i += 1) {
|
|
201
|
+
baseArray[i] += 2;
|
|
202
|
+
}
|
|
203
|
+
const nestedArray = state_1.sharedState.objectValue.nestedArray;
|
|
204
|
+
for (let i = 0; i < nestedArray.length; i += 1) {
|
|
205
|
+
nestedArray[i] += 2;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
return {
|
|
209
|
+
reset() {
|
|
210
|
+
(0, state_1.resetSharedState)();
|
|
211
|
+
},
|
|
212
|
+
mutateExisting(iterations) {
|
|
213
|
+
const entries = Array.from(state_1.sharedState.objectSet.values());
|
|
214
|
+
if (!entries.length)
|
|
215
|
+
return;
|
|
216
|
+
for (let cycle = 0; cycle < iterations; cycle += 1) {
|
|
217
|
+
let idx = 0;
|
|
218
|
+
for (const entry of entries) {
|
|
219
|
+
entry.label = `Object ${entry["@id"]} #${cycle}-${idx}`;
|
|
220
|
+
entry.count += 2;
|
|
221
|
+
idx += 1;
|
|
222
|
+
}
|
|
223
|
+
bumpGlobalNumericFields();
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
bulkMutate(iterations) {
|
|
227
|
+
const entries = Array.from(state_1.sharedState.objectSet.values());
|
|
228
|
+
if (!entries.length)
|
|
229
|
+
return;
|
|
230
|
+
for (let i = 0; i < iterations; i += 1) {
|
|
231
|
+
for (const entry of entries) {
|
|
232
|
+
entry.count += 2;
|
|
233
|
+
}
|
|
234
|
+
bumpGlobalNumericFields();
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
batchAddRemove(iterations) {
|
|
238
|
+
const created = [];
|
|
239
|
+
for (let i = 0; i < iterations; i += 1) {
|
|
240
|
+
const id = generateId();
|
|
241
|
+
state_1.sharedState.objectSet.add({
|
|
242
|
+
"@id": id,
|
|
243
|
+
label: `Perf entry ${id}`,
|
|
244
|
+
count: i,
|
|
245
|
+
});
|
|
246
|
+
created.push(id);
|
|
247
|
+
}
|
|
248
|
+
for (const id of created) {
|
|
249
|
+
for (const entry of state_1.sharedState.objectSet) {
|
|
250
|
+
if (entry["@id"] === id) {
|
|
251
|
+
state_1.sharedState.objectSet.delete(entry);
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
function registerSharedStateAdapter(framework) {
|
|
260
|
+
return registerScenarioAdapter(framework, "deep", createSharedStateAdapter());
|
|
261
|
+
}
|
|
262
|
+
function registerPerfRunners() {
|
|
263
|
+
if (typeof window === "undefined")
|
|
264
|
+
return false;
|
|
265
|
+
const suite = window.perfSuite;
|
|
266
|
+
if (!suite?.registerRunner)
|
|
267
|
+
return false;
|
|
268
|
+
Object.keys(adapters).forEach((framework) => {
|
|
269
|
+
const variants = adapters[framework];
|
|
270
|
+
if (!variants)
|
|
271
|
+
return;
|
|
272
|
+
Object.keys(variants).forEach((variant) => {
|
|
273
|
+
const adapter = variants[variant];
|
|
274
|
+
if (!adapter)
|
|
275
|
+
return;
|
|
276
|
+
suite.registerRunner(framework, variant, (counts) => runScenarioWithAdapter(framework, variant, adapter, counts));
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
async function runScenarioImmediately(framework, variant, counts) {
|
|
282
|
+
const adapter = getAdapter(framework, variant);
|
|
283
|
+
if (!adapter) {
|
|
284
|
+
throw new Error(`No perf scenario adapter registered for ${framework}:${variant}`);
|
|
285
|
+
}
|
|
286
|
+
return runScenarioWithAdapter(framework, variant, adapter, counts);
|
|
287
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
interface Window {
|
|
3
|
+
renderCounts?: Record<string, number>;
|
|
4
|
+
renderEventCounts?: Record<string, number>;
|
|
5
|
+
objectRenderCounts?: Record<string, Record<string, number>>;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export declare function recordRender(framework: string, count: number): void;
|
|
9
|
+
export declare function getRenderCount(framework: string): number;
|
|
10
|
+
export declare function snapshotRenderCounts(): Record<string, number>;
|
|
11
|
+
export declare function recordObjectRender(framework: string, objectId: string, count: number): void;
|
|
12
|
+
export declare function snapshotObjectRenderCounts(): Record<string, Record<string, number>>;
|
|
13
|
+
//# sourceMappingURL=renderMetrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderMetrics.d.ts","sourceRoot":"","sources":["../../../../src/test/frontend/utils/renderMetrics.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,MAAM;QACZ,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACtC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3C,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;KAC/D;CACJ;AAQD,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAK5D;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI7D;AAED,wBAAgB,kBAAkB,CAC9B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,QAOhB;AAED,wBAAgB,0BAA0B,IAAI,MAAM,CAChD,MAAM,EACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CACzB,CAKA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.recordRender = recordRender;
|
|
4
|
+
exports.getRenderCount = getRenderCount;
|
|
5
|
+
exports.snapshotRenderCounts = snapshotRenderCounts;
|
|
6
|
+
exports.recordObjectRender = recordObjectRender;
|
|
7
|
+
exports.snapshotObjectRenderCounts = snapshotObjectRenderCounts;
|
|
8
|
+
const incrementRenderEvents = (framework) => {
|
|
9
|
+
if (typeof window === "undefined")
|
|
10
|
+
return;
|
|
11
|
+
const events = (window.renderEventCounts ??= {});
|
|
12
|
+
events[framework] = (events[framework] ?? 0) + 1;
|
|
13
|
+
};
|
|
14
|
+
function recordRender(framework, count) {
|
|
15
|
+
if (typeof window === "undefined")
|
|
16
|
+
return;
|
|
17
|
+
const store = (window.renderCounts ??= {});
|
|
18
|
+
store[framework] = count;
|
|
19
|
+
incrementRenderEvents(framework);
|
|
20
|
+
}
|
|
21
|
+
function getRenderCount(framework) {
|
|
22
|
+
if (typeof window === "undefined")
|
|
23
|
+
return 0;
|
|
24
|
+
return window.renderCounts?.[framework] ?? 0;
|
|
25
|
+
}
|
|
26
|
+
function snapshotRenderCounts() {
|
|
27
|
+
if (typeof window === "undefined")
|
|
28
|
+
return {};
|
|
29
|
+
const source = window.renderEventCounts ?? {};
|
|
30
|
+
return Object.fromEntries(Object.entries(source));
|
|
31
|
+
}
|
|
32
|
+
function recordObjectRender(framework, objectId, count) {
|
|
33
|
+
if (typeof window === "undefined")
|
|
34
|
+
return;
|
|
35
|
+
const host = (window.objectRenderCounts ??= {});
|
|
36
|
+
const entries = (host[framework] ??= {});
|
|
37
|
+
entries[objectId] = count;
|
|
38
|
+
incrementRenderEvents(framework);
|
|
39
|
+
}
|
|
40
|
+
function snapshotObjectRenderCounts() {
|
|
41
|
+
if (typeof window === "undefined")
|
|
42
|
+
return {};
|
|
43
|
+
const source = window.objectRenderCounts ?? {};
|
|
44
|
+
return JSON.parse(JSON.stringify(source));
|
|
45
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type TestState } from "./mockData.js";
|
|
2
|
+
declare global {
|
|
3
|
+
interface Window {
|
|
4
|
+
sharedState?: TestState;
|
|
5
|
+
testHarness?: {
|
|
6
|
+
resetState: () => void;
|
|
7
|
+
noopMutation: (path: string) => void;
|
|
8
|
+
getRenderCounts: () => Record<string, number>;
|
|
9
|
+
ready: boolean;
|
|
10
|
+
};
|
|
11
|
+
perfSuite?: {
|
|
12
|
+
registerRunner: (framework: string, variant: string, runner: (counts: PerfScenarioCounts) => Promise<PerfScenarioResult>) => void;
|
|
13
|
+
runScenario?: (framework: string, variant: string, counts: PerfScenarioCounts) => Promise<PerfScenarioResult | undefined>;
|
|
14
|
+
runners?: Record<string, Record<string, (counts: PerfScenarioCounts) => Promise<PerfScenarioResult>>>;
|
|
15
|
+
latestResults?: Record<string, Record<string, PerfScenarioResult>>;
|
|
16
|
+
listeners?: Set<(result: PerfScenarioResult) => void>;
|
|
17
|
+
publishResult?: (result: PerfScenarioResult) => void;
|
|
18
|
+
subscribe?: (listener: (result: PerfScenarioResult) => void) => () => void;
|
|
19
|
+
};
|
|
20
|
+
subscribeSharedState?: (listener: () => void) => () => void;
|
|
21
|
+
sharedStateVersion?: number;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export type PerfScenarioCounts = {
|
|
25
|
+
primitives?: number;
|
|
26
|
+
nested?: number;
|
|
27
|
+
arrays?: number;
|
|
28
|
+
sets?: number;
|
|
29
|
+
objectSet?: number;
|
|
30
|
+
warmupRuns?: number;
|
|
31
|
+
repeatRuns?: number;
|
|
32
|
+
};
|
|
33
|
+
export type PerfVariant = "deep" | "native";
|
|
34
|
+
export type PerfScenarioBlockResult = {
|
|
35
|
+
name: string;
|
|
36
|
+
duration: number;
|
|
37
|
+
renderCounts: Record<string, number>;
|
|
38
|
+
objectRenderCounts: Record<string, Record<string, number>>;
|
|
39
|
+
};
|
|
40
|
+
export type PerfScenarioRun = {
|
|
41
|
+
runIndex: number;
|
|
42
|
+
totalDuration: number;
|
|
43
|
+
blocks: Record<string, PerfScenarioBlockResult>;
|
|
44
|
+
};
|
|
45
|
+
export type PerfScenarioResult = {
|
|
46
|
+
framework: string;
|
|
47
|
+
variant: PerfVariant;
|
|
48
|
+
totalDuration: number;
|
|
49
|
+
blocks: Record<string, PerfScenarioBlockResult>;
|
|
50
|
+
runCount: number;
|
|
51
|
+
warmupCount: number;
|
|
52
|
+
runs?: PerfScenarioRun[];
|
|
53
|
+
completedAt?: number;
|
|
54
|
+
};
|
|
55
|
+
export declare const sharedState: import("../../../types.js").DeepSignalObject<TestState>;
|
|
56
|
+
export declare function resetSharedState(): void;
|
|
57
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../src/test/frontend/utils/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sharedState = void 0;
|
|
4
|
+
exports.resetSharedState = resetSharedState;
|
|
5
|
+
const deepSignal_js_1 = require("../../../deepSignal.js");
|
|
6
|
+
const mockData_js_1 = require("./mockData.js");
|
|
7
|
+
const paths_js_1 = require("./paths.js");
|
|
8
|
+
const createState = () => (0, deepSignal_js_1.deepSignal)((0, mockData_js_1.buildInitialState)());
|
|
9
|
+
exports.sharedState = createState();
|
|
10
|
+
const sharedStateListeners = new Set();
|
|
11
|
+
const notifySharedStateListeners = () => {
|
|
12
|
+
if (!sharedStateListeners.size)
|
|
13
|
+
return;
|
|
14
|
+
sharedStateListeners.forEach((listener) => {
|
|
15
|
+
try {
|
|
16
|
+
listener();
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
console.error("sharedState listener failed", error);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
const now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
24
|
+
const microtask = () => new Promise((resolve) => setTimeout(resolve, 0));
|
|
25
|
+
function resetSharedState() {
|
|
26
|
+
const next = (0, mockData_js_1.buildInitialState)();
|
|
27
|
+
for (const key of Object.keys(exports.sharedState)) {
|
|
28
|
+
delete exports.sharedState[key];
|
|
29
|
+
}
|
|
30
|
+
Object.assign(exports.sharedState, next);
|
|
31
|
+
}
|
|
32
|
+
const measure = async (mutator, settle = true) => {
|
|
33
|
+
const start = now();
|
|
34
|
+
mutator();
|
|
35
|
+
if (settle)
|
|
36
|
+
await microtask();
|
|
37
|
+
return now() - start;
|
|
38
|
+
};
|
|
39
|
+
if (typeof window !== "undefined") {
|
|
40
|
+
window.sharedState = exports.sharedState;
|
|
41
|
+
const harness = (window.testHarness ??= {
|
|
42
|
+
resetState: () => undefined,
|
|
43
|
+
noopMutation: () => undefined,
|
|
44
|
+
getRenderCounts: () => (window.renderCounts ?? {}),
|
|
45
|
+
ready: false,
|
|
46
|
+
});
|
|
47
|
+
harness.resetState = () => resetSharedState();
|
|
48
|
+
harness.noopMutation = (path) => {
|
|
49
|
+
const current = (0, paths_js_1.getByPath)(exports.sharedState, path);
|
|
50
|
+
(0, paths_js_1.setByPath)(exports.sharedState, path, current);
|
|
51
|
+
};
|
|
52
|
+
harness.ready = true;
|
|
53
|
+
const perfSuite = (window.perfSuite ??= {
|
|
54
|
+
runners: {},
|
|
55
|
+
latestResults: {},
|
|
56
|
+
listeners: new Set(),
|
|
57
|
+
registerRunner(framework, variant, runner) {
|
|
58
|
+
const runnerMap = (this.runners ??= {});
|
|
59
|
+
const frameworkRunners = (runnerMap[framework] ??= {});
|
|
60
|
+
frameworkRunners[variant] = runner;
|
|
61
|
+
},
|
|
62
|
+
publishResult(result) {
|
|
63
|
+
const latest = (this.latestResults ??= {});
|
|
64
|
+
(latest[result.framework] ??= {})[result.variant] = result;
|
|
65
|
+
this.listeners?.forEach((listener) => listener(result));
|
|
66
|
+
},
|
|
67
|
+
subscribe(listener) {
|
|
68
|
+
this.listeners?.add(listener);
|
|
69
|
+
return () => this.listeners?.delete(listener);
|
|
70
|
+
},
|
|
71
|
+
async runScenario(framework, variant, counts) {
|
|
72
|
+
const runnerMap = (this.runners ??= {});
|
|
73
|
+
const runner = runnerMap[framework]?.[variant];
|
|
74
|
+
if (!runner)
|
|
75
|
+
return undefined;
|
|
76
|
+
return runner(counts);
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.test.d.ts","sourceRoot":"","sources":["../../../src/test/lib/core.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,53 @@
|
|
|
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 core_1 = require("../../core");
|
|
14
|
+
const deepSignal_1 = require("../../deepSignal");
|
|
15
|
+
(0, vitest_1.describe)("core.ts coverage", () => {
|
|
16
|
+
(0, vitest_1.it)("signal tagging helpers (.value/.peek/.get/.set)", () => {
|
|
17
|
+
const s = (0, core_1.signal)(1);
|
|
18
|
+
(0, vitest_1.expect)((0, core_1.isSignal)(s)).toBe(true);
|
|
19
|
+
(0, vitest_1.expect)(s.value).toBe(1);
|
|
20
|
+
(0, vitest_1.expect)(s.peek()).toBe(1);
|
|
21
|
+
(0, vitest_1.expect)(s.get()).toBe(1);
|
|
22
|
+
s.set(2);
|
|
23
|
+
(0, vitest_1.expect)(s.value).toBe(2);
|
|
24
|
+
s.value = 3;
|
|
25
|
+
(0, vitest_1.expect)(s.peek()).toBe(3);
|
|
26
|
+
});
|
|
27
|
+
(0, vitest_1.it)("computed tagging helpers (.value/.peek/.get)", () => {
|
|
28
|
+
const s = (0, core_1.signal)(2);
|
|
29
|
+
const c = (0, core_1.computed)(() => s.value * 2);
|
|
30
|
+
(0, vitest_1.expect)((0, core_1.isSignal)(c)).toBe(true);
|
|
31
|
+
(0, vitest_1.expect)(c.value).toBe(4);
|
|
32
|
+
(0, vitest_1.expect)(c.peek()).toBe(4);
|
|
33
|
+
(0, vitest_1.expect)(c.get()).toBe(4);
|
|
34
|
+
s.value = 3;
|
|
35
|
+
(0, vitest_1.expect)(c.value).toBe(6);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
(0, vitest_1.describe)("deepSignal.ts extra branches", () => {
|
|
39
|
+
(0, vitest_1.it)("access well-known symbol property returns raw value and not a signal", () => {
|
|
40
|
+
const tag = Symbol.toStringTag;
|
|
41
|
+
const ds = (0, deepSignal_1.deepSignal)({ [tag]: "Custom", x: 1 });
|
|
42
|
+
const val = ds[tag];
|
|
43
|
+
(0, vitest_1.expect)(val).toBe("Custom");
|
|
44
|
+
});
|
|
45
|
+
(0, vitest_1.it)("access Set Symbol.iterator.toString() key path (skip branch)", () => {
|
|
46
|
+
const ds = (0, deepSignal_1.deepSignal)({ set: new Set([1]) });
|
|
47
|
+
const iterKey = Symbol.iterator.toString(); // 'Symbol(Symbol.iterator)'
|
|
48
|
+
// Accessing this string property triggers skip branch (no special handling needed)
|
|
49
|
+
const maybe = ds.set[iterKey];
|
|
50
|
+
// underlying Set likely has undefined for that string key
|
|
51
|
+
(0, vitest_1.expect)(maybe).toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deepSignalOptions.test.d.ts","sourceRoot":"","sources":["../../../src/test/lib/deepSignalOptions.test.ts"],"names":[],"mappings":""}
|