@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
|
@@ -0,0 +1,988 @@
|
|
|
1
|
+
//#region src/common/env.ts
|
|
2
|
+
function normalizeEnvValue(value) {
|
|
3
|
+
if (typeof value === "string") return value;
|
|
4
|
+
if (typeof value === "boolean" || typeof value === "number") return String(value);
|
|
5
|
+
}
|
|
6
|
+
function getBuildTimeMetaEnv() {
|
|
7
|
+
return import.meta.env;
|
|
8
|
+
}
|
|
9
|
+
function getBuildTimeNodeEnv(metaEnv) {
|
|
10
|
+
const nodeEnv = normalizeEnvValue(metaEnv?.NODE_ENV);
|
|
11
|
+
if (nodeEnv != null || !metaEnv) return nodeEnv;
|
|
12
|
+
if (typeof metaEnv.MODE === "string" && metaEnv.MODE.length > 0) return metaEnv.MODE;
|
|
13
|
+
if (metaEnv.PROD === true || metaEnv.PROD === "true") return "production";
|
|
14
|
+
if (metaEnv.DEV === true || metaEnv.DEV === "true") return "development";
|
|
15
|
+
}
|
|
16
|
+
function resolveNodeEnv(processNodeEnv, buildNodeEnv) {
|
|
17
|
+
if (buildNodeEnv === "production") return "production";
|
|
18
|
+
if (processNodeEnv != null) return processNodeEnv;
|
|
19
|
+
if (buildNodeEnv === "development") return "development";
|
|
20
|
+
return buildNodeEnv === "test" ? void 0 : buildNodeEnv;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Resolves one environment variable. Build-time values (`import.meta.env`)
|
|
24
|
+
* take precedence over `process.env`, except that `NODE_ENV` follows
|
|
25
|
+
* `resolveNodeEnv`. Only the requested variable is read: enumerating
|
|
26
|
+
* `process.env` is costly on some hosts (every enumeration copies the
|
|
27
|
+
* environment block on Windows), and these checks run on hot paths such as
|
|
28
|
+
* every component render.
|
|
29
|
+
*/
|
|
30
|
+
function getRuntimeEnvValue(name) {
|
|
31
|
+
const processValue = globalThis.process?.env?.[name];
|
|
32
|
+
const metaEnv = getBuildTimeMetaEnv();
|
|
33
|
+
if (name === "NODE_ENV") return resolveNodeEnv(processValue, getBuildTimeNodeEnv(metaEnv));
|
|
34
|
+
return normalizeEnvValue(metaEnv?.[name]) ?? processValue;
|
|
35
|
+
}
|
|
36
|
+
function isProductionEnvironment() {
|
|
37
|
+
return getRuntimeEnvValue("NODE_ENV") === "production";
|
|
38
|
+
}
|
|
39
|
+
function isDevelopmentEnvironment() {
|
|
40
|
+
return !isProductionEnvironment();
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/common/logger.ts
|
|
44
|
+
/**
|
|
45
|
+
* Shared logger interface for runtime and tooling code.
|
|
46
|
+
*
|
|
47
|
+
* Debug, info, and warn stay silent in production. Errors always log.
|
|
48
|
+
*/
|
|
49
|
+
function callConsole(method, args) {
|
|
50
|
+
const c = typeof console !== "undefined" ? console : void 0;
|
|
51
|
+
if (!c) return;
|
|
52
|
+
const fn = c[method];
|
|
53
|
+
if (typeof fn === "function") try {
|
|
54
|
+
fn.apply(console, args);
|
|
55
|
+
} catch {}
|
|
56
|
+
}
|
|
57
|
+
const logger = {
|
|
58
|
+
debug: (...args) => {
|
|
59
|
+
if (isProductionEnvironment()) return;
|
|
60
|
+
callConsole("debug", args);
|
|
61
|
+
},
|
|
62
|
+
info: (...args) => {
|
|
63
|
+
if (isProductionEnvironment()) return;
|
|
64
|
+
callConsole("info", args);
|
|
65
|
+
},
|
|
66
|
+
warn: (...args) => {
|
|
67
|
+
if (isProductionEnvironment()) return;
|
|
68
|
+
callConsole("warn", args);
|
|
69
|
+
},
|
|
70
|
+
error: (...args) => {
|
|
71
|
+
callConsole("error", args);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/core/component/diagnostics.ts
|
|
76
|
+
let settings = {
|
|
77
|
+
slowRenderWarnings: true,
|
|
78
|
+
slowRenderThresholdMs: 5
|
|
79
|
+
};
|
|
80
|
+
/** Configure render diagnostics; returns a function restoring the previous settings. */
|
|
81
|
+
function configureRenderDiagnostics(options) {
|
|
82
|
+
const threshold = options.slowRenderThresholdMs;
|
|
83
|
+
if (threshold !== void 0 && (!Number.isFinite(threshold) || threshold < 0)) throw new TypeError("configureRenderDiagnostics slowRenderThresholdMs must be a finite, non-negative number.");
|
|
84
|
+
const previous = settings;
|
|
85
|
+
settings = {
|
|
86
|
+
slowRenderWarnings: options.slowRenderWarnings ?? previous.slowRenderWarnings,
|
|
87
|
+
slowRenderThresholdMs: threshold ?? previous.slowRenderThresholdMs
|
|
88
|
+
};
|
|
89
|
+
let restored = false;
|
|
90
|
+
return () => {
|
|
91
|
+
if (restored) return;
|
|
92
|
+
restored = true;
|
|
93
|
+
settings = previous;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const warned = /* @__PURE__ */ new WeakSet();
|
|
97
|
+
function isTimingRenders() {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
function reportRenderTime(instance, name, milliseconds) {
|
|
101
|
+
if (milliseconds <= settings.slowRenderThresholdMs || warned.has(instance)) return;
|
|
102
|
+
warned.add(instance);
|
|
103
|
+
logger.warn(`[askr] Slow render detected in ${name}: ${milliseconds}ms. Consider optimizing component performance.`);
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/core/reactive/owner.ts
|
|
107
|
+
var Owner = class {
|
|
108
|
+
constructor(parent) {
|
|
109
|
+
this.owned = null;
|
|
110
|
+
this.ownedHoles = 0;
|
|
111
|
+
this.ownedIndex = -1;
|
|
112
|
+
this.cleanups = null;
|
|
113
|
+
this.context = null;
|
|
114
|
+
this.disposed = false;
|
|
115
|
+
this.parent = parent;
|
|
116
|
+
if (parent) {
|
|
117
|
+
const owned = parent.owned ??= [];
|
|
118
|
+
this.ownedIndex = owned.length;
|
|
119
|
+
owned.push(this);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
onCleanup(fn) {
|
|
123
|
+
if (this.disposed) {
|
|
124
|
+
fn();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
(this.cleanups ??= []).push(fn);
|
|
128
|
+
}
|
|
129
|
+
lookup(key) {
|
|
130
|
+
if (this.context?.has(key)) return this.context.get(key);
|
|
131
|
+
for (let owner = this.parent; owner; owner = owner.parent) if (owner.context?.has(key)) return owner.context.get(key);
|
|
132
|
+
}
|
|
133
|
+
/** Detach from the parent without disposing (the caller disposes). */
|
|
134
|
+
detach() {
|
|
135
|
+
const parent = this.parent;
|
|
136
|
+
const siblings = parent?.owned;
|
|
137
|
+
if (parent && siblings && siblings[this.ownedIndex] === this) {
|
|
138
|
+
if (this.ownedIndex === siblings.length - 1) {
|
|
139
|
+
siblings.pop();
|
|
140
|
+
while (siblings.length && siblings[siblings.length - 1] === null) {
|
|
141
|
+
siblings.pop();
|
|
142
|
+
parent.ownedHoles--;
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
siblings[this.ownedIndex] = null;
|
|
146
|
+
parent.ownedHoles++;
|
|
147
|
+
if (parent.ownedHoles > 16 && parent.ownedHoles * 2 > siblings.length) compactOwned(parent, siblings);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
this.parent = null;
|
|
151
|
+
this.ownedIndex = -1;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Dispose this owner and everything it owns. Returns the failures instead of
|
|
155
|
+
* throwing so a caller can decide whether to report or aggregate them.
|
|
156
|
+
*/
|
|
157
|
+
dispose(errors = []) {
|
|
158
|
+
if (this.disposed) return errors;
|
|
159
|
+
this.detach();
|
|
160
|
+
disposeTree(this, errors);
|
|
161
|
+
return errors;
|
|
162
|
+
}
|
|
163
|
+
/** Dispose owned children and run cleanups, keeping this owner alive. */
|
|
164
|
+
reset(errors = []) {
|
|
165
|
+
disposeChildren(this, errors);
|
|
166
|
+
runCleanups(this, errors);
|
|
167
|
+
return errors;
|
|
168
|
+
}
|
|
169
|
+
onDispose() {}
|
|
170
|
+
};
|
|
171
|
+
function compactOwned(parent, siblings) {
|
|
172
|
+
let next = 0;
|
|
173
|
+
for (const child of siblings) {
|
|
174
|
+
if (!child) continue;
|
|
175
|
+
child.ownedIndex = next;
|
|
176
|
+
siblings[next++] = child;
|
|
177
|
+
}
|
|
178
|
+
siblings.length = next;
|
|
179
|
+
parent.ownedHoles = 0;
|
|
180
|
+
}
|
|
181
|
+
/** Take `owner`'s children, leaving it with none. */
|
|
182
|
+
function takeOwned(owner) {
|
|
183
|
+
const owned = owner.owned;
|
|
184
|
+
owner.owned = null;
|
|
185
|
+
owner.ownedHoles = 0;
|
|
186
|
+
return owned;
|
|
187
|
+
}
|
|
188
|
+
function disposeChildren(owner, errors) {
|
|
189
|
+
const owned = takeOwned(owner);
|
|
190
|
+
if (!owned) return;
|
|
191
|
+
for (let i = owned.length - 1; i >= 0; i--) {
|
|
192
|
+
const child = owned[i];
|
|
193
|
+
if (!child) continue;
|
|
194
|
+
child.parent = null;
|
|
195
|
+
disposeTree(child, errors);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function runCleanups(owner, errors) {
|
|
199
|
+
const cleanups = owner.cleanups;
|
|
200
|
+
if (!cleanups) return;
|
|
201
|
+
owner.cleanups = null;
|
|
202
|
+
for (let i = cleanups.length - 1; i >= 0; i--) try {
|
|
203
|
+
cleanups[i]();
|
|
204
|
+
} catch (error) {
|
|
205
|
+
errors.push(error);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function disposeTree(owner, errors) {
|
|
209
|
+
const pending = [{
|
|
210
|
+
owner,
|
|
211
|
+
finish: false
|
|
212
|
+
}];
|
|
213
|
+
while (pending.length) {
|
|
214
|
+
const frame = pending.pop();
|
|
215
|
+
const current = frame.owner;
|
|
216
|
+
if (frame.finish) {
|
|
217
|
+
runCleanups(current, errors);
|
|
218
|
+
try {
|
|
219
|
+
current.onDispose();
|
|
220
|
+
} catch (error) {
|
|
221
|
+
errors.push(error);
|
|
222
|
+
}
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (frame.detach) current.parent = null;
|
|
226
|
+
if (current.disposed) continue;
|
|
227
|
+
current.disposed = true;
|
|
228
|
+
pending.push({
|
|
229
|
+
owner: current,
|
|
230
|
+
finish: true
|
|
231
|
+
});
|
|
232
|
+
const children = takeOwned(current);
|
|
233
|
+
if (!children) continue;
|
|
234
|
+
for (const child of children) if (child) pending.push({
|
|
235
|
+
owner: child,
|
|
236
|
+
finish: false,
|
|
237
|
+
detach: true
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
let currentOwner = null;
|
|
242
|
+
function getOwner() {
|
|
243
|
+
return currentOwner;
|
|
244
|
+
}
|
|
245
|
+
function runWithOwner(owner, fn) {
|
|
246
|
+
const previous = currentOwner;
|
|
247
|
+
currentOwner = owner;
|
|
248
|
+
try {
|
|
249
|
+
return fn();
|
|
250
|
+
} finally {
|
|
251
|
+
currentOwner = previous;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//#endregion
|
|
255
|
+
//#region src/core/reactive/graph.ts
|
|
256
|
+
/**
|
|
257
|
+
* Reactive graph.
|
|
258
|
+
*
|
|
259
|
+
* Sources hold values; computations derive values or perform effects from
|
|
260
|
+
* the sources they read. A write marks direct observers DIRTY and everything
|
|
261
|
+
* downstream of them CHECK. A computation that is read (or run by the
|
|
262
|
+
* scheduler) first brings its CHECK sources up to date and only re-runs if
|
|
263
|
+
* one of them actually changed, so updates are glitch-free and unchanged
|
|
264
|
+
* derived values cut propagation off.
|
|
265
|
+
*
|
|
266
|
+
* There is one subscriber kind. `derive()`, `selector()`, fine-grained
|
|
267
|
+
* bindings, `watch()`, and component renders are all computations; they
|
|
268
|
+
* differ only in how they are scheduled when they go stale.
|
|
269
|
+
*/
|
|
270
|
+
const CLEAN = 0;
|
|
271
|
+
const CHECK = 1;
|
|
272
|
+
const DIRTY = 2;
|
|
273
|
+
function unobserve(source, observer) {
|
|
274
|
+
const observers = source._observers;
|
|
275
|
+
if (!observers?.delete(observer) || observers.size > 0) return;
|
|
276
|
+
source._unobserved?.();
|
|
277
|
+
}
|
|
278
|
+
let tracking = null;
|
|
279
|
+
let trackingSources = null;
|
|
280
|
+
function track(source) {
|
|
281
|
+
if (trackingSources) trackingSources.add(source);
|
|
282
|
+
}
|
|
283
|
+
/** Notify every observer of `source` that its value changed. */
|
|
284
|
+
function notifySource(source, except) {
|
|
285
|
+
const observers = source._observers;
|
|
286
|
+
if (!observers) return;
|
|
287
|
+
for (const observer of observers) if (observer !== except) observer._mark(DIRTY);
|
|
288
|
+
}
|
|
289
|
+
/** Record a read of `source` by the running computation. */
|
|
290
|
+
function trackSource(source) {
|
|
291
|
+
track(source);
|
|
292
|
+
}
|
|
293
|
+
function isTracking() {
|
|
294
|
+
return trackingSources !== null;
|
|
295
|
+
}
|
|
296
|
+
function getTrackingComputation() {
|
|
297
|
+
return tracking;
|
|
298
|
+
}
|
|
299
|
+
function untrack(fn) {
|
|
300
|
+
const previous = tracking;
|
|
301
|
+
const previousSources = trackingSources;
|
|
302
|
+
tracking = null;
|
|
303
|
+
trackingSources = null;
|
|
304
|
+
try {
|
|
305
|
+
return fn();
|
|
306
|
+
} finally {
|
|
307
|
+
tracking = previous;
|
|
308
|
+
trackingSources = previousSources;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
var Signal = class {
|
|
312
|
+
constructor(value, equals = Object.is) {
|
|
313
|
+
this._observers = null;
|
|
314
|
+
this._value = value;
|
|
315
|
+
this._equals = equals;
|
|
316
|
+
}
|
|
317
|
+
read() {
|
|
318
|
+
track(this);
|
|
319
|
+
return this._value;
|
|
320
|
+
}
|
|
321
|
+
peek() {
|
|
322
|
+
return this._value;
|
|
323
|
+
}
|
|
324
|
+
/** Returns true when the value changed and observers were notified. */
|
|
325
|
+
write(value) {
|
|
326
|
+
if (this._equals(this._value, value)) return false;
|
|
327
|
+
this._value = value;
|
|
328
|
+
notifySource(this);
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
var Computation = class Computation extends Owner {
|
|
333
|
+
constructor(owner, fn, schedule, equals = Object.is, failedRunSources = "union") {
|
|
334
|
+
super(owner);
|
|
335
|
+
this._observers = null;
|
|
336
|
+
this._sources = null;
|
|
337
|
+
this._state = DIRTY;
|
|
338
|
+
this._value = void 0;
|
|
339
|
+
this._error = void 0;
|
|
340
|
+
this._hasError = false;
|
|
341
|
+
this._running = false;
|
|
342
|
+
this._invalidatedWhileRunning = false;
|
|
343
|
+
this._dropped = false;
|
|
344
|
+
this._fn = fn;
|
|
345
|
+
this._schedule = schedule;
|
|
346
|
+
this._equals = equals;
|
|
347
|
+
this._failedRunSources = failedRunSources;
|
|
348
|
+
}
|
|
349
|
+
/** Read the current value, recomputing if a source changed. */
|
|
350
|
+
read() {
|
|
351
|
+
if (this._running) throw new Error("Circular reactive dependency detected.");
|
|
352
|
+
track(this);
|
|
353
|
+
this.update();
|
|
354
|
+
if (this._hasError) throw this._error;
|
|
355
|
+
return this._value;
|
|
356
|
+
}
|
|
357
|
+
get stale() {
|
|
358
|
+
return this._state !== CLEAN;
|
|
359
|
+
}
|
|
360
|
+
/** Bring this computation up to date, running it only if a source changed. */
|
|
361
|
+
update() {
|
|
362
|
+
if (this.disposed) return;
|
|
363
|
+
if (this._state === CHECK) {
|
|
364
|
+
for (const source of this._sources ?? EMPTY) if (source instanceof Computation) {
|
|
365
|
+
source.update();
|
|
366
|
+
if (this._state === DIRTY) break;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (this._state === DIRTY) this.run();
|
|
370
|
+
else this._state = CLEAN;
|
|
371
|
+
}
|
|
372
|
+
/** Run unconditionally, tracking the sources read. */
|
|
373
|
+
run() {
|
|
374
|
+
if (this.disposed) return;
|
|
375
|
+
const previous = tracking;
|
|
376
|
+
const previousSources = trackingSources;
|
|
377
|
+
const nextSources = /* @__PURE__ */ new Set();
|
|
378
|
+
tracking = this;
|
|
379
|
+
trackingSources = nextSources;
|
|
380
|
+
this._running = true;
|
|
381
|
+
this._invalidatedWhileRunning = false;
|
|
382
|
+
this._dropped = false;
|
|
383
|
+
let value;
|
|
384
|
+
let error;
|
|
385
|
+
let failed = false;
|
|
386
|
+
try {
|
|
387
|
+
value = this._fn();
|
|
388
|
+
} catch (caught) {
|
|
389
|
+
failed = true;
|
|
390
|
+
error = caught;
|
|
391
|
+
} finally {
|
|
392
|
+
this._running = false;
|
|
393
|
+
tracking = previous;
|
|
394
|
+
trackingSources = previousSources;
|
|
395
|
+
}
|
|
396
|
+
if (this.disposed) return;
|
|
397
|
+
this._state = this._invalidatedWhileRunning ? DIRTY : CLEAN;
|
|
398
|
+
this.setSources(failed && this._failedRunSources === "previous" ? new Set(this._sources ?? EMPTY) : nextSources, failed && this._failedRunSources === "union");
|
|
399
|
+
if (failed) {
|
|
400
|
+
const changed = !this._hasError || this._error !== error;
|
|
401
|
+
this._hasError = true;
|
|
402
|
+
this._error = error;
|
|
403
|
+
this._value = void 0;
|
|
404
|
+
if (changed) this.markObserversDirty();
|
|
405
|
+
if (this._schedule) throw error;
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const changed = this._hasError || this._equals === null || !this._equals(this._value, value);
|
|
409
|
+
this._hasError = false;
|
|
410
|
+
this._error = void 0;
|
|
411
|
+
this._value = value;
|
|
412
|
+
if (changed) this.markObserversDirty();
|
|
413
|
+
}
|
|
414
|
+
/** Replace the function this computation runs; it runs again on next update. */
|
|
415
|
+
setFn(fn) {
|
|
416
|
+
this._fn = fn;
|
|
417
|
+
this._mark(DIRTY);
|
|
418
|
+
}
|
|
419
|
+
/** Restore subscriptions captured before a discarded component pass. */
|
|
420
|
+
restoreSources(sources) {
|
|
421
|
+
this.setSources(new Set(sources), false);
|
|
422
|
+
}
|
|
423
|
+
/** Force a re-run on the next update, as if a source changed. */
|
|
424
|
+
invalidate() {
|
|
425
|
+
this._mark(DIRTY);
|
|
426
|
+
}
|
|
427
|
+
/** Retry after a later source change, without rerunning a discarded pass. */
|
|
428
|
+
deferRetry() {
|
|
429
|
+
if (this.disposed) return;
|
|
430
|
+
this._state = DIRTY;
|
|
431
|
+
this._dropped = true;
|
|
432
|
+
}
|
|
433
|
+
markObserversDirty() {
|
|
434
|
+
const observers = this._observers;
|
|
435
|
+
if (!observers) return;
|
|
436
|
+
for (const observer of observers) observer._state = DIRTY;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* A failed run keeps its previous subscriptions as well, so a change to
|
|
440
|
+
* anything either run read retries it.
|
|
441
|
+
*/
|
|
442
|
+
setSources(next, keepPrevious) {
|
|
443
|
+
const previous = this._sources;
|
|
444
|
+
if (previous) {
|
|
445
|
+
for (const source of previous) if (keepPrevious) next.add(source);
|
|
446
|
+
else if (!next.has(source)) unobserve(source, this);
|
|
447
|
+
}
|
|
448
|
+
for (const source of next) (source._observers ??= /* @__PURE__ */ new Set()).add(this);
|
|
449
|
+
this._sources = next.size ? next : null;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* The scheduler dropped this computation's pending run (loop guard or
|
|
453
|
+
* clear). It stays stale, and the next change schedules it again.
|
|
454
|
+
*/
|
|
455
|
+
dropScheduled() {
|
|
456
|
+
if (!this.disposed && this._state !== CLEAN) this._dropped = true;
|
|
457
|
+
}
|
|
458
|
+
_mark(state) {
|
|
459
|
+
if (this.disposed) return;
|
|
460
|
+
if (this._running && state === DIRTY) {
|
|
461
|
+
const firstInvalidation = !this._invalidatedWhileRunning;
|
|
462
|
+
this._invalidatedWhileRunning = true;
|
|
463
|
+
if (state > this._state) this._state = state;
|
|
464
|
+
this._dropped = false;
|
|
465
|
+
this._schedule?.(this);
|
|
466
|
+
if (firstInvalidation) {
|
|
467
|
+
const observers = this._observers;
|
|
468
|
+
if (observers) for (const observer of observers) observer._mark(CHECK);
|
|
469
|
+
}
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const dropped = this._dropped;
|
|
473
|
+
if (this._state >= state && !dropped) return;
|
|
474
|
+
const wasClean = this._state === CLEAN;
|
|
475
|
+
if (state > this._state) this._state = state;
|
|
476
|
+
if (!wasClean && !dropped) return;
|
|
477
|
+
this._dropped = false;
|
|
478
|
+
if (this._schedule) this._schedule(this);
|
|
479
|
+
if (!wasClean) return;
|
|
480
|
+
const observers = this._observers;
|
|
481
|
+
if (observers) for (const observer of observers) observer._mark(CHECK);
|
|
482
|
+
}
|
|
483
|
+
onDispose() {
|
|
484
|
+
for (const source of this._sources ?? EMPTY) unobserve(source, this);
|
|
485
|
+
this._sources = null;
|
|
486
|
+
this._observers = null;
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
const EMPTY = /* @__PURE__ */ new Set();
|
|
490
|
+
/** FIFO queue with an advancing head, so taking the next job is O(1). */
|
|
491
|
+
var JobQueue = class {
|
|
492
|
+
constructor() {
|
|
493
|
+
this.items = [];
|
|
494
|
+
this.head = 0;
|
|
495
|
+
}
|
|
496
|
+
get length() {
|
|
497
|
+
return this.items.length - this.head;
|
|
498
|
+
}
|
|
499
|
+
push(job) {
|
|
500
|
+
this.items.push(job);
|
|
501
|
+
}
|
|
502
|
+
shift() {
|
|
503
|
+
if (this.head >= this.items.length) return void 0;
|
|
504
|
+
const job = this.items[this.head];
|
|
505
|
+
this.items[this.head++] = void 0;
|
|
506
|
+
if (this.head === this.items.length) {
|
|
507
|
+
this.items.length = 0;
|
|
508
|
+
this.head = 0;
|
|
509
|
+
} else if (this.head > 1024 && this.head * 2 > this.items.length) {
|
|
510
|
+
this.items = this.items.slice(this.head);
|
|
511
|
+
this.head = 0;
|
|
512
|
+
}
|
|
513
|
+
return job;
|
|
514
|
+
}
|
|
515
|
+
clear() {
|
|
516
|
+
this.items.length = 0;
|
|
517
|
+
this.head = 0;
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
/**
|
|
521
|
+
* Render lane: a binary min-heap ordered by depth, then by queue order, so the
|
|
522
|
+
* shallowest job runs first and equal depths run in the order they queued.
|
|
523
|
+
*/
|
|
524
|
+
var RenderQueue = class {
|
|
525
|
+
constructor() {
|
|
526
|
+
this.heap = [];
|
|
527
|
+
this.seq = 0;
|
|
528
|
+
}
|
|
529
|
+
get length() {
|
|
530
|
+
return this.heap.length;
|
|
531
|
+
}
|
|
532
|
+
push(job) {
|
|
533
|
+
const heap = this.heap;
|
|
534
|
+
const entry = {
|
|
535
|
+
job,
|
|
536
|
+
depth: job.depth ?? 0,
|
|
537
|
+
seq: this.seq++
|
|
538
|
+
};
|
|
539
|
+
let index = heap.push(entry) - 1;
|
|
540
|
+
while (index > 0) {
|
|
541
|
+
const parent = index - 1 >> 1;
|
|
542
|
+
if (!before(entry, heap[parent])) break;
|
|
543
|
+
heap[index] = heap[parent];
|
|
544
|
+
index = parent;
|
|
545
|
+
}
|
|
546
|
+
heap[index] = entry;
|
|
547
|
+
}
|
|
548
|
+
shift() {
|
|
549
|
+
const heap = this.heap;
|
|
550
|
+
if (heap.length === 0) return void 0;
|
|
551
|
+
const top = heap[0];
|
|
552
|
+
const last = heap.pop();
|
|
553
|
+
if (heap.length > 0) {
|
|
554
|
+
let index = 0;
|
|
555
|
+
for (;;) {
|
|
556
|
+
const left = index * 2 + 1;
|
|
557
|
+
if (left >= heap.length) break;
|
|
558
|
+
const right = left + 1;
|
|
559
|
+
const child = right < heap.length && before(heap[right], heap[left]) ? right : left;
|
|
560
|
+
if (!before(heap[child], last)) break;
|
|
561
|
+
heap[index] = heap[child];
|
|
562
|
+
index = child;
|
|
563
|
+
}
|
|
564
|
+
heap[index] = last;
|
|
565
|
+
}
|
|
566
|
+
if (heap.length === 0) this.seq = 0;
|
|
567
|
+
return top.job;
|
|
568
|
+
}
|
|
569
|
+
clear() {
|
|
570
|
+
this.heap.length = 0;
|
|
571
|
+
this.seq = 0;
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
function before(a, b) {
|
|
575
|
+
return a.depth < b.depth || a.depth === b.depth && a.seq < b.seq;
|
|
576
|
+
}
|
|
577
|
+
const lanes = {
|
|
578
|
+
render: new RenderQueue(),
|
|
579
|
+
effect: new JobQueue(),
|
|
580
|
+
post: new JobQueue()
|
|
581
|
+
};
|
|
582
|
+
const queued = /* @__PURE__ */ new Set();
|
|
583
|
+
let flushing = false;
|
|
584
|
+
let batchDepth = 0;
|
|
585
|
+
let kickScheduled = false;
|
|
586
|
+
let flushVersion = 0;
|
|
587
|
+
let runCounts = null;
|
|
588
|
+
const flushWaiters = [];
|
|
589
|
+
function schedule(job, lane) {
|
|
590
|
+
if (queued.has(job)) return;
|
|
591
|
+
queued.add(job);
|
|
592
|
+
lanes[lane].push(job);
|
|
593
|
+
kick();
|
|
594
|
+
}
|
|
595
|
+
function queueTask(fn) {
|
|
596
|
+
const owner = getOwner();
|
|
597
|
+
schedule({ run: () => runWithOwner(owner, fn) }, "post");
|
|
598
|
+
}
|
|
599
|
+
const computationJobs = /* @__PURE__ */ new WeakMap();
|
|
600
|
+
/**
|
|
601
|
+
* A scheduler for computations that run as effects: when one goes stale it
|
|
602
|
+
* is queued in `lane` and brought up to date (re-running only if a source
|
|
603
|
+
* actually changed) when the flush reaches it.
|
|
604
|
+
*/
|
|
605
|
+
function effectScheduler(lane, depth = 0, onLimit, maxRuns) {
|
|
606
|
+
return (computation) => {
|
|
607
|
+
let job = computationJobs.get(computation);
|
|
608
|
+
if (!job) {
|
|
609
|
+
job = {
|
|
610
|
+
depth,
|
|
611
|
+
run: () => computation.update(),
|
|
612
|
+
cancel: () => computation.dropScheduled(),
|
|
613
|
+
onLimit,
|
|
614
|
+
maxRuns,
|
|
615
|
+
get skip() {
|
|
616
|
+
return computation.disposed;
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
computationJobs.set(computation, job);
|
|
620
|
+
}
|
|
621
|
+
schedule(job, lane);
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
function isFlushing() {
|
|
625
|
+
return flushing;
|
|
626
|
+
}
|
|
627
|
+
function kick() {
|
|
628
|
+
if (flushing || batchDepth > 0 || kickScheduled) return;
|
|
629
|
+
kickScheduled = true;
|
|
630
|
+
queueMicrotask(() => {
|
|
631
|
+
kickScheduled = false;
|
|
632
|
+
if (!flushing && batchDepth === 0 && queued.size > 0) flushSync();
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Run `fn` with scheduling deferred, then flush synchronously when the
|
|
637
|
+
* outermost batch exits. Event handlers run inside a batch.
|
|
638
|
+
*/
|
|
639
|
+
function batch(fn) {
|
|
640
|
+
batchDepth++;
|
|
641
|
+
let value;
|
|
642
|
+
let callbackError;
|
|
643
|
+
let callbackFailed = false;
|
|
644
|
+
try {
|
|
645
|
+
value = fn();
|
|
646
|
+
} catch (error) {
|
|
647
|
+
callbackFailed = true;
|
|
648
|
+
callbackError = error;
|
|
649
|
+
}
|
|
650
|
+
batchDepth--;
|
|
651
|
+
let flushError;
|
|
652
|
+
let flushFailed = false;
|
|
653
|
+
if (batchDepth === 0 && !flushing && queued.size > 0) try {
|
|
654
|
+
flushSync();
|
|
655
|
+
} catch (error) {
|
|
656
|
+
flushFailed = true;
|
|
657
|
+
flushError = error;
|
|
658
|
+
}
|
|
659
|
+
if (callbackFailed && flushFailed) throw new AggregateError([callbackError, flushError], "Batch callback and scheduler flush failed");
|
|
660
|
+
if (callbackFailed) throw callbackError;
|
|
661
|
+
if (flushFailed) throw flushError;
|
|
662
|
+
return value;
|
|
663
|
+
}
|
|
664
|
+
function takeNext() {
|
|
665
|
+
return lanes.render.shift() ?? lanes.effect.shift() ?? lanes.post.shift() ?? null;
|
|
666
|
+
}
|
|
667
|
+
/** Drain every queued job now. Nested calls during a flush are no-ops. */
|
|
668
|
+
function flushSync() {
|
|
669
|
+
if (flushing) return;
|
|
670
|
+
flushing = true;
|
|
671
|
+
runCounts = /* @__PURE__ */ new Map();
|
|
672
|
+
const failures = [];
|
|
673
|
+
try {
|
|
674
|
+
for (let job = takeNext(); job; job = takeNext()) {
|
|
675
|
+
queued.delete(job);
|
|
676
|
+
if (job.skip) continue;
|
|
677
|
+
const count = (runCounts.get(job) ?? 0) + 1;
|
|
678
|
+
runCounts.set(job, count);
|
|
679
|
+
if (count > (job.maxRuns ?? 50)) {
|
|
680
|
+
job.cancel?.();
|
|
681
|
+
if (job.onLimit) try {
|
|
682
|
+
job.onLimit();
|
|
683
|
+
} catch (error) {
|
|
684
|
+
failures.push(error);
|
|
685
|
+
}
|
|
686
|
+
else failures.push(/* @__PURE__ */ new Error(`[Askr] exceeded MAX_FLUSH_DEPTH (50): a scheduled update kept re-queueing itself.`));
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
try {
|
|
690
|
+
job.run();
|
|
691
|
+
} catch (error) {
|
|
692
|
+
failures.push(error);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
} finally {
|
|
696
|
+
flushing = false;
|
|
697
|
+
runCounts = null;
|
|
698
|
+
flushVersion++;
|
|
699
|
+
for (const resolve of flushWaiters.splice(0)) resolve();
|
|
700
|
+
}
|
|
701
|
+
if (failures.length === 1) throw failures[0];
|
|
702
|
+
if (failures.length > 1) throw new AggregateError(failures, "Scheduler flush failed");
|
|
703
|
+
}
|
|
704
|
+
function getFlushVersion() {
|
|
705
|
+
return flushVersion;
|
|
706
|
+
}
|
|
707
|
+
//#endregion
|
|
708
|
+
//#region src/core/component/render-state.ts
|
|
709
|
+
/**
|
|
710
|
+
* Whether a component render or a function child is executing. State writes
|
|
711
|
+
* are rejected while one is, because they would re-trigger the render.
|
|
712
|
+
*/
|
|
713
|
+
let renderDepth = 0;
|
|
714
|
+
function isRendering() {
|
|
715
|
+
return renderDepth > 0;
|
|
716
|
+
}
|
|
717
|
+
function withRendering(fn) {
|
|
718
|
+
renderDepth++;
|
|
719
|
+
try {
|
|
720
|
+
return fn();
|
|
721
|
+
} finally {
|
|
722
|
+
renderDepth--;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
/** Run `fn` outside any render (event handlers and lifecycle callbacks). */
|
|
726
|
+
function outsideRendering(fn) {
|
|
727
|
+
const saved = renderDepth;
|
|
728
|
+
renderDepth = 0;
|
|
729
|
+
try {
|
|
730
|
+
return fn();
|
|
731
|
+
} finally {
|
|
732
|
+
renderDepth = saved;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
//#endregion
|
|
736
|
+
//#region src/core/component/journal.ts
|
|
737
|
+
let activeJournal = null;
|
|
738
|
+
function createRenderJournal() {
|
|
739
|
+
return { entries: [] };
|
|
740
|
+
}
|
|
741
|
+
/** Run synchronous render work with `journal` as its undo destination. */
|
|
742
|
+
function withRenderJournal(journal, render) {
|
|
743
|
+
const previous = activeJournal;
|
|
744
|
+
activeJournal = journal;
|
|
745
|
+
try {
|
|
746
|
+
return render();
|
|
747
|
+
} finally {
|
|
748
|
+
activeJournal = previous;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
function recordUndo(undo, journal = activeJournal) {
|
|
752
|
+
journal?.entries.push(undo);
|
|
753
|
+
}
|
|
754
|
+
function journalMark(journal) {
|
|
755
|
+
return journal.entries.length;
|
|
756
|
+
}
|
|
757
|
+
/** Undo everything recorded after `mark`, most recent first. */
|
|
758
|
+
function rewindJournal(journal, mark, errors) {
|
|
759
|
+
while (journal.entries.length > mark) try {
|
|
760
|
+
journal.entries.pop()();
|
|
761
|
+
} catch (error) {
|
|
762
|
+
errors.push(error);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
/** Keep the changes recorded after `mark` (their render committed). */
|
|
766
|
+
function settleJournal(journal, mark) {
|
|
767
|
+
journal.entries.length = Math.min(journal.entries.length, mark);
|
|
768
|
+
}
|
|
769
|
+
//#endregion
|
|
770
|
+
//#region src/core/component/setup.ts
|
|
771
|
+
const definitions = /* @__PURE__ */ new WeakMap();
|
|
772
|
+
function setupDefinitionFor(component) {
|
|
773
|
+
return definitions.get(component);
|
|
774
|
+
}
|
|
775
|
+
//#endregion
|
|
776
|
+
//#region src/core/component/instance.ts
|
|
777
|
+
let renderHost = null;
|
|
778
|
+
let nextContextRevision = 0;
|
|
779
|
+
/** Bumped whenever any instance's `contextRevision` changes or is undone. */
|
|
780
|
+
let contextEpoch = 0;
|
|
781
|
+
/** Current context epoch; cached inherited revisions are valid within one. */
|
|
782
|
+
function getContextEpoch() {
|
|
783
|
+
return contextEpoch;
|
|
784
|
+
}
|
|
785
|
+
function setRenderHost(host) {
|
|
786
|
+
renderHost = host;
|
|
787
|
+
}
|
|
788
|
+
var ComponentInstance = class extends Owner {
|
|
789
|
+
get displayName() {
|
|
790
|
+
return this.name ?? this.fn.name;
|
|
791
|
+
}
|
|
792
|
+
constructor(parent, fn, props, onStale) {
|
|
793
|
+
super(parent);
|
|
794
|
+
this.propsSource = null;
|
|
795
|
+
this.hooks = [];
|
|
796
|
+
this.hookKinds = [];
|
|
797
|
+
this.hookIndex = 0;
|
|
798
|
+
this.name = null;
|
|
799
|
+
this.mounted = false;
|
|
800
|
+
this.renderCount = 0;
|
|
801
|
+
this.commitQueue = null;
|
|
802
|
+
this.commitSyncQueue = null;
|
|
803
|
+
this.abortController = null;
|
|
804
|
+
this.setupRender = null;
|
|
805
|
+
this.setupPhase = null;
|
|
806
|
+
this.view = null;
|
|
807
|
+
this.server = false;
|
|
808
|
+
this.serverResourceKeys = [];
|
|
809
|
+
this.hydrationResourceKeys = null;
|
|
810
|
+
this.hydrationResourceIndex = 0;
|
|
811
|
+
this.serverContext = void 0;
|
|
812
|
+
this.boundary = null;
|
|
813
|
+
this.contextRevision = 0;
|
|
814
|
+
this.seenAncestorContextRevision = 0;
|
|
815
|
+
this.inheritedContextRevision = 0;
|
|
816
|
+
this.inheritedContextEpoch = -1;
|
|
817
|
+
this.fn = fn;
|
|
818
|
+
this.props = props;
|
|
819
|
+
this.setupDefinition = setupDefinitionFor(fn);
|
|
820
|
+
this.depth = depthOf(parent) + 1;
|
|
821
|
+
this.computation = new Computation(this, () => this.invoke(), onStale ?? renderLaneScheduler(this), null, "previous");
|
|
822
|
+
}
|
|
823
|
+
get signal() {
|
|
824
|
+
return (this.abortController ??= new AbortController()).signal;
|
|
825
|
+
}
|
|
826
|
+
/** Run the component function. Called only through `computation`. */
|
|
827
|
+
invoke() {
|
|
828
|
+
const previous = enterInstance(this);
|
|
829
|
+
this.hookIndex = 0;
|
|
830
|
+
this.hydrationResourceIndex = 0;
|
|
831
|
+
if (this.server) this.serverResourceKeys = [];
|
|
832
|
+
this.commitQueue = null;
|
|
833
|
+
this.commitSyncQueue = null;
|
|
834
|
+
try {
|
|
835
|
+
const context = this.server ? {
|
|
836
|
+
signal: this.signal,
|
|
837
|
+
ssr: this.serverContext
|
|
838
|
+
} : { signal: this.signal };
|
|
839
|
+
const started = !this.server && isTimingRenders() ? Date.now() : 0;
|
|
840
|
+
const output = withRendering(() => runWithOwner(this, () => {
|
|
841
|
+
const setup = this.setupDefinition;
|
|
842
|
+
if (!setup) return this.fn(this.props, context);
|
|
843
|
+
if (!this.setupRender) {
|
|
844
|
+
this.setupPhase = "setup";
|
|
845
|
+
try {
|
|
846
|
+
this.setupRender = setup(this.props, context, () => (this.propsSource ??= new Signal(this.props)).read());
|
|
847
|
+
} finally {
|
|
848
|
+
this.setupPhase = null;
|
|
849
|
+
}
|
|
850
|
+
verifyHookCount(this);
|
|
851
|
+
}
|
|
852
|
+
this.setupPhase = "render";
|
|
853
|
+
try {
|
|
854
|
+
return this.setupRender(this.props, context);
|
|
855
|
+
} finally {
|
|
856
|
+
this.setupPhase = null;
|
|
857
|
+
}
|
|
858
|
+
}));
|
|
859
|
+
if (started) reportRenderTime(this, this.displayName || "<anonymous>", Date.now() - started);
|
|
860
|
+
if (!this.setupDefinition) verifyHookCount(this);
|
|
861
|
+
this.renderCount++;
|
|
862
|
+
return output;
|
|
863
|
+
} finally {
|
|
864
|
+
enterInstance(previous);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
/** Render now with the current props, tracking reads. Throws on failure. */
|
|
868
|
+
render(onDiscard) {
|
|
869
|
+
if (onDiscard && this.mounted) {
|
|
870
|
+
const sources = new Set(this.computation._sources ?? []);
|
|
871
|
+
onDiscard(() => this.computation.restoreSources(sources));
|
|
872
|
+
}
|
|
873
|
+
this.computation.run();
|
|
874
|
+
if (this.computation._hasError) throw this.computation._error;
|
|
875
|
+
return this.computation._value;
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Make `value` visible under `key` to everything this instance renders.
|
|
879
|
+
* Called during render; undone if the render is discarded.
|
|
880
|
+
*/
|
|
881
|
+
provide(key, value) {
|
|
882
|
+
const context = this.context ??= /* @__PURE__ */ new Map();
|
|
883
|
+
const had = context.has(key);
|
|
884
|
+
const previous = context.get(key);
|
|
885
|
+
if (had && Object.is(previous, value)) return;
|
|
886
|
+
const previousRevision = this.contextRevision;
|
|
887
|
+
this.contextRevision = ++nextContextRevision;
|
|
888
|
+
contextEpoch++;
|
|
889
|
+
context.set(key, value);
|
|
890
|
+
recordUndo(() => {
|
|
891
|
+
this.contextRevision = previousRevision;
|
|
892
|
+
contextEpoch++;
|
|
893
|
+
if (had) context.set(key, previous);
|
|
894
|
+
else context.delete(key);
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
/** Replace props for the render that follows; undone if it is discarded. */
|
|
898
|
+
setProps(props) {
|
|
899
|
+
const previous = this.props;
|
|
900
|
+
if (previous === props) return;
|
|
901
|
+
this.props = props;
|
|
902
|
+
this.propsSource?.write(props);
|
|
903
|
+
recordUndo(() => {
|
|
904
|
+
this.props = previous;
|
|
905
|
+
this.propsSource?.write(previous);
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
onCommit(fn) {
|
|
909
|
+
(this.commitQueue ??= []).push(fn);
|
|
910
|
+
}
|
|
911
|
+
onCommitSync(fn) {
|
|
912
|
+
(this.commitSyncQueue ??= []).push(fn);
|
|
913
|
+
}
|
|
914
|
+
isSetupRenderPhase() {
|
|
915
|
+
return this.setupPhase === "render";
|
|
916
|
+
}
|
|
917
|
+
onDispose() {
|
|
918
|
+
this.abortController?.abort(COMPONENT_ABORT_REASON);
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
const COMPONENT_ABORT_REASON = new DOMException("This operation was aborted", "AbortError");
|
|
922
|
+
function depthOf(owner) {
|
|
923
|
+
for (let o = owner; o; o = o.parent) {
|
|
924
|
+
if (o instanceof ComponentInstance) return o.depth;
|
|
925
|
+
const depth = o.depth;
|
|
926
|
+
if (typeof depth === "number") return depth;
|
|
927
|
+
}
|
|
928
|
+
return 0;
|
|
929
|
+
}
|
|
930
|
+
function renderLaneScheduler(instance) {
|
|
931
|
+
let job = null;
|
|
932
|
+
return () => {
|
|
933
|
+
job ??= {
|
|
934
|
+
depth: instance.depth,
|
|
935
|
+
run: () => renderHost?.rerender(instance),
|
|
936
|
+
cancel: () => instance.computation.dropScheduled(),
|
|
937
|
+
get skip() {
|
|
938
|
+
return instance.disposed || !instance.computation.stale;
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
schedule(job, "render");
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
let currentInstance = null;
|
|
945
|
+
function enterInstance(instance) {
|
|
946
|
+
const previous = currentInstance;
|
|
947
|
+
currentInstance = instance;
|
|
948
|
+
return previous;
|
|
949
|
+
}
|
|
950
|
+
function getCurrentInstance() {
|
|
951
|
+
return currentInstance;
|
|
952
|
+
}
|
|
953
|
+
function requireInstance(api) {
|
|
954
|
+
const instance = currentInstance;
|
|
955
|
+
if (!instance) throw new Error(`${api} can only be called during component render execution. Move ${api} calls to the top level of your component function.`);
|
|
956
|
+
return instance;
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Claim the next hook slot. Returns the slot index; the caller stores its
|
|
960
|
+
* value in `instance.hooks[index]`.
|
|
961
|
+
*/
|
|
962
|
+
function claimHook(instance, kind) {
|
|
963
|
+
if (instance.isSetupRenderPhase()) throw new Error(`[Askr] ${kind}() cannot be called during setup component render. Declare lifecycle values in the setup callback.`);
|
|
964
|
+
const index = instance.hookIndex++;
|
|
965
|
+
const expected = instance.hookKinds[index];
|
|
966
|
+
if (expected === void 0) {
|
|
967
|
+
if (instance.renderCount > 0) throw hookOrderError(instance, index, kind, void 0);
|
|
968
|
+
instance.hookKinds[index] = kind;
|
|
969
|
+
} else if (expected !== kind) throw hookOrderError(instance, index, kind, expected);
|
|
970
|
+
return index;
|
|
971
|
+
}
|
|
972
|
+
function verifyHookCount(instance) {
|
|
973
|
+
if (instance.renderCount > 0 && instance.hookIndex !== instance.hookKinds.length) throw hookOrderError(instance, instance.hookIndex, void 0, instance.hookKinds[instance.hookIndex]);
|
|
974
|
+
}
|
|
975
|
+
var HookOrderChangeError = class extends Error {
|
|
976
|
+
constructor(instance, message) {
|
|
977
|
+
super(message);
|
|
978
|
+
this.instance = instance;
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
function hookOrderError(instance, index, actual, expected) {
|
|
982
|
+
return new HookOrderChangeError(instance, `[Askr] Hook order changed in ${instance.displayName || "anonymous component"}: slot ${index} was ${expected ? `${expected}()` : "not claimed"} on the first render but ${actual ? `${actual}()` : "not claimed"} now. Call state(), derive() and other lifecycle APIs unconditionally at the top level of the component, in the same order on every render.`);
|
|
983
|
+
}
|
|
984
|
+
function getRenderHost() {
|
|
985
|
+
return renderHost;
|
|
986
|
+
}
|
|
987
|
+
//#endregion
|
|
988
|
+
export { untrack as A, schedule as C, isTracking as D, getTrackingComputation as E, logger as F, isDevelopmentEnvironment as I, isProductionEnvironment as L, getOwner as M, runWithOwner as N, notifySource as O, configureRenderDiagnostics as P, queueTask as S, Signal as T, batch as _, getCurrentInstance as a, getFlushVersion as b, setRenderHost as c, recordUndo as d, rewindJournal as f, outsideRendering as g, isRendering as h, getContextEpoch as i, Owner as j, trackSource as k, createRenderJournal as l, withRenderJournal as m, HookOrderChangeError as n, getRenderHost as o, settleJournal as p, claimHook as r, requireInstance as s, ComponentInstance as t, journalMark as u, effectScheduler as v, Computation as w, isFlushing as x, flushSync as y };
|