@askrjs/askr 0.3.0 → 0.4.0
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/CHANGELOG.md +874 -0
- package/README.md +61 -29
- package/capabilities.json +19 -9
- package/dist/actions/index.js +56 -12
- package/dist/benchmark.js +10 -15
- package/dist/boot/index.d.ts +3 -2
- package/dist/boot/index.js +1 -1
- package/dist/boot-DaoTYoCw.js +3989 -0
- package/dist/browser-activity-CB8e-V4f.js +220 -0
- package/dist/components/index.d.ts +8 -1
- package/dist/components/index.js +1 -43
- package/dist/{aria-hKjThHcp.js → compose-ref-DWzdbxsD.js} +41 -4
- package/dist/control/index.d.ts +2 -0
- package/dist/control/index.js +1 -1
- package/dist/control-nfpT2fbK.js +209 -0
- package/dist/core.d.ts +5 -9
- package/dist/csp-nonce-DwGw1A6Q.js +35 -0
- package/dist/data/index.js +3 -3
- package/dist/{data-2edW0jhF.js → data-BCwClJiF.js} +283 -94
- package/dist/data-runtime-C1bFAlba.js +242 -0
- package/dist/{deferred-CbgNhf67.js → deferred-bEEHHvic.js} +42 -20
- package/dist/domains/actions.d.ts +8 -3
- package/dist/domains/component.d.ts +1 -107
- package/dist/domains/context.d.ts +0 -15
- package/dist/domains/control.d.ts +36 -249
- package/dist/domains/data.d.ts +31 -13
- package/dist/domains/diagnostics.d.ts +1 -2
- package/dist/domains/lifecycle.d.ts +2 -5
- package/dist/domains/refs.d.ts +0 -2
- package/dist/domains/routing.d.ts +43 -8
- package/dist/domains/scheduler.d.ts +1 -76
- package/dist/domains/server.d.ts +5 -2
- package/dist/domains/state.d.ts +2 -5
- package/dist/domains/telemetry.d.ts +0 -2
- package/dist/elements.d.ts +214 -51
- package/dist/error-boundary-Bpwofwps.js +293 -0
- package/dist/foundations/icon/index.d.ts +0 -58
- package/dist/foundations/icon/index.js +2 -2
- package/dist/foundations/index.js +2 -1
- package/dist/foundations/interactions/index.js +1 -1
- package/dist/foundations/state/index.d.ts +3 -3
- package/dist/foundations/state/index.js +5 -3
- package/dist/foundations/structures/index.js +2 -1
- package/dist/foundations/utilities/index.d.ts +2 -0
- package/dist/foundations/utilities/index.js +1 -2
- package/dist/fx/index.d.ts +31 -58
- package/dist/fx/index.js +313 -195
- package/dist/hooks-BimKStr4.js +202 -0
- package/dist/index.d.ts +11 -74
- package/dist/index.js +8 -447
- package/dist/instance-BAnfmXOR.js +988 -0
- package/dist/{interactions-CTeACdT_.js → interactions-D2BoREb-.js} +3 -2
- package/dist/jsx-dev-runtime.d.ts +41 -18
- package/dist/jsx-dev-runtime.js +1 -6
- package/dist/{jsx-runtime-CPJXAOBF.js → jsx-runtime-C4idqHY0.js} +18 -7
- package/dist/jsx-runtime.js +1 -1
- package/dist/jsx.d.ts +61 -37
- package/dist/{manifest-D1P3UNWV.js → manifest-CW9gyNBY.js} +3 -3
- package/dist/{navigate-prC5V8XV.js → navigate-CP_0Zy5U.js} +454 -87
- package/dist/navigation.d.ts +12 -3
- package/dist/peer-types.d.ts +8 -0
- package/dist/portal-DuOKfnLa.js +349 -0
- package/dist/promise-Dz8iIHAm.js +16 -0
- package/dist/query-registry-WMCN0BJL.js +320 -0
- package/dist/readable-DFWOsTac.js +21 -0
- package/dist/{render-context-B7ohlh6t.js → render-context-DGHnEveC.js} +27 -11
- package/dist/render-sync-IC5lMFpq.js +1240 -0
- package/dist/resolution-Cx82-xjW.js +1244 -0
- package/dist/resource-CLjB7RBQ.js +377 -0
- package/dist/resources/index.d.ts +21 -11
- package/dist/resources/index.js +45 -244
- package/dist/router/index.d.ts +14 -5
- package/dist/router/index.js +22 -53
- package/dist/scope-CwpVSHVf.js +55 -0
- package/dist/{shared-kU8yvryp.js → shared-O2ZnmYAf.js} +1 -7
- package/dist/{snapshot-source-An4iKajG.js → snapshot-Cjx4N988.js} +6 -1
- package/dist/ssg/index.d.ts +1 -2
- package/dist/ssg/index.js +5 -13
- package/dist/ssr/index.d.ts +26 -28
- package/dist/ssr/index.js +4 -4
- package/dist/{ssr-CiNm6Vzl.js → ssr-BJ8ug7zH.js} +133 -122
- package/dist/ssr-DoV-vJ-5.js +229 -0
- package/dist/state-DWO1KzkX.js +232 -0
- package/dist/structures-D4dDMqHX.js +231 -0
- package/dist/testing/index.d.ts +8 -4
- package/dist/testing/index.js +29 -34
- package/dist/url-CnbfpX0K.js +185 -0
- package/dist/utilities.d.ts +48 -2
- package/dist/verify-hydration-DpVaDnqz.js +152 -0
- package/package.json +19 -9
- package/dist/access-CEA2pOry.js +0 -1046
- package/dist/activity-oy2YFTFF.js +0 -201
- package/dist/boot-BMBMWUGI.js +0 -1084
- package/dist/capabilities-Bbf0mlp9.js +0 -42
- package/dist/compose-ref-v0qGlHoN.js +0 -26
- package/dist/control-BR_mrX__.js +0 -173
- package/dist/control-C-ApnFs1.js +0 -20
- package/dist/csp-nonce-BfFWcd0w.js +0 -173
- package/dist/data-runtime-CxoY98lX.js +0 -149
- package/dist/default-portal-runtime-DvMvWJVp.js +0 -20
- package/dist/dom-renderer.d.ts +0 -77
- package/dist/domains/renderer.d.ts +0 -127
- package/dist/eager-control.d.ts +0 -8
- package/dist/effect-C3Zsc1k6.js +0 -292
- package/dist/for-state-BDykeOJL.js +0 -1753
- package/dist/instance-VAFYVJOa.js +0 -1027
- package/dist/notify-DxxuDPeY.js +0 -266
- package/dist/portal-w3oQ6oQU.js +0 -29
- package/dist/query-registry-LrYpQsDJ.js +0 -83
- package/dist/render-resolved-BC0W7_5b.js +0 -1123
- package/dist/renderer-CT0HeY58.js +0 -8623
- package/dist/resolution-3DskUkjG.js +0 -577
- package/dist/resource-18-sFRl9.js +0 -344
- package/dist/route-matching-CUzZ31gT.js +0 -551
- package/dist/router-internal-7nuNEHfG.js +0 -36
- package/dist/scope-Cq_iYbTR.js +0 -403
- package/dist/selector-BSPVkjKP.js +0 -311
- package/dist/ssr-EPjwJH5R.js +0 -29
- package/dist/state-BXJfudq1.js +0 -91
- package/dist/structures-CjEzfj4h.js +0 -654
- package/dist/timer-C7uqnJVt.js +0 -85
- package/dist/verify-hydration-ikaxotf7.js +0 -27
- package/dist/vnode-D24p6IW2.js +0 -7
package/dist/access-CEA2pOry.js
DELETED
|
@@ -1,1046 +0,0 @@
|
|
|
1
|
-
import { a as clearCurrentComponentScope, s as endComponentScope } from "./scope-Cq_iYbTR.js";
|
|
2
|
-
//#region src/common/env.ts
|
|
3
|
-
function normalizeEnv(source) {
|
|
4
|
-
if (!source) return {};
|
|
5
|
-
const normalized = {};
|
|
6
|
-
for (const [key, value] of Object.entries(source)) if (typeof value === "string") normalized[key] = value;
|
|
7
|
-
else if (typeof value === "boolean" || typeof value === "number") normalized[key] = String(value);
|
|
8
|
-
return normalized;
|
|
9
|
-
}
|
|
10
|
-
function getBuildTimeEnv() {
|
|
11
|
-
const metaEnv = import.meta.env;
|
|
12
|
-
const normalized = normalizeEnv(metaEnv);
|
|
13
|
-
if (normalized.NODE_ENV == null && metaEnv) {
|
|
14
|
-
if (typeof metaEnv.MODE === "string" && metaEnv.MODE.length > 0) normalized.NODE_ENV = metaEnv.MODE;
|
|
15
|
-
else if (metaEnv.PROD === true || metaEnv.PROD === "true") normalized.NODE_ENV = "production";
|
|
16
|
-
else if (metaEnv.DEV === true || metaEnv.DEV === "true") normalized.NODE_ENV = "development";
|
|
17
|
-
}
|
|
18
|
-
return normalized;
|
|
19
|
-
}
|
|
20
|
-
function resolveNodeEnv(processNodeEnv, buildNodeEnv) {
|
|
21
|
-
if (buildNodeEnv === "production") return "production";
|
|
22
|
-
if (processNodeEnv != null) return processNodeEnv;
|
|
23
|
-
if (buildNodeEnv === "development") return "development";
|
|
24
|
-
return buildNodeEnv === "test" ? void 0 : buildNodeEnv;
|
|
25
|
-
}
|
|
26
|
-
function getRuntimeEnv() {
|
|
27
|
-
const processEnv = globalThis.process?.env ?? {};
|
|
28
|
-
const buildTimeEnv = getBuildTimeEnv();
|
|
29
|
-
return {
|
|
30
|
-
...processEnv,
|
|
31
|
-
...buildTimeEnv,
|
|
32
|
-
NODE_ENV: resolveNodeEnv(processEnv.NODE_ENV, buildTimeEnv.NODE_ENV)
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
function isProductionEnvironment() {
|
|
36
|
-
return getRuntimeEnv().NODE_ENV === "production";
|
|
37
|
-
}
|
|
38
|
-
function isDevelopmentEnvironment() {
|
|
39
|
-
return !isProductionEnvironment();
|
|
40
|
-
}
|
|
41
|
-
//#endregion
|
|
42
|
-
//#region src/common/invariant.ts
|
|
43
|
-
/**
|
|
44
|
-
* Invariant assertions shared by production runtime code.
|
|
45
|
-
*/
|
|
46
|
-
function invariant(condition, message, context) {
|
|
47
|
-
if (!condition) {
|
|
48
|
-
const contextStr = context ? "\n" + JSON.stringify(context, null, 2) : "";
|
|
49
|
-
throw new Error(`[Askr Invariant] ${message}${contextStr}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
function assertSchedulingPrecondition(condition, violationMessage) {
|
|
53
|
-
invariant(condition, `[Scheduler Precondition] ${violationMessage}`);
|
|
54
|
-
}
|
|
55
|
-
//#endregion
|
|
56
|
-
//#region src/runtime/diagnostics/perf-metrics.ts
|
|
57
|
-
const incrementPerfMetric = () => {};
|
|
58
|
-
const addPerfDuration = () => {};
|
|
59
|
-
//#endregion
|
|
60
|
-
//#region src/runtime/scheduler-scopes.ts
|
|
61
|
-
var SchedulerScopes = class {
|
|
62
|
-
constructor() {
|
|
63
|
-
this.handlerScopes = 0;
|
|
64
|
-
this.handlerFlag = false;
|
|
65
|
-
this.progressScopes = 0;
|
|
66
|
-
}
|
|
67
|
-
get inHandler() {
|
|
68
|
-
return this.handlerFlag || this.handlerScopes > 0;
|
|
69
|
-
}
|
|
70
|
-
get allowSyncProgress() {
|
|
71
|
-
return this.progressScopes > 0;
|
|
72
|
-
}
|
|
73
|
-
canKick(running) {
|
|
74
|
-
return !running && !this.inHandler && !this.allowSyncProgress;
|
|
75
|
-
}
|
|
76
|
-
adjustHandler(delta) {
|
|
77
|
-
this.handlerScopes += delta;
|
|
78
|
-
}
|
|
79
|
-
setHandlerFlag(value) {
|
|
80
|
-
this.handlerFlag = value;
|
|
81
|
-
}
|
|
82
|
-
adjustProgress(delta) {
|
|
83
|
-
this.progressScopes += delta;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
//#endregion
|
|
87
|
-
//#region src/runtime/scheduled-work.ts
|
|
88
|
-
const workByTask = /* @__PURE__ */ new WeakMap();
|
|
89
|
-
var ScheduledWork = class {
|
|
90
|
-
constructor(run) {
|
|
91
|
-
this.pending = false;
|
|
92
|
-
this.task = () => {
|
|
93
|
-
this.pending = false;
|
|
94
|
-
run();
|
|
95
|
-
};
|
|
96
|
-
workByTask.set(this.task, this);
|
|
97
|
-
}
|
|
98
|
-
request(scheduler, lane) {
|
|
99
|
-
if (this.pending) return;
|
|
100
|
-
this.pending = true;
|
|
101
|
-
try {
|
|
102
|
-
scheduler.enqueueInLane(lane, this.task);
|
|
103
|
-
} catch (error) {
|
|
104
|
-
this.cancel();
|
|
105
|
-
throw error;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
cancel() {
|
|
109
|
-
this.pending = false;
|
|
110
|
-
}
|
|
111
|
-
static release(task) {
|
|
112
|
-
workByTask.get(task)?.cancel();
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
//#endregion
|
|
116
|
-
//#region src/runtime/scheduler.ts
|
|
117
|
-
/**
|
|
118
|
-
* Serialized update scheduler — safer design (no inline execution, explicit flush)
|
|
119
|
-
*
|
|
120
|
-
* Key ideas:
|
|
121
|
-
* - Never execute a task inline from `enqueue`.
|
|
122
|
-
* - `flush()` is explicit and non-reentrant.
|
|
123
|
-
* - `runWithSyncProgress()` allows enqueues temporarily but does not run tasks
|
|
124
|
-
* inline; it runs `fn` and then does an explicit `flush()`.
|
|
125
|
-
* - `waitForFlush()` is race-free with a monotonic `flushVersion`.
|
|
126
|
-
*/
|
|
127
|
-
const MAX_FLUSH_DEPTH = 50;
|
|
128
|
-
const SCHEDULER_LANES = [
|
|
129
|
-
"derived",
|
|
130
|
-
"component",
|
|
131
|
-
"reactive",
|
|
132
|
-
"post"
|
|
133
|
-
];
|
|
134
|
-
var Scheduler = class {
|
|
135
|
-
constructor() {
|
|
136
|
-
this.bulkCommitProbe = () => false;
|
|
137
|
-
this.lanes = {
|
|
138
|
-
derived: {
|
|
139
|
-
tasks: [],
|
|
140
|
-
head: 0
|
|
141
|
-
},
|
|
142
|
-
component: {
|
|
143
|
-
tasks: [],
|
|
144
|
-
head: 0
|
|
145
|
-
},
|
|
146
|
-
reactive: {
|
|
147
|
-
tasks: [],
|
|
148
|
-
head: 0
|
|
149
|
-
},
|
|
150
|
-
post: {
|
|
151
|
-
tasks: [],
|
|
152
|
-
head: 0
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
this.scopes = new SchedulerScopes();
|
|
156
|
-
this.running = false;
|
|
157
|
-
this.depth = 0;
|
|
158
|
-
this.executionDepth = 0;
|
|
159
|
-
this.flushVersion = 0;
|
|
160
|
-
this.kickScheduled = false;
|
|
161
|
-
this.waiters = [];
|
|
162
|
-
this.taskCount = 0;
|
|
163
|
-
}
|
|
164
|
-
setBulkCommitProbe(probe) {
|
|
165
|
-
this.bulkCommitProbe = probe;
|
|
166
|
-
}
|
|
167
|
-
isBulkCommitActive() {
|
|
168
|
-
try {
|
|
169
|
-
return this.bulkCommitProbe();
|
|
170
|
-
} catch (e) {
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
hasPendingTasks() {
|
|
175
|
-
for (const lane of SCHEDULER_LANES) {
|
|
176
|
-
const queue = this.lanes[lane];
|
|
177
|
-
if (queue.head < queue.tasks.length) return true;
|
|
178
|
-
}
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
getPendingTaskCount() {
|
|
182
|
-
let total = 0;
|
|
183
|
-
for (const lane of SCHEDULER_LANES) {
|
|
184
|
-
const queue = this.lanes[lane];
|
|
185
|
-
total += queue.tasks.length - queue.head;
|
|
186
|
-
}
|
|
187
|
-
return total;
|
|
188
|
-
}
|
|
189
|
-
compactLane(queue) {
|
|
190
|
-
if (queue.head >= queue.tasks.length) {
|
|
191
|
-
queue.tasks.length = 0;
|
|
192
|
-
queue.head = 0;
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
if (queue.head <= 0) return;
|
|
196
|
-
const remaining = queue.tasks.length - queue.head;
|
|
197
|
-
for (let i = 0; i < remaining; i++) queue.tasks[i] = queue.tasks[queue.head + i];
|
|
198
|
-
queue.tasks.length = remaining;
|
|
199
|
-
queue.head = 0;
|
|
200
|
-
}
|
|
201
|
-
scheduleFlushKick() {
|
|
202
|
-
if (this.kickScheduled || !this.scopes.canKick(this.running) || this.isBulkCommitActive() || !this.hasPendingTasks()) return;
|
|
203
|
-
this.kickScheduled = true;
|
|
204
|
-
queueMicrotask(() => {
|
|
205
|
-
this.kickScheduled = false;
|
|
206
|
-
if (!this.scopes.canKick(this.running) || this.isBulkCommitActive() || !this.hasPendingTasks()) return;
|
|
207
|
-
try {
|
|
208
|
-
this.flush();
|
|
209
|
-
} catch (err) {
|
|
210
|
-
setTimeout(() => {
|
|
211
|
-
throw err;
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
enqueue(task) {
|
|
217
|
-
this.enqueueInLane("component", task);
|
|
218
|
-
}
|
|
219
|
-
enqueueInLane(lane, task) {
|
|
220
|
-
assertSchedulingPrecondition(typeof task === "function", "enqueue() requires a function");
|
|
221
|
-
if (this.isBulkCommitActive() && !this.scopes.allowSyncProgress) {
|
|
222
|
-
ScheduledWork.release(task);
|
|
223
|
-
if (isDevelopmentEnvironment()) throw new Error("[Scheduler] enqueue() during bulk commit (not allowed)");
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
this.lanes[lane].tasks.push(task);
|
|
227
|
-
this.taskCount++;
|
|
228
|
-
this.scheduleFlushKick();
|
|
229
|
-
}
|
|
230
|
-
flush() {
|
|
231
|
-
invariant(!this.running, "[Scheduler] flush() called while already running");
|
|
232
|
-
if (isDevelopmentEnvironment()) {
|
|
233
|
-
if (this.isBulkCommitActive() && !this.scopes.allowSyncProgress) throw new Error("[Scheduler] flush() started during bulk commit (not allowed)");
|
|
234
|
-
}
|
|
235
|
-
this.running = true;
|
|
236
|
-
this.depth = 0;
|
|
237
|
-
const failures = [];
|
|
238
|
-
let executedTaskCount = 0;
|
|
239
|
-
const executionsByTask = isDevelopmentEnvironment() ? /* @__PURE__ */ new Map() : null;
|
|
240
|
-
try {
|
|
241
|
-
while (true) {
|
|
242
|
-
let didRunTask = false;
|
|
243
|
-
for (const lane of SCHEDULER_LANES) {
|
|
244
|
-
const laneQueue = this.lanes[lane];
|
|
245
|
-
let executedInLane = 0;
|
|
246
|
-
while (laneQueue.head < laneQueue.tasks.length) {
|
|
247
|
-
const task = laneQueue.tasks[laneQueue.head++];
|
|
248
|
-
if (executionsByTask) {
|
|
249
|
-
const taskExecutions = (executionsByTask.get(task) ?? 0) + 1;
|
|
250
|
-
executionsByTask.set(task, taskExecutions);
|
|
251
|
-
this.depth = Math.max(this.depth, taskExecutions);
|
|
252
|
-
if (taskExecutions > MAX_FLUSH_DEPTH) throw new Error(`[Scheduler] exceeded MAX_FLUSH_DEPTH (${MAX_FLUSH_DEPTH}). Likely infinite update loop.`);
|
|
253
|
-
}
|
|
254
|
-
try {
|
|
255
|
-
this.executionDepth++;
|
|
256
|
-
task();
|
|
257
|
-
} catch (err) {
|
|
258
|
-
failures.push(err);
|
|
259
|
-
} finally {
|
|
260
|
-
if (this.executionDepth > 0) this.executionDepth--;
|
|
261
|
-
executedTaskCount++;
|
|
262
|
-
executedInLane++;
|
|
263
|
-
didRunTask = true;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
if (executedInLane > 0) this.taskCount = Math.max(0, this.taskCount - executedInLane);
|
|
267
|
-
}
|
|
268
|
-
if (!didRunTask) break;
|
|
269
|
-
}
|
|
270
|
-
} finally {
|
|
271
|
-
this.running = false;
|
|
272
|
-
this.depth = 0;
|
|
273
|
-
this.executionDepth = 0;
|
|
274
|
-
for (const lane of SCHEDULER_LANES) {
|
|
275
|
-
const queue = this.lanes[lane];
|
|
276
|
-
if (queue.head > 0) this.compactLane(queue);
|
|
277
|
-
}
|
|
278
|
-
this.completeEpoch();
|
|
279
|
-
}
|
|
280
|
-
if (failures.length === 1) throw failures[0];
|
|
281
|
-
if (failures.length > 1) throw new AggregateError(failures, "Scheduler task failures");
|
|
282
|
-
}
|
|
283
|
-
runWithSyncProgress(fn) {
|
|
284
|
-
this.scopes.adjustProgress(1);
|
|
285
|
-
const startVersion = this.flushVersion;
|
|
286
|
-
try {
|
|
287
|
-
const res = fn();
|
|
288
|
-
if (!this.running && this.hasPendingTasks()) this.flush();
|
|
289
|
-
if (isDevelopmentEnvironment()) {
|
|
290
|
-
if (!this.running && this.hasPendingTasks()) throw new Error("[Scheduler] tasks remain after runWithSyncProgress flush");
|
|
291
|
-
}
|
|
292
|
-
return res;
|
|
293
|
-
} finally {
|
|
294
|
-
this.scopes.adjustProgress(-1);
|
|
295
|
-
if (this.flushVersion === startVersion) this.completeIdleEpoch();
|
|
296
|
-
this.scheduleFlushKick();
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
waitForFlush(targetVersion, timeoutMs = 2e3) {
|
|
300
|
-
const target = typeof targetVersion === "number" ? targetVersion : this.flushVersion + 1;
|
|
301
|
-
if (this.flushVersion >= target) return Promise.resolve();
|
|
302
|
-
return new Promise((resolve, reject) => {
|
|
303
|
-
const waiter = {
|
|
304
|
-
target,
|
|
305
|
-
resolve,
|
|
306
|
-
reject
|
|
307
|
-
};
|
|
308
|
-
waiter.timer = setTimeout(() => {
|
|
309
|
-
const index = this.waiters.indexOf(waiter);
|
|
310
|
-
if (index >= 0) this.waiters.splice(index, 1);
|
|
311
|
-
if (waiter.timer !== void 0) clearTimeout(waiter.timer);
|
|
312
|
-
const diag = {
|
|
313
|
-
flushVersion: this.flushVersion,
|
|
314
|
-
queueLen: this.getPendingTaskCount(),
|
|
315
|
-
running: this.running,
|
|
316
|
-
inHandler: this.scopes.inHandler,
|
|
317
|
-
bulk: this.isBulkCommitActive()
|
|
318
|
-
};
|
|
319
|
-
reject(/* @__PURE__ */ new Error(`waitForFlush timeout ${timeoutMs}ms: ${JSON.stringify(diag)}`));
|
|
320
|
-
}, timeoutMs);
|
|
321
|
-
this.waiters.push(waiter);
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
getState() {
|
|
325
|
-
return {
|
|
326
|
-
queueLength: this.getPendingTaskCount(),
|
|
327
|
-
running: this.running,
|
|
328
|
-
depth: this.depth,
|
|
329
|
-
executionDepth: this.executionDepth,
|
|
330
|
-
taskCount: this.taskCount,
|
|
331
|
-
flushVersion: this.flushVersion,
|
|
332
|
-
laneQueues: {
|
|
333
|
-
derived: this.lanes.derived.tasks.length - this.lanes.derived.head,
|
|
334
|
-
component: this.lanes.component.tasks.length - this.lanes.component.head,
|
|
335
|
-
reactive: this.lanes.reactive.tasks.length - this.lanes.reactive.head,
|
|
336
|
-
post: this.lanes.post.tasks.length - this.lanes.post.head
|
|
337
|
-
},
|
|
338
|
-
inHandler: this.scopes.inHandler,
|
|
339
|
-
allowSyncProgress: this.scopes.allowSyncProgress
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
getFlushVersion() {
|
|
343
|
-
return this.flushVersion;
|
|
344
|
-
}
|
|
345
|
-
flushIfQueued() {
|
|
346
|
-
if (!this.running && this.hasPendingTasks()) this.flush();
|
|
347
|
-
}
|
|
348
|
-
runInHandlerScope(fn, flushMode = "defer") {
|
|
349
|
-
this.scopes.adjustHandler(1);
|
|
350
|
-
try {
|
|
351
|
-
return fn();
|
|
352
|
-
} finally {
|
|
353
|
-
this.scopes.adjustHandler(-1);
|
|
354
|
-
if (!this.scopes.inHandler) {
|
|
355
|
-
if (flushMode === "sync") this.flushIfQueued();
|
|
356
|
-
else this.scheduleFlushKick();
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
setInHandler(v) {
|
|
361
|
-
this.scopes.setHandlerFlag(v);
|
|
362
|
-
if (!v) this.scheduleFlushKick();
|
|
363
|
-
}
|
|
364
|
-
isInHandler() {
|
|
365
|
-
return this.scopes.inHandler;
|
|
366
|
-
}
|
|
367
|
-
isExecuting() {
|
|
368
|
-
return this.running || this.executionDepth > 0;
|
|
369
|
-
}
|
|
370
|
-
clearPendingSyncTasks() {
|
|
371
|
-
const remaining = this.getPendingTaskCount();
|
|
372
|
-
if (remaining <= 0) return 0;
|
|
373
|
-
for (const lane of SCHEDULER_LANES) {
|
|
374
|
-
const queue = this.lanes[lane];
|
|
375
|
-
for (let index = queue.head; index < queue.tasks.length; index++) ScheduledWork.release(queue.tasks[index]);
|
|
376
|
-
queue.tasks.length = this.running ? queue.head : 0;
|
|
377
|
-
if (!this.running) queue.head = 0;
|
|
378
|
-
}
|
|
379
|
-
this.taskCount = Math.max(0, this.taskCount - remaining);
|
|
380
|
-
this.completeIdleEpoch();
|
|
381
|
-
return remaining;
|
|
382
|
-
}
|
|
383
|
-
completeIdleEpoch() {
|
|
384
|
-
if (!this.running && !this.scopes.allowSyncProgress && !this.hasPendingTasks()) this.completeEpoch();
|
|
385
|
-
}
|
|
386
|
-
completeEpoch() {
|
|
387
|
-
this.flushVersion++;
|
|
388
|
-
this.resolveWaiters();
|
|
389
|
-
}
|
|
390
|
-
resolveWaiters() {
|
|
391
|
-
if (this.waiters.length === 0) return;
|
|
392
|
-
const ready = [];
|
|
393
|
-
const remaining = [];
|
|
394
|
-
for (const w of this.waiters) if (this.flushVersion >= w.target) {
|
|
395
|
-
if (w.timer !== void 0) clearTimeout(w.timer);
|
|
396
|
-
ready.push(w.resolve);
|
|
397
|
-
} else remaining.push(w);
|
|
398
|
-
this.waiters = remaining;
|
|
399
|
-
for (const r of ready) r();
|
|
400
|
-
}
|
|
401
|
-
};
|
|
402
|
-
const globalScheduler = new Scheduler();
|
|
403
|
-
//#endregion
|
|
404
|
-
//#region src/runtime/transactions/coordinator.ts
|
|
405
|
-
const transactionCreation = Symbol("transaction creation");
|
|
406
|
-
let transactionState;
|
|
407
|
-
/** Read-only transaction identity with explicit resource and notification operations. */
|
|
408
|
-
var CommitTransaction = class {
|
|
409
|
-
#state;
|
|
410
|
-
static {
|
|
411
|
-
transactionState = (transaction) => transaction.#state;
|
|
412
|
-
}
|
|
413
|
-
constructor(coordinator, parent, token) {
|
|
414
|
-
if (token !== transactionCreation) throw new Error("[Askr] Transactions must be created by their coordinator.");
|
|
415
|
-
this.#state = {
|
|
416
|
-
phase: "preparing",
|
|
417
|
-
parent,
|
|
418
|
-
coordinator,
|
|
419
|
-
participants: [],
|
|
420
|
-
seen: void 0,
|
|
421
|
-
index: void 0,
|
|
422
|
-
resources: void 0,
|
|
423
|
-
errors: void 0,
|
|
424
|
-
completions: void 0,
|
|
425
|
-
deferNotifications: false
|
|
426
|
-
};
|
|
427
|
-
Object.freeze(this);
|
|
428
|
-
}
|
|
429
|
-
get phase() {
|
|
430
|
-
return this.#state.phase;
|
|
431
|
-
}
|
|
432
|
-
get parent() {
|
|
433
|
-
return this.#state.parent;
|
|
434
|
-
}
|
|
435
|
-
get coordinator() {
|
|
436
|
-
return this.#state.coordinator;
|
|
437
|
-
}
|
|
438
|
-
get participants() {
|
|
439
|
-
return Object.freeze(this.#state.participants.slice());
|
|
440
|
-
}
|
|
441
|
-
get errors() {
|
|
442
|
-
return Object.freeze(this.#state.errors?.slice() ?? []);
|
|
443
|
-
}
|
|
444
|
-
get resourceCount() {
|
|
445
|
-
return this.#state.resources?.size ?? 0;
|
|
446
|
-
}
|
|
447
|
-
/** Detached diagnostic collections cannot mutate coordinator bookkeeping. */
|
|
448
|
-
inspect() {
|
|
449
|
-
return {
|
|
450
|
-
index: new Map(Array.from(this.#state.index ?? [], ([kind, entries]) => [kind, new Map(entries)])),
|
|
451
|
-
resources: new Map(this.#state.resources),
|
|
452
|
-
seen: new Set(this.#state.seen),
|
|
453
|
-
completions: new Map(this.#state.completions)
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
get active() {
|
|
457
|
-
const phase = this.#state.phase;
|
|
458
|
-
return phase === "preparing" || phase === "applying" || phase === "publishing";
|
|
459
|
-
}
|
|
460
|
-
hasResource(key) {
|
|
461
|
-
return this.#state.resources?.has(key) ?? false;
|
|
462
|
-
}
|
|
463
|
-
resource(key) {
|
|
464
|
-
return this.#state.resources?.get(key);
|
|
465
|
-
}
|
|
466
|
-
captureResource(key, resource) {
|
|
467
|
-
if (!this.active) throw new Error("[Askr] Cannot capture a resource on a settled transaction.");
|
|
468
|
-
const resources = this.#state.resources ??= /* @__PURE__ */ new Map();
|
|
469
|
-
if (resources.has(key)) return resources.get(key);
|
|
470
|
-
resources.set(key, resource);
|
|
471
|
-
return resource;
|
|
472
|
-
}
|
|
473
|
-
setDeferredNotifications(enabled) {
|
|
474
|
-
if (!this.active) throw new Error("[Askr] Cannot configure a settled transaction.");
|
|
475
|
-
this.#state.deferNotifications = enabled;
|
|
476
|
-
}
|
|
477
|
-
participant(key, kind) {
|
|
478
|
-
return this.#state.index?.get(kind)?.get(key);
|
|
479
|
-
}
|
|
480
|
-
};
|
|
481
|
-
/** Reuse the larger map while preserving earlier values, including undefined. */
|
|
482
|
-
function mergeEarlierEntries(earlier, later, reuseLater = true) {
|
|
483
|
-
if (reuseLater && (!earlier || earlier.size < later.size)) {
|
|
484
|
-
for (const [key, value] of earlier ?? []) later.set(key, value);
|
|
485
|
-
return later;
|
|
486
|
-
}
|
|
487
|
-
earlier ??= /* @__PURE__ */ new Map();
|
|
488
|
-
for (const [key, value] of later) if (!earlier.has(key)) earlier.set(key, value);
|
|
489
|
-
return earlier;
|
|
490
|
-
}
|
|
491
|
-
/** One synchronous stack per runtime. This coordinator has no component,
|
|
492
|
-
* subscription, renderer, scheduler, or browser dependencies. */
|
|
493
|
-
var CommitCoordinator = class {
|
|
494
|
-
constructor(options = {}) {
|
|
495
|
-
this.frame = null;
|
|
496
|
-
this.options = options;
|
|
497
|
-
}
|
|
498
|
-
get current() {
|
|
499
|
-
return this.frame;
|
|
500
|
-
}
|
|
501
|
-
begin() {
|
|
502
|
-
const transaction = new CommitTransaction(this, this.frame?.active ? this.frame : null, transactionCreation);
|
|
503
|
-
this.frame = transaction;
|
|
504
|
-
return transaction;
|
|
505
|
-
}
|
|
506
|
-
register(participant) {
|
|
507
|
-
if (!this.frame?.active) return false;
|
|
508
|
-
this.add(this.frame, participant);
|
|
509
|
-
return true;
|
|
510
|
-
}
|
|
511
|
-
add(transaction, participant) {
|
|
512
|
-
const state = transactionState(transaction);
|
|
513
|
-
if (state.seen?.has(participant)) return;
|
|
514
|
-
if (participant.key) {
|
|
515
|
-
const kinds = state.index ??= /* @__PURE__ */ new Map();
|
|
516
|
-
let index = kinds.get(participant.kind);
|
|
517
|
-
if (!index) kinds.set(participant.kind, index = /* @__PURE__ */ new Map());
|
|
518
|
-
const previous = index.get(participant.key);
|
|
519
|
-
if (previous) {
|
|
520
|
-
this.validateCollision(previous, participant);
|
|
521
|
-
if (previous === participant) return;
|
|
522
|
-
if (participant.collision === "keep-first") {
|
|
523
|
-
(state.seen ??= /* @__PURE__ */ new Set()).add(participant);
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
try {
|
|
527
|
-
participant.merge(previous);
|
|
528
|
-
(state.seen ??= /* @__PURE__ */ new Set()).add(participant);
|
|
529
|
-
} catch (error) {
|
|
530
|
-
state.participants.push(participant);
|
|
531
|
-
this.discardMergedTransactions(transaction);
|
|
532
|
-
throw error;
|
|
533
|
-
}
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
index.set(participant.key, participant);
|
|
537
|
-
} else (state.seen ??= /* @__PURE__ */ new Set()).add(participant);
|
|
538
|
-
state.participants.push(participant);
|
|
539
|
-
}
|
|
540
|
-
validateCollision(previous, participant) {
|
|
541
|
-
if (previous !== participant && participant.collision !== "keep-first" && !participant.merge) throw new Error("[Askr] Participant collision requires merge or keep-first.");
|
|
542
|
-
}
|
|
543
|
-
discardMergedTransactions(transaction, mergedParent) {
|
|
544
|
-
const state = transactionState(transaction);
|
|
545
|
-
const affected = /* @__PURE__ */ new Set([transaction]);
|
|
546
|
-
const participants = new Set(state.participants);
|
|
547
|
-
const ancestors = [];
|
|
548
|
-
for (let ancestor = state.parent; ancestor; ancestor = transactionState(ancestor).parent) if (ancestor.active) ancestors.push(ancestor);
|
|
549
|
-
let changed;
|
|
550
|
-
do {
|
|
551
|
-
changed = false;
|
|
552
|
-
for (const ancestor of ancestors) if (!affected.has(ancestor) && (ancestor === mergedParent || transactionState(ancestor).participants.some((participant) => participants.has(participant)))) {
|
|
553
|
-
affected.add(ancestor);
|
|
554
|
-
changed = true;
|
|
555
|
-
for (const participant of transactionState(ancestor).participants) participants.add(participant);
|
|
556
|
-
}
|
|
557
|
-
} while (changed);
|
|
558
|
-
const frames = [transaction, ...ancestors.filter((ancestor) => affected.has(ancestor))];
|
|
559
|
-
const owners = /* @__PURE__ */ new Set();
|
|
560
|
-
for (let index = frames.length - 1; index >= 0; index--) {
|
|
561
|
-
const frame = frames[index];
|
|
562
|
-
const frameState = transactionState(frame);
|
|
563
|
-
frameState.phase = "discarding";
|
|
564
|
-
for (let member = frameState.participants.length - 1; member >= 0; member--) {
|
|
565
|
-
const participant = frameState.participants[member];
|
|
566
|
-
if (owners.has(participant)) frameState.participants.splice(member, 1);
|
|
567
|
-
else owners.add(participant);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
for (const frame of frames) this.drainRollback(frame);
|
|
571
|
-
}
|
|
572
|
-
suspend(transaction) {
|
|
573
|
-
const state = transactionState(transaction);
|
|
574
|
-
this.assertOwned(transaction);
|
|
575
|
-
if (this.frame === transaction) this.frame = this.liveFrame(state.parent);
|
|
576
|
-
}
|
|
577
|
-
apply(transaction, operation) {
|
|
578
|
-
const state = transactionState(transaction);
|
|
579
|
-
this.assertOwned(transaction);
|
|
580
|
-
if (!transaction.active) throw new Error("[Askr] Cannot apply a settled transaction.");
|
|
581
|
-
const previous = this.frame;
|
|
582
|
-
const previousPhase = state.phase;
|
|
583
|
-
if (previous && previous !== transaction && previous.active) {
|
|
584
|
-
let ancestor = previous;
|
|
585
|
-
while (ancestor && ancestor !== transaction) ancestor = transactionState(ancestor).parent;
|
|
586
|
-
if (!ancestor) state.parent = previous;
|
|
587
|
-
}
|
|
588
|
-
this.frame = transaction;
|
|
589
|
-
state.phase = "applying";
|
|
590
|
-
try {
|
|
591
|
-
return operation();
|
|
592
|
-
} finally {
|
|
593
|
-
if (transaction.active) state.phase = previousPhase;
|
|
594
|
-
this.frame = this.liveFrame(previous);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
/** State writes survive rollback. Their notifications wait until all
|
|
598
|
-
* framework restoration or post-commit work has finished. */
|
|
599
|
-
deferCompletion(key, complete) {
|
|
600
|
-
const transaction = this.frame;
|
|
601
|
-
if (!transaction || !this.notificationsDeferred()) return false;
|
|
602
|
-
const state = transactionState(transaction);
|
|
603
|
-
(state.completions ??= /* @__PURE__ */ new Map()).set(key, complete);
|
|
604
|
-
return true;
|
|
605
|
-
}
|
|
606
|
-
notificationsDeferred() {
|
|
607
|
-
let transaction = this.frame;
|
|
608
|
-
while (transaction) {
|
|
609
|
-
const state = transactionState(transaction);
|
|
610
|
-
if (state.deferNotifications && (transaction.active || state.phase === "discarding")) return true;
|
|
611
|
-
transaction = state.parent;
|
|
612
|
-
}
|
|
613
|
-
return false;
|
|
614
|
-
}
|
|
615
|
-
commit(transaction) {
|
|
616
|
-
const state = transactionState(transaction);
|
|
617
|
-
this.assertOwned(transaction);
|
|
618
|
-
if (!transaction.active || state.committing) return;
|
|
619
|
-
state.committing = true;
|
|
620
|
-
try {
|
|
621
|
-
const parent = state.parent;
|
|
622
|
-
if (parent?.active) {
|
|
623
|
-
this.joinParentTransaction(transaction, state, parent);
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
this.applyAndSettle(transaction, state);
|
|
627
|
-
} finally {
|
|
628
|
-
state.committing = false;
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
/**
|
|
632
|
-
* Fold a nested transaction into its still-open parent.
|
|
633
|
-
*
|
|
634
|
-
* Nothing is applied here: the child's participants, index, resources and
|
|
635
|
-
* identity set move to the parent, which will run them when it commits. The
|
|
636
|
-
* work is in reconciling identity — a participant may already exist in the
|
|
637
|
-
* parent under the same key, in which case the two have to be merged, and a
|
|
638
|
-
* merge can register further participants of its own, so it runs with its own
|
|
639
|
-
* failure ownership.
|
|
640
|
-
*/
|
|
641
|
-
joinParentTransaction(transaction, state, parent) {
|
|
642
|
-
const parentState = transactionState(parent);
|
|
643
|
-
let needsMerge = false;
|
|
644
|
-
let changedIdentity = false;
|
|
645
|
-
for (const participant of state.participants) {
|
|
646
|
-
if (participant.key && state.seen?.has(participant)) changedIdentity = true;
|
|
647
|
-
if (parentState.seen?.has(participant)) continue;
|
|
648
|
-
const previous = participant.key ? parent.participant(participant.key, participant.kind) : void 0;
|
|
649
|
-
if (previous) {
|
|
650
|
-
this.validateCollision(previous, participant);
|
|
651
|
-
if (previous !== participant && participant.collision !== "keep-first") needsMerge = true;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
for (const [kind, index] of state.index?.entries() ?? []) {
|
|
655
|
-
index.forEach((participant, key) => {
|
|
656
|
-
if (participant.key !== key || participant.kind !== kind) changedIdentity = true;
|
|
657
|
-
});
|
|
658
|
-
if (changedIdentity) break;
|
|
659
|
-
}
|
|
660
|
-
if (needsMerge || changedIdentity) {
|
|
661
|
-
try {
|
|
662
|
-
for (const participant of state.participants) {
|
|
663
|
-
if (parentState.seen?.has(participant)) continue;
|
|
664
|
-
const previous = participant.key ? parent.participant(participant.key, participant.kind) : void 0;
|
|
665
|
-
if (previous && previous !== participant) {
|
|
666
|
-
if (participant.collision !== "keep-first") participant.merge(previous);
|
|
667
|
-
if (!parent.active) this.discard(transaction);
|
|
668
|
-
if (!transaction.active) return;
|
|
669
|
-
(parentState.seen ??= /* @__PURE__ */ new Set()).add(participant);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
} catch (error) {
|
|
673
|
-
this.discardMergedTransactions(transaction, parent);
|
|
674
|
-
throw error;
|
|
675
|
-
}
|
|
676
|
-
for (const participant of state.participants) if (!participant.key || !parent.participant(participant.key, participant.kind)) this.add(parent, participant);
|
|
677
|
-
} else {
|
|
678
|
-
for (const participant of state.participants) {
|
|
679
|
-
if (parentState.seen?.has(participant)) {
|
|
680
|
-
if (participant.key) state.index.get(participant.kind).delete(participant.key);
|
|
681
|
-
continue;
|
|
682
|
-
}
|
|
683
|
-
if (participant.key) {
|
|
684
|
-
const previous = parent.participant(participant.key, participant.kind);
|
|
685
|
-
if (previous) {
|
|
686
|
-
if (previous !== participant) (state.seen ??= /* @__PURE__ */ new Set()).add(participant);
|
|
687
|
-
continue;
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
parentState.participants.push(participant);
|
|
691
|
-
}
|
|
692
|
-
for (const [kind, index] of state.index?.entries() ?? []) {
|
|
693
|
-
if (!index.size) continue;
|
|
694
|
-
const kinds = parentState.index ??= /* @__PURE__ */ new Map();
|
|
695
|
-
kinds.set(kind, mergeEarlierEntries(kinds.get(kind), index));
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
if (state.resources) parentState.resources = mergeEarlierEntries(parentState.resources, state.resources, !needsMerge && !changedIdentity);
|
|
699
|
-
if (state.seen) {
|
|
700
|
-
if (!parentState.seen) parentState.seen = state.seen;
|
|
701
|
-
else if (parentState.seen.size < state.seen.size) {
|
|
702
|
-
for (const participant of parentState.seen) state.seen.add(participant);
|
|
703
|
-
parentState.seen = state.seen;
|
|
704
|
-
} else for (const participant of state.seen) parentState.seen.add(participant);
|
|
705
|
-
}
|
|
706
|
-
this.mergeCompletions(transaction, parent);
|
|
707
|
-
state.phase = "joined";
|
|
708
|
-
this.suspend(transaction);
|
|
709
|
-
this.release(transaction);
|
|
710
|
-
}
|
|
711
|
-
/**
|
|
712
|
-
* Run a root transaction to completion.
|
|
713
|
-
*
|
|
714
|
-
* Apply and publish interleave because an application hook can render during
|
|
715
|
-
* either, appending participants that must themselves be applied before
|
|
716
|
-
* anything else publishes. Settlement then runs each remaining phase over
|
|
717
|
-
* every participant, collecting failures rather than stopping, so one broken
|
|
718
|
-
* cleanup cannot strand the rest.
|
|
719
|
-
*
|
|
720
|
-
* A participant that deactivates the transaction returns early from here,
|
|
721
|
-
* which deliberately skips settlement — the `finally` blocks still restore
|
|
722
|
-
* the frame and release.
|
|
723
|
-
*/
|
|
724
|
-
applyAndSettle(transaction, state) {
|
|
725
|
-
const previous = this.frame;
|
|
726
|
-
this.frame = transaction;
|
|
727
|
-
let applied = 0;
|
|
728
|
-
let published = 0;
|
|
729
|
-
try {
|
|
730
|
-
while (applied < state.participants.length || published < state.participants.length) {
|
|
731
|
-
state.phase = "applying";
|
|
732
|
-
while (applied < state.participants.length) {
|
|
733
|
-
state.participants[applied++].apply?.();
|
|
734
|
-
if (!transaction.active) return;
|
|
735
|
-
}
|
|
736
|
-
state.phase = "publishing";
|
|
737
|
-
if (published < state.participants.length) state.participants[published++].publish?.();
|
|
738
|
-
if (!transaction.active) return;
|
|
739
|
-
}
|
|
740
|
-
} catch (error) {
|
|
741
|
-
this.discard(transaction);
|
|
742
|
-
throw error;
|
|
743
|
-
} finally {
|
|
744
|
-
this.frame = this.liveFrame(previous === transaction ? state.parent : previous);
|
|
745
|
-
}
|
|
746
|
-
state.phase = "settling";
|
|
747
|
-
try {
|
|
748
|
-
for (const phase of [
|
|
749
|
-
"settle",
|
|
750
|
-
"activate",
|
|
751
|
-
"complete"
|
|
752
|
-
]) for (const participant of state.participants) try {
|
|
753
|
-
participant[phase]?.();
|
|
754
|
-
} catch (error) {
|
|
755
|
-
(state.errors ??= []).push(error);
|
|
756
|
-
}
|
|
757
|
-
this.complete(transaction, (error) => (state.errors ??= []).push(error));
|
|
758
|
-
state.phase = "committed";
|
|
759
|
-
if (state.errors?.length) this.options.settlementErrors?.(state.errors, transaction);
|
|
760
|
-
} finally {
|
|
761
|
-
state.phase = "committed";
|
|
762
|
-
this.release(transaction);
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
discard(transaction) {
|
|
766
|
-
this.assertOwned(transaction);
|
|
767
|
-
if (!transaction.active) return;
|
|
768
|
-
this.drainRollback(transaction);
|
|
769
|
-
}
|
|
770
|
-
drainRollback(transaction) {
|
|
771
|
-
const state = transactionState(transaction);
|
|
772
|
-
const previous = this.frame;
|
|
773
|
-
this.frame = transaction;
|
|
774
|
-
state.phase = "discarding";
|
|
775
|
-
const report = (error) => {
|
|
776
|
-
try {
|
|
777
|
-
this.options.rollbackError?.(error);
|
|
778
|
-
} catch {}
|
|
779
|
-
};
|
|
780
|
-
try {
|
|
781
|
-
for (let index = state.participants.length - 1; index >= 0; index--) try {
|
|
782
|
-
state.participants[index].rollback?.();
|
|
783
|
-
} catch (error) {
|
|
784
|
-
report(error);
|
|
785
|
-
}
|
|
786
|
-
} finally {
|
|
787
|
-
state.phase = "discarded";
|
|
788
|
-
this.frame = this.liveFrame(previous === transaction ? state.parent : previous);
|
|
789
|
-
if (state.parent?.active || state.parent?.phase === "discarding") this.mergeCompletions(transaction, state.parent);
|
|
790
|
-
else this.complete(transaction, report);
|
|
791
|
-
this.release(transaction);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
mergeCompletions(transaction, parent) {
|
|
795
|
-
const state = transactionState(transaction);
|
|
796
|
-
if (!state.completions) return;
|
|
797
|
-
const parentState = transactionState(parent);
|
|
798
|
-
const completions = parentState.completions ??= /* @__PURE__ */ new Map();
|
|
799
|
-
for (const [key, complete] of state.completions) completions.set(key, complete);
|
|
800
|
-
}
|
|
801
|
-
complete(transaction, report) {
|
|
802
|
-
const state = transactionState(transaction);
|
|
803
|
-
for (const complete of state.completions?.values() ?? []) try {
|
|
804
|
-
complete();
|
|
805
|
-
} catch (error) {
|
|
806
|
-
report(error);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
release(transaction) {
|
|
810
|
-
const state = transactionState(transaction);
|
|
811
|
-
state.participants.length = 0;
|
|
812
|
-
state.seen = void 0;
|
|
813
|
-
state.index = void 0;
|
|
814
|
-
state.resources = void 0;
|
|
815
|
-
state.completions?.clear();
|
|
816
|
-
state.completions = void 0;
|
|
817
|
-
}
|
|
818
|
-
liveFrame(transaction) {
|
|
819
|
-
while (transaction && !transaction.active) transaction = transactionState(transaction).parent;
|
|
820
|
-
return transaction;
|
|
821
|
-
}
|
|
822
|
-
assertOwned(transaction) {
|
|
823
|
-
if (transactionState(transaction).coordinator !== this) throw new Error("[Askr] Transaction belongs to another runtime.");
|
|
824
|
-
}
|
|
825
|
-
};
|
|
826
|
-
//#endregion
|
|
827
|
-
//#region src/common/logger.ts
|
|
828
|
-
/**
|
|
829
|
-
* Shared logger interface for runtime and tooling code.
|
|
830
|
-
*
|
|
831
|
-
* Debug, info, and warn stay silent in production. Errors always log.
|
|
832
|
-
*/
|
|
833
|
-
function callConsole(method, args) {
|
|
834
|
-
const c = typeof console !== "undefined" ? console : void 0;
|
|
835
|
-
if (!c) return;
|
|
836
|
-
const fn = c[method];
|
|
837
|
-
if (typeof fn === "function") try {
|
|
838
|
-
fn.apply(console, args);
|
|
839
|
-
} catch {}
|
|
840
|
-
}
|
|
841
|
-
const logger = {
|
|
842
|
-
debug: (...args) => {
|
|
843
|
-
if (isProductionEnvironment()) return;
|
|
844
|
-
callConsole("debug", args);
|
|
845
|
-
},
|
|
846
|
-
info: (...args) => {
|
|
847
|
-
if (isProductionEnvironment()) return;
|
|
848
|
-
callConsole("info", args);
|
|
849
|
-
},
|
|
850
|
-
warn: (...args) => {
|
|
851
|
-
if (isProductionEnvironment()) return;
|
|
852
|
-
callConsole("warn", args);
|
|
853
|
-
},
|
|
854
|
-
error: (...args) => {
|
|
855
|
-
callConsole("error", args);
|
|
856
|
-
}
|
|
857
|
-
};
|
|
858
|
-
//#endregion
|
|
859
|
-
//#region src/runtime/runtime-state.ts
|
|
860
|
-
/**
|
|
861
|
-
* The renderer host for an environment that has no DOM renderer.
|
|
862
|
-
*
|
|
863
|
-
* This is not only a pre-boot placeholder: SSR and SSG never install a renderer,
|
|
864
|
-
* so it is the live host for the whole life of every server render. It
|
|
865
|
-
* therefore has to be usable, not merely loud, and it follows one rule:
|
|
866
|
-
*
|
|
867
|
-
* - Bookkeeping degrades. Capturing, releasing and inspecting hosts, scopes and
|
|
868
|
-
* key maps answer as though nothing is mounted, because during a server
|
|
869
|
-
* render nothing is.
|
|
870
|
-
* - Anything that would produce or destroy DOM throws. Reaching `evaluate`,
|
|
871
|
-
* `replaceComponentRange` or a teardown without a renderer means execution
|
|
872
|
-
* believed it had one, which is a bug worth surfacing rather than silently
|
|
873
|
-
* dropping the output.
|
|
874
|
-
*
|
|
875
|
-
* Browser composition replaces this through `installRuntimeRenderer`.
|
|
876
|
-
*/
|
|
877
|
-
function createMissingRendererHost() {
|
|
878
|
-
const noop = () => void 0;
|
|
879
|
-
const missing = (method) => {
|
|
880
|
-
throw new Error(`[Askr] renderer host is not configured; cannot call ${method}().`);
|
|
881
|
-
};
|
|
882
|
-
return {
|
|
883
|
-
captureComponentHost: noop,
|
|
884
|
-
releaseComponentHost: noop,
|
|
885
|
-
detachPortalHostOutput: noop,
|
|
886
|
-
isComponentHostDetached() {
|
|
887
|
-
return false;
|
|
888
|
-
},
|
|
889
|
-
clearChildScopeHost(scope) {
|
|
890
|
-
scope.dom = void 0;
|
|
891
|
-
scope.range = void 0;
|
|
892
|
-
},
|
|
893
|
-
captureChildScopeHost: noop,
|
|
894
|
-
resolveScopeBoundary() {
|
|
895
|
-
return {
|
|
896
|
-
dom: void 0,
|
|
897
|
-
range: void 0
|
|
898
|
-
};
|
|
899
|
-
},
|
|
900
|
-
prepareScopeRemoval() {
|
|
901
|
-
return {
|
|
902
|
-
dom: void 0,
|
|
903
|
-
range: void 0
|
|
904
|
-
};
|
|
905
|
-
},
|
|
906
|
-
recordRemovedScopeBoundary: noop,
|
|
907
|
-
teardownScopeHost() {
|
|
908
|
-
return 0;
|
|
909
|
-
},
|
|
910
|
-
hasUnmountedComponentHost() {
|
|
911
|
-
return false;
|
|
912
|
-
},
|
|
913
|
-
recordInlineComponentHost(instance, target) {
|
|
914
|
-
instance.target = target;
|
|
915
|
-
},
|
|
916
|
-
applyComponentResult(instance) {
|
|
917
|
-
if (instance.target || instance._placeholder) return missing("applyComponentResult");
|
|
918
|
-
return false;
|
|
919
|
-
},
|
|
920
|
-
classifyComponentUpdate() {
|
|
921
|
-
return {
|
|
922
|
-
useFastPath: false,
|
|
923
|
-
reason: "no-root"
|
|
924
|
-
};
|
|
925
|
-
},
|
|
926
|
-
evaluate() {
|
|
927
|
-
missing("evaluate");
|
|
928
|
-
},
|
|
929
|
-
cleanupInstancesUnder() {
|
|
930
|
-
missing("cleanupInstancesUnder");
|
|
931
|
-
},
|
|
932
|
-
replaceComponentRange() {
|
|
933
|
-
return missing("replaceComponentRange");
|
|
934
|
-
},
|
|
935
|
-
teardownNodeSubtree() {
|
|
936
|
-
missing("teardownNodeSubtree");
|
|
937
|
-
},
|
|
938
|
-
populateKeyMapForElement: noop,
|
|
939
|
-
getKeyMapForElement: noop,
|
|
940
|
-
isKeyedReorderFastPathEligible() {
|
|
941
|
-
return {
|
|
942
|
-
useFastPath: false,
|
|
943
|
-
totalKeyed: 0,
|
|
944
|
-
totalChildren: 0,
|
|
945
|
-
currentKeyCount: 0,
|
|
946
|
-
moveCount: 0,
|
|
947
|
-
lisLen: 0,
|
|
948
|
-
hasPropChanges: false,
|
|
949
|
-
isWholeKeyedList: false
|
|
950
|
-
};
|
|
951
|
-
},
|
|
952
|
-
markReactivePropsDirtySource: noop
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
function createRuntimeState(scheduler = globalScheduler) {
|
|
956
|
-
return {
|
|
957
|
-
scheduler,
|
|
958
|
-
renderer: createMissingRendererHost(),
|
|
959
|
-
commits: new CommitCoordinator({
|
|
960
|
-
rollbackError(error) {
|
|
961
|
-
logger.error("[Askr] transaction rollback failed:", error);
|
|
962
|
-
},
|
|
963
|
-
settlementErrors(errors) {
|
|
964
|
-
logger.error("[Askr] committed lifecycle work failed:", new AggregateError(errors, "Committed lifecycle work failed"));
|
|
965
|
-
}
|
|
966
|
-
})
|
|
967
|
-
};
|
|
968
|
-
}
|
|
969
|
-
const defaultRuntimeState = createRuntimeState();
|
|
970
|
-
//#endregion
|
|
971
|
-
//#region src/runtime/access.ts
|
|
972
|
-
function getRuntimeScheduler() {
|
|
973
|
-
return defaultRuntimeState.scheduler;
|
|
974
|
-
}
|
|
975
|
-
function getRuntimeEvaluation() {
|
|
976
|
-
return defaultRuntimeState.renderer;
|
|
977
|
-
}
|
|
978
|
-
function getRuntimeCleanup() {
|
|
979
|
-
return defaultRuntimeState.renderer;
|
|
980
|
-
}
|
|
981
|
-
function getRuntimeScopes() {
|
|
982
|
-
return defaultRuntimeState.renderer;
|
|
983
|
-
}
|
|
984
|
-
function getRuntimeKeys() {
|
|
985
|
-
return defaultRuntimeState.renderer;
|
|
986
|
-
}
|
|
987
|
-
/** Not exported: `markRuntimeReactivePropsDirtySource` is its only caller. */
|
|
988
|
-
function getRuntimeReactivity() {
|
|
989
|
-
return defaultRuntimeState.renderer;
|
|
990
|
-
}
|
|
991
|
-
/** Native boot composition does not require a published extension view. */
|
|
992
|
-
function installRuntimeRenderer(renderer) {
|
|
993
|
-
defaultRuntimeState.renderer = renderer;
|
|
994
|
-
}
|
|
995
|
-
function enqueueRuntimeTask(task) {
|
|
996
|
-
getRuntimeScheduler().enqueue(task);
|
|
997
|
-
}
|
|
998
|
-
function enqueueRuntimeLane(lane, task) {
|
|
999
|
-
getRuntimeScheduler().enqueueInLane(lane, task);
|
|
1000
|
-
}
|
|
1001
|
-
function requestRuntimeWork(lane, work) {
|
|
1002
|
-
work.request(getRuntimeScheduler(), lane);
|
|
1003
|
-
}
|
|
1004
|
-
function runRuntimeHandlerScope(fn, flushMode = "defer") {
|
|
1005
|
-
const savedScope = clearCurrentComponentScope();
|
|
1006
|
-
try {
|
|
1007
|
-
return getRuntimeScheduler().runInHandlerScope(fn, flushMode);
|
|
1008
|
-
} finally {
|
|
1009
|
-
endComponentScope(savedScope);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
function runRuntimeWithSyncProgress(fn) {
|
|
1013
|
-
return getRuntimeScheduler().runWithSyncProgress(fn);
|
|
1014
|
-
}
|
|
1015
|
-
function isRuntimeSchedulerExecuting() {
|
|
1016
|
-
return getRuntimeScheduler().isExecuting();
|
|
1017
|
-
}
|
|
1018
|
-
function getRuntimeFlushVersion() {
|
|
1019
|
-
return getRuntimeScheduler().getFlushVersion();
|
|
1020
|
-
}
|
|
1021
|
-
function flushRuntimeScheduler() {
|
|
1022
|
-
getRuntimeScheduler().flush();
|
|
1023
|
-
}
|
|
1024
|
-
function markRuntimeReactivePropsDirtySource(source) {
|
|
1025
|
-
getRuntimeReactivity().markReactivePropsDirtySource(source);
|
|
1026
|
-
}
|
|
1027
|
-
/**
|
|
1028
|
-
* Wrap an event listener so its work runs inside a scheduler handler scope.
|
|
1029
|
-
*
|
|
1030
|
-
* Resolves the active scheduler per call rather than closing over the module
|
|
1031
|
-
* singleton, so a runtime constructed with its own scheduler schedules its own
|
|
1032
|
-
* handlers.
|
|
1033
|
-
*/
|
|
1034
|
-
function scheduleEventHandler(handler) {
|
|
1035
|
-
return (event) => {
|
|
1036
|
-
try {
|
|
1037
|
-
getRuntimeScheduler().runInHandlerScope(() => {
|
|
1038
|
-
handler.call(null, event);
|
|
1039
|
-
});
|
|
1040
|
-
} catch (error) {
|
|
1041
|
-
logger.error("[Askr] Event handler error:", error);
|
|
1042
|
-
}
|
|
1043
|
-
};
|
|
1044
|
-
}
|
|
1045
|
-
//#endregion
|
|
1046
|
-
export { getRuntimeEnv as C, incrementPerfMetric as S, isProductionEnvironment as T, defaultRuntimeState as _, getRuntimeEvaluation as a, ScheduledWork as b, getRuntimeScopes as c, markRuntimeReactivePropsDirtySource as d, requestRuntimeWork as f, createRuntimeState as g, scheduleEventHandler as h, getRuntimeCleanup as i, installRuntimeRenderer as l, runRuntimeWithSyncProgress as m, enqueueRuntimeTask as n, getRuntimeFlushVersion as o, runRuntimeHandlerScope as p, flushRuntimeScheduler as r, getRuntimeKeys as s, enqueueRuntimeLane as t, isRuntimeSchedulerExecuting as u, logger as v, isDevelopmentEnvironment as w, addPerfDuration as x, SCHEDULER_LANES as y };
|