@ng-org/alien-deepsignals 0.1.2-alpha.5 → 0.1.2-alpha.7
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 +55 -374
- package/dist/core.d.ts +66 -60
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +70 -82
- package/dist/deepSignal.d.ts +19 -5
- package/dist/deepSignal.d.ts.map +1 -1
- package/dist/deepSignal.js +214 -141
- package/dist/effect.d.ts.map +1 -1
- package/dist/effect.js +8 -1
- package/dist/hooks/react/useDeepSignal.d.ts +1 -1
- package/dist/hooks/react/useDeepSignal.d.ts.map +1 -1
- package/dist/hooks/react/useDeepSignal.js +5 -2
- package/dist/hooks/svelte/index.d.ts +0 -1
- package/dist/hooks/svelte/index.d.ts.map +1 -1
- package/dist/hooks/svelte/useDeepSignal.svelte.d.ts +3 -15
- package/dist/hooks/svelte/useDeepSignal.svelte.d.ts.map +1 -1
- package/dist/hooks/svelte/useDeepSignal.svelte.js +29 -63
- package/dist/hooks/svelte4/index.d.ts +4 -0
- package/dist/hooks/svelte4/index.d.ts.map +1 -0
- package/dist/hooks/svelte4/index.js +8 -0
- package/dist/hooks/svelte4/useDeepSignal.svelte.d.ts +27 -0
- package/dist/hooks/svelte4/useDeepSignal.svelte.d.ts.map +1 -0
- package/dist/hooks/svelte4/useDeepSignal.svelte.js +91 -0
- package/dist/hooks/vue/useDeepSignal.d.ts +2 -2
- package/dist/hooks/vue/useDeepSignal.d.ts.map +1 -1
- package/dist/hooks/vue/useDeepSignal.js +22 -74
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/test/frontend/astro-app/src/components/ReactPanel.d.ts.map +1 -1
- package/dist/test/frontend/astro-app/src/components/{ReactPanel.js → ReactPanel.jsx} +56 -45
- package/dist/test/frontend/playwright/crossFrameworkHooks.spec.js +13 -3
- package/dist/test/frontend/utils/mockData.d.ts.map +1 -1
- package/dist/test/frontend/utils/mockData.js +13 -4
- package/dist/test/lib/index.test.js +16 -3
- package/dist/test/lib/misc.test.js +104 -1
- package/dist/test/lib/watch.test.js +2 -11
- package/dist/types.d.ts +94 -19
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +9 -0
- package/dist/watch.d.ts +37 -0
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +35 -0
- package/package.json +8 -5
- package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.d.ts +0 -4
- package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.d.ts.map +0 -1
- package/dist/test/frontend/astro-app/src/components/PerfSuiteClient.js +0 -225
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.d.ts +0 -4
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.d.ts.map +0 -1
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfDeep.js +0 -150
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.d.ts +0 -4
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.d.ts.map +0 -1
- package/dist/test/frontend/astro-app/src/components/perf/react/ReactPerfNative.js +0 -184
- package/dist/test/frontend/playwright/perfSuite.spec.d.ts +0 -2
- package/dist/test/frontend/playwright/perfSuite.spec.d.ts.map +0 -1
- package/dist/test/frontend/playwright/perfSuite.spec.js +0 -128
- package/dist/test/frontend/utils/perfScenarios.d.ts +0 -15
- package/dist/test/frontend/utils/perfScenarios.d.ts.map +0 -1
- package/dist/test/frontend/utils/perfScenarios.js +0 -287
- package/dist/test/lib/core.test.d.ts +0 -2
- package/dist/test/lib/core.test.d.ts.map +0 -1
- package/dist/test/lib/core.test.js +0 -53
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
const react_1 = __importStar(require("react"));
|
|
37
|
-
const mockData_1 = require("../../../../../utils/mockData");
|
|
38
|
-
const renderMetrics_1 = require("../../../../../utils/renderMetrics");
|
|
39
|
-
const perfScenarios_1 = require("../../../../../utils/perfScenarios");
|
|
40
|
-
const ObjectRow = ({ entry, updateEntries }) => {
|
|
41
|
-
const rowRenderCount = (0, react_1.useRef)(0);
|
|
42
|
-
rowRenderCount.current += 1;
|
|
43
|
-
(0, renderMetrics_1.recordObjectRender)("react", entry["@id"], rowRenderCount.current);
|
|
44
|
-
return (<div className="object-row" data-entry-id={entry["@id"]} data-render-count={rowRenderCount.current}>
|
|
45
|
-
<span className="object-id">{entry["@id"]}</span>
|
|
46
|
-
<input type="text" data-role="label" value={entry.label} onChange={(event) => updateEntries((draft) => {
|
|
47
|
-
const target = draft.find((item) => item["@id"] === entry["@id"]);
|
|
48
|
-
if (target)
|
|
49
|
-
target.label = event.target.value;
|
|
50
|
-
})}/>
|
|
51
|
-
<input type="number" data-role="count-input" value={entry.count} onChange={(event) => updateEntries((draft) => {
|
|
52
|
-
const target = draft.find((item) => item["@id"] === entry["@id"]);
|
|
53
|
-
if (target)
|
|
54
|
-
target.count = Number(event.target.value || 0);
|
|
55
|
-
})}/>
|
|
56
|
-
<span data-role="count">{entry.count}</span>
|
|
57
|
-
<button type="button" data-action="increment" onClick={() => updateEntries((draft) => {
|
|
58
|
-
const target = draft.find((item) => item["@id"] === entry["@id"]);
|
|
59
|
-
if (target)
|
|
60
|
-
target.count += 1;
|
|
61
|
-
})}>
|
|
62
|
-
+1
|
|
63
|
-
</button>
|
|
64
|
-
</div>);
|
|
65
|
-
};
|
|
66
|
-
const cloneInitialEntries = () => (0, mockData_1.cloneDefaultObjectSet)();
|
|
67
|
-
const ReactPerfNative = () => {
|
|
68
|
-
const [objectSet, setObjectSet] = (0, react_1.useState)(() => new Set(cloneInitialEntries()));
|
|
69
|
-
const renderCount = (0, react_1.useRef)(0);
|
|
70
|
-
const counterRef = (0, react_1.useRef)(0);
|
|
71
|
-
const [busy, setBusy] = (0, react_1.useState)(false);
|
|
72
|
-
renderCount.current += 1;
|
|
73
|
-
(0, react_1.useEffect)(() => {
|
|
74
|
-
(0, renderMetrics_1.recordRender)("react", renderCount.current);
|
|
75
|
-
});
|
|
76
|
-
const entries = (0, react_1.useMemo)(() => Array.from(objectSet.values()), [objectSet]);
|
|
77
|
-
const updateEntries = (0, react_1.useCallback)((mutate) => {
|
|
78
|
-
setObjectSet((prev) => {
|
|
79
|
-
const draft = Array.from(prev.values()).map((entry) => ({
|
|
80
|
-
...entry,
|
|
81
|
-
}));
|
|
82
|
-
mutate(draft);
|
|
83
|
-
return new Set(draft);
|
|
84
|
-
});
|
|
85
|
-
}, []);
|
|
86
|
-
(0, react_1.useEffect)(() => {
|
|
87
|
-
const dispose = (0, perfScenarios_1.registerScenarioAdapter)("react", "native", {
|
|
88
|
-
reset: () => {
|
|
89
|
-
setObjectSet(new Set(cloneInitialEntries()));
|
|
90
|
-
},
|
|
91
|
-
mutateExisting: (iterations) => {
|
|
92
|
-
updateEntries((draft) => {
|
|
93
|
-
if (!draft.length)
|
|
94
|
-
return;
|
|
95
|
-
for (let cycle = 0; cycle < iterations; cycle += 1) {
|
|
96
|
-
draft.forEach((entry, index) => {
|
|
97
|
-
entry.label = `POJO ${entry["@id"]} #${cycle}-${index}`;
|
|
98
|
-
entry.count += 2;
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
},
|
|
103
|
-
bulkMutate: (iterations) => {
|
|
104
|
-
updateEntries((draft) => {
|
|
105
|
-
if (!draft.length)
|
|
106
|
-
return;
|
|
107
|
-
for (let i = 0; i < iterations; i += 1) {
|
|
108
|
-
for (const entry of draft) {
|
|
109
|
-
entry.count += 2;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
},
|
|
114
|
-
batchAddRemove: (iterations) => {
|
|
115
|
-
const additions = [];
|
|
116
|
-
for (let i = 0; i < iterations; i += 1) {
|
|
117
|
-
counterRef.current += 1;
|
|
118
|
-
const id = `react-native-${counterRef.current}`;
|
|
119
|
-
additions.push({
|
|
120
|
-
"@id": id,
|
|
121
|
-
label: `Native ${id}`,
|
|
122
|
-
count: i,
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
updateEntries((draft) => {
|
|
126
|
-
draft.push(...additions);
|
|
127
|
-
});
|
|
128
|
-
updateEntries((draft) => {
|
|
129
|
-
const start = Math.max(draft.length - additions.length, 0);
|
|
130
|
-
draft.splice(start, additions.length);
|
|
131
|
-
});
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
return () => dispose();
|
|
135
|
-
}, [updateEntries]);
|
|
136
|
-
const handleAddEntry = () => {
|
|
137
|
-
counterRef.current += 1;
|
|
138
|
-
const id = `react-native-${counterRef.current}`;
|
|
139
|
-
updateEntries((draft) => {
|
|
140
|
-
draft.push({ "@id": id, label: `Native ${id}`, count: 0 });
|
|
141
|
-
});
|
|
142
|
-
};
|
|
143
|
-
const handleRemoveEntry = () => {
|
|
144
|
-
updateEntries((draft) => {
|
|
145
|
-
draft.pop();
|
|
146
|
-
});
|
|
147
|
-
};
|
|
148
|
-
const handleRunScenario = async () => {
|
|
149
|
-
try {
|
|
150
|
-
setBusy(true);
|
|
151
|
-
await (0, perfScenarios_1.runScenarioImmediately)("react", "native");
|
|
152
|
-
}
|
|
153
|
-
finally {
|
|
154
|
-
setBusy(false);
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
return (<section className="perf-panel react" data-field="objectSet">
|
|
158
|
-
<h2 className="title">react (native state)</h2>
|
|
159
|
-
<div className="render-meta" data-render-count={renderCount.current}>
|
|
160
|
-
Render #{renderCount.current}
|
|
161
|
-
</div>
|
|
162
|
-
<div className="field" data-field="objectSet">
|
|
163
|
-
<legend>objectSet entries</legend>
|
|
164
|
-
<div className="set-controls">
|
|
165
|
-
<span data-role="set-size">Size: {objectSet.size}</span>
|
|
166
|
-
<div>
|
|
167
|
-
<button type="button" onClick={handleAddEntry}>
|
|
168
|
-
Add entry
|
|
169
|
-
</button>
|
|
170
|
-
<button type="button" onClick={handleRemoveEntry}>
|
|
171
|
-
Remove entry
|
|
172
|
-
</button>
|
|
173
|
-
<button type="button" data-action="run-scenario" onClick={handleRunScenario} disabled={busy}>
|
|
174
|
-
{busy ? "Running…" : "Run perf scenario"}
|
|
175
|
-
</button>
|
|
176
|
-
</div>
|
|
177
|
-
</div>
|
|
178
|
-
<div className="object-set">
|
|
179
|
-
{entries.map((entry) => (<ObjectRow key={entry["@id"]} entry={entry} updateEntries={updateEntries}/>))}
|
|
180
|
-
</div>
|
|
181
|
-
</div>
|
|
182
|
-
</section>);
|
|
183
|
-
};
|
|
184
|
-
exports.default = ReactPerfNative;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"perfSuite.spec.d.ts","sourceRoot":"","sources":["../../../../src/test/frontend/playwright/perfSuite.spec.ts"],"names":[],"mappings":""}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const test_1 = require("@playwright/test");
|
|
4
|
-
const frameworks = ["react", "vue", "svelte"];
|
|
5
|
-
const scenarios = frameworks.flatMap((framework) => [
|
|
6
|
-
{ framework, variant: "deep" },
|
|
7
|
-
{ framework, variant: "native" },
|
|
8
|
-
]);
|
|
9
|
-
const waitForFrameworkReady = async (page, framework) => {
|
|
10
|
-
await page.waitForSelector(`.${framework} .title`, {
|
|
11
|
-
state: "visible",
|
|
12
|
-
});
|
|
13
|
-
await page.waitForFunction((name) => (window.renderEventCounts?.[name] ?? 0) > 0, framework, { timeout: 5_000 });
|
|
14
|
-
};
|
|
15
|
-
const waitForPerfSuiteReady = async (page, framework, variant) => {
|
|
16
|
-
await page.waitForFunction(({ framework, variant }) => Boolean(window.perfSuite?.runners?.[framework]?.[variant]), { framework, variant }, { timeout: 15_000 });
|
|
17
|
-
};
|
|
18
|
-
const navigateToScenario = async (page, framework, variant) => {
|
|
19
|
-
await page.goto(`/perf?framework=${framework}&variant=${variant}`);
|
|
20
|
-
await waitForFrameworkReady(page, framework);
|
|
21
|
-
await waitForPerfSuiteReady(page, framework, variant);
|
|
22
|
-
};
|
|
23
|
-
const runScenario = async (page, framework, variant, counts) => {
|
|
24
|
-
return page.evaluate(({ framework, variant, counts }) => window.perfSuite?.runScenario?.(framework, variant, counts), { framework, variant, counts });
|
|
25
|
-
};
|
|
26
|
-
const formatDuration = (value) => typeof value === "number" ? `${value.toFixed(1)}ms` : "n/a";
|
|
27
|
-
const logScenarioOverview = (framework, variant, result) => {
|
|
28
|
-
if (!result) {
|
|
29
|
-
// eslint-disable-next-line no-console
|
|
30
|
-
console.log(`[perfSuite] ${framework}/${variant} produced no result`);
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
const mutate = result.blocks?.mutateExisting;
|
|
34
|
-
const renderCount = mutate?.renderCounts?.[framework] ?? 0;
|
|
35
|
-
const touchedIds = Object.keys(mutate?.objectRenderCounts?.[framework] ?? {});
|
|
36
|
-
const sample = touchedIds.slice(0, 5);
|
|
37
|
-
const sampleDisplay = sample.length
|
|
38
|
-
? ` [${sample.join(", ")}${touchedIds.length > sample.length ? ", ..." : ""}]`
|
|
39
|
-
: "";
|
|
40
|
-
// eslint-disable-next-line no-console
|
|
41
|
-
console.log(`[perfSuite] ${framework}/${variant} total=${formatDuration(result.totalDuration)} mutate=${formatDuration(mutate?.duration)} renders=${renderCount} touched=${touchedIds.length}${sampleDisplay}`);
|
|
42
|
-
};
|
|
43
|
-
test_1.test.describe("perfSuite object-set scenarios", () => {
|
|
44
|
-
const baseCounts = {
|
|
45
|
-
primitives: 96,
|
|
46
|
-
nested: 64,
|
|
47
|
-
arrays: 40,
|
|
48
|
-
sets: 40,
|
|
49
|
-
objectSet: 48,
|
|
50
|
-
};
|
|
51
|
-
for (const { framework, variant } of scenarios) {
|
|
52
|
-
(0, test_1.test)(`${framework}/${variant} runner reports block metrics`, async ({ page, }) => {
|
|
53
|
-
await navigateToScenario(page, framework, variant);
|
|
54
|
-
if (framework === "react" && variant === "deep") {
|
|
55
|
-
const debugInfo = await page.evaluate(() => ({
|
|
56
|
-
identity: window.__reactSharedIdentity,
|
|
57
|
-
globalMatch: window.__reactSharedGlobal,
|
|
58
|
-
watchHits: window.__reactWatchHits,
|
|
59
|
-
adapterMatch: window.__adapterSharedIdentity,
|
|
60
|
-
version: window.sharedStateVersion,
|
|
61
|
-
}));
|
|
62
|
-
// eslint-disable-next-line no-console
|
|
63
|
-
console.log(`[debug] react/deep identity=${debugInfo.identity} global=${debugInfo.globalMatch} adapter=${debugInfo.adapterMatch} watchHits=${debugInfo.watchHits} version=${debugInfo.version}`);
|
|
64
|
-
}
|
|
65
|
-
const result = await runScenario(page, framework, variant, baseCounts);
|
|
66
|
-
(0, test_1.expect)(result).toBeTruthy();
|
|
67
|
-
const mutateBlock = result?.blocks?.mutateExisting;
|
|
68
|
-
logScenarioOverview(framework, variant, result);
|
|
69
|
-
(0, test_1.expect)(mutateBlock?.duration ?? 0).toBeGreaterThan(0);
|
|
70
|
-
const alphaRenders = mutateBlock?.objectRenderCounts?.[framework]?.["urn:object:alpha"] ?? 0;
|
|
71
|
-
(0, test_1.expect)(alphaRenders).toBeGreaterThan(0);
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
(0, test_1.test)("objectSet counts are configurable (react/deep)", async ({ page }) => {
|
|
75
|
-
const framework = "react";
|
|
76
|
-
const variant = "deep";
|
|
77
|
-
await navigateToScenario(page, framework, variant);
|
|
78
|
-
const light = await runScenario(page, framework, variant, {
|
|
79
|
-
objectSet: 1,
|
|
80
|
-
});
|
|
81
|
-
const heavy = await runScenario(page, framework, variant, {
|
|
82
|
-
objectSet: 8,
|
|
83
|
-
});
|
|
84
|
-
(0, test_1.expect)(light).toBeTruthy();
|
|
85
|
-
(0, test_1.expect)(heavy).toBeTruthy();
|
|
86
|
-
const lightAlpha = light?.blocks?.mutateExisting?.objectRenderCounts?.react?.["urn:object:alpha"] ?? 0;
|
|
87
|
-
const heavyAlpha = heavy?.blocks?.mutateExisting?.objectRenderCounts?.react?.["urn:object:alpha"] ?? 0;
|
|
88
|
-
(0, test_1.expect)(heavyAlpha).toBeGreaterThanOrEqual(lightAlpha);
|
|
89
|
-
});
|
|
90
|
-
for (const framework of frameworks) {
|
|
91
|
-
(0, test_1.test)(`${framework}/deep only re-renders touched object`, async ({ page, }) => {
|
|
92
|
-
const variant = "deep";
|
|
93
|
-
await navigateToScenario(page, framework, variant);
|
|
94
|
-
const targetId = "urn:object:alpha";
|
|
95
|
-
const peerId = "urn:object:beta";
|
|
96
|
-
const targetRow = page.locator(`[data-entry-id="${targetId}"]`);
|
|
97
|
-
const peerRow = page.locator(`[data-entry-id="${peerId}"]`);
|
|
98
|
-
await Promise.all([targetRow.waitFor(), peerRow.waitFor()]);
|
|
99
|
-
const readRenderCount = async (locator) => Number((await locator.getAttribute("data-render-count")) ?? 0);
|
|
100
|
-
const targetBefore = await readRenderCount(targetRow);
|
|
101
|
-
const peerBefore = await readRenderCount(peerRow);
|
|
102
|
-
await page.evaluate((id) => {
|
|
103
|
-
const state = window.sharedState;
|
|
104
|
-
const entry = Array.from(state.objectSet.values()).find((item) => item["@id"] === id);
|
|
105
|
-
if (!entry)
|
|
106
|
-
throw new Error("Entry not found");
|
|
107
|
-
entry.count += 1;
|
|
108
|
-
}, targetId);
|
|
109
|
-
await page.waitForFunction(({ selector, before }) => {
|
|
110
|
-
const element = document.querySelector(selector);
|
|
111
|
-
if (!element)
|
|
112
|
-
return false;
|
|
113
|
-
const count = Number(element.getAttribute("data-render-count") ?? "0");
|
|
114
|
-
return count > before;
|
|
115
|
-
}, {
|
|
116
|
-
selector: `[data-entry-id="${targetId}"]`,
|
|
117
|
-
before: targetBefore,
|
|
118
|
-
});
|
|
119
|
-
const targetAfter = await readRenderCount(targetRow);
|
|
120
|
-
const peerAfter = await readRenderCount(peerRow);
|
|
121
|
-
(0, test_1.expect)(targetAfter).toBeGreaterThan(targetBefore);
|
|
122
|
-
if (framework === "react") {
|
|
123
|
-
test_1.test.fail(true, "React reconciles the entire list because the single deepSignal subscription sits at the component root, so any Set mutation invalidates every row.");
|
|
124
|
-
}
|
|
125
|
-
(0, test_1.expect)(peerAfter).toBe(peerBefore);
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { PerfScenarioCounts, PerfScenarioResult, PerfVariant } from "./state";
|
|
2
|
-
declare const frameworks: readonly ["react", "vue", "svelte"];
|
|
3
|
-
type Framework = (typeof frameworks)[number];
|
|
4
|
-
type ScenarioAdapter = {
|
|
5
|
-
reset: () => void | Promise<void>;
|
|
6
|
-
mutateExisting: (iterations: number) => void | Promise<void>;
|
|
7
|
-
bulkMutate: (iterations: number) => void | Promise<void>;
|
|
8
|
-
batchAddRemove: (iterations: number) => void | Promise<void>;
|
|
9
|
-
};
|
|
10
|
-
export declare function registerScenarioAdapter(framework: Framework, variant: PerfVariant, adapter: ScenarioAdapter): () => void;
|
|
11
|
-
export declare function registerSharedStateAdapter(framework: Framework): () => void;
|
|
12
|
-
export declare function registerPerfRunners(): boolean;
|
|
13
|
-
export declare function runScenarioImmediately(framework: Framework, variant: PerfVariant, counts?: PerfScenarioCounts): Promise<PerfScenarioResult>;
|
|
14
|
-
export {};
|
|
15
|
-
//# sourceMappingURL=perfScenarios.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"perfScenarios.d.ts","sourceRoot":"","sources":["../../../../src/test/frontend/utils/perfScenarios.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAER,kBAAkB,EAClB,kBAAkB,EAElB,WAAW,EACd,MAAM,SAAS,CAAC;AAQjB,QAAA,MAAM,UAAU,qCAAsC,CAAC;AACvD,KAAK,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7C,KAAK,eAAe,GAAG;IACnB,KAAK,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,cAAc,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,UAAU,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,cAAc,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE,CAAC;AAqQF,wBAAgB,uBAAuB,CACnC,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,eAAe,cAS3B;AA6ED,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,SAAS,cAM9D;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAgB7C;AAED,wBAAsB,sBAAsB,CACxC,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,WAAW,EACpB,MAAM,CAAC,EAAE,kBAAkB,+BAS9B"}
|
|
@@ -1,287 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"core.test.d.ts","sourceRoot":"","sources":["../../../src/test/lib/core.test.ts"],"names":[],"mappings":""}
|