@askrjs/askr 0.3.1 → 0.4.1

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.
Files changed (123) hide show
  1. package/CHANGELOG.md +898 -1
  2. package/README.md +61 -29
  3. package/capabilities.json +19 -9
  4. package/dist/actions/index.js +56 -12
  5. package/dist/benchmark.js +10 -15
  6. package/dist/boot/index.d.ts +3 -2
  7. package/dist/boot/index.js +1 -1
  8. package/dist/boot-C5HnQZio.js +4032 -0
  9. package/dist/browser-activity-2r5gA1Cb.js +222 -0
  10. package/dist/components/index.d.ts +8 -1
  11. package/dist/components/index.js +1 -43
  12. package/dist/{aria-hKjThHcp.js → compose-ref-ccRU59BM.js} +48 -5
  13. package/dist/control/index.d.ts +2 -0
  14. package/dist/control/index.js +1 -1
  15. package/dist/control-hr3B0QaD.js +211 -0
  16. package/dist/core.d.ts +5 -9
  17. package/dist/csp-nonce-D-XM_uuJ.js +35 -0
  18. package/dist/data/index.js +3 -3
  19. package/dist/{data-2edW0jhF.js → data-BsnOUpQ6.js} +294 -95
  20. package/dist/data-runtime-i-RD0Ffm.js +242 -0
  21. package/dist/{deferred-CbgNhf67.js → deferred-nGZglpj6.js} +42 -20
  22. package/dist/domains/actions.d.ts +8 -3
  23. package/dist/domains/component.d.ts +1 -107
  24. package/dist/domains/context.d.ts +0 -15
  25. package/dist/domains/control.d.ts +36 -249
  26. package/dist/domains/data.d.ts +31 -13
  27. package/dist/domains/diagnostics.d.ts +1 -2
  28. package/dist/domains/lifecycle.d.ts +2 -5
  29. package/dist/domains/refs.d.ts +0 -2
  30. package/dist/domains/routing.d.ts +43 -8
  31. package/dist/domains/scheduler.d.ts +1 -76
  32. package/dist/domains/server.d.ts +5 -2
  33. package/dist/domains/state.d.ts +2 -5
  34. package/dist/domains/telemetry.d.ts +0 -2
  35. package/dist/elements.d.ts +214 -51
  36. package/dist/error-boundary-ChXoIFVl.js +293 -0
  37. package/dist/foundations/icon/index.d.ts +0 -58
  38. package/dist/foundations/icon/index.js +2 -2
  39. package/dist/foundations/index.js +2 -1
  40. package/dist/foundations/interactions/index.js +1 -1
  41. package/dist/foundations/state/index.d.ts +3 -3
  42. package/dist/foundations/state/index.js +5 -3
  43. package/dist/foundations/structures/index.js +2 -1
  44. package/dist/foundations/utilities/index.d.ts +2 -0
  45. package/dist/foundations/utilities/index.js +1 -2
  46. package/dist/fx/index.d.ts +31 -58
  47. package/dist/fx/index.js +313 -195
  48. package/dist/hooks-kjLigwDJ.js +202 -0
  49. package/dist/index.d.ts +11 -74
  50. package/dist/index.js +8 -447
  51. package/dist/instance-DL-YKrj6.js +992 -0
  52. package/dist/{interactions-CTeACdT_.js → interactions-BAa2xuJ-.js} +3 -2
  53. package/dist/jsx-dev-runtime.d.ts +41 -18
  54. package/dist/jsx-dev-runtime.js +1 -6
  55. package/dist/{jsx-runtime-CPJXAOBF.js → jsx-runtime-C4idqHY0.js} +18 -7
  56. package/dist/jsx-runtime.js +1 -1
  57. package/dist/jsx.d.ts +61 -37
  58. package/dist/{manifest-D1P3UNWV.js → manifest-BNupb86f.js} +3 -3
  59. package/dist/{navigate-prC5V8XV.js → navigate-DsfO12hB.js} +468 -85
  60. package/dist/navigation.d.ts +12 -3
  61. package/dist/peer-types.d.ts +8 -0
  62. package/dist/portal-LYf4cbHd.js +349 -0
  63. package/dist/promise-Dz8iIHAm.js +16 -0
  64. package/dist/query-registry-tzSiVrV_.js +325 -0
  65. package/dist/readable-DFWOsTac.js +21 -0
  66. package/dist/{render-context-B7ohlh6t.js → render-context-DGHnEveC.js} +27 -11
  67. package/dist/render-sync-CUxvoVab.js +1256 -0
  68. package/dist/resolution-CF1BqHLm.js +1258 -0
  69. package/dist/resource-BhcaFqdB.js +377 -0
  70. package/dist/resources/index.d.ts +21 -11
  71. package/dist/resources/index.js +65 -266
  72. package/dist/router/index.d.ts +14 -5
  73. package/dist/router/index.js +22 -53
  74. package/dist/scope-ila_zaId.js +55 -0
  75. package/dist/{shared-kU8yvryp.js → shared-O2ZnmYAf.js} +1 -7
  76. package/dist/{snapshot-source-An4iKajG.js → snapshot-Cjx4N988.js} +6 -1
  77. package/dist/ssg/index.d.ts +1 -2
  78. package/dist/ssg/index.js +43 -22
  79. package/dist/ssr/index.d.ts +26 -28
  80. package/dist/ssr/index.js +4 -4
  81. package/dist/{ssr-CiNm6Vzl.js → ssr-9LOx_bEu.js} +133 -122
  82. package/dist/ssr-DPfdcq0e.js +229 -0
  83. package/dist/state-i9D4EtY6.js +250 -0
  84. package/dist/structures-CkehlOT3.js +231 -0
  85. package/dist/testing/index.d.ts +8 -4
  86. package/dist/testing/index.js +29 -34
  87. package/dist/url-C9xMpMMv.js +185 -0
  88. package/dist/utilities.d.ts +48 -2
  89. package/dist/verify-hydration-D_cyn1OX.js +152 -0
  90. package/package.json +19 -9
  91. package/dist/access-CEA2pOry.js +0 -1046
  92. package/dist/activity-oy2YFTFF.js +0 -201
  93. package/dist/boot-DLplW9h4.js +0 -1104
  94. package/dist/capabilities-Bbf0mlp9.js +0 -42
  95. package/dist/compose-ref-v0qGlHoN.js +0 -26
  96. package/dist/control-BR_mrX__.js +0 -173
  97. package/dist/control-C-ApnFs1.js +0 -20
  98. package/dist/csp-nonce-BfFWcd0w.js +0 -173
  99. package/dist/data-runtime-CxoY98lX.js +0 -149
  100. package/dist/default-portal-runtime-DvMvWJVp.js +0 -20
  101. package/dist/dom-renderer.d.ts +0 -77
  102. package/dist/domains/renderer.d.ts +0 -127
  103. package/dist/eager-control.d.ts +0 -8
  104. package/dist/effect-C3Zsc1k6.js +0 -292
  105. package/dist/for-state-BDykeOJL.js +0 -1753
  106. package/dist/instance-VAFYVJOa.js +0 -1027
  107. package/dist/notify-DxxuDPeY.js +0 -266
  108. package/dist/portal-w3oQ6oQU.js +0 -29
  109. package/dist/query-registry-LrYpQsDJ.js +0 -83
  110. package/dist/render-resolved-BC0W7_5b.js +0 -1123
  111. package/dist/renderer-CT0HeY58.js +0 -8623
  112. package/dist/resolution-3DskUkjG.js +0 -577
  113. package/dist/resource-18-sFRl9.js +0 -344
  114. package/dist/route-matching-CUzZ31gT.js +0 -551
  115. package/dist/router-internal-7nuNEHfG.js +0 -36
  116. package/dist/scope-Cq_iYbTR.js +0 -403
  117. package/dist/selector-BSPVkjKP.js +0 -311
  118. package/dist/ssr-EPjwJH5R.js +0 -29
  119. package/dist/state-BXJfudq1.js +0 -91
  120. package/dist/structures-CjEzfj4h.js +0 -654
  121. package/dist/timer-C7uqnJVt.js +0 -85
  122. package/dist/verify-hydration-ikaxotf7.js +0 -27
  123. package/dist/vnode-D24p6IW2.js +0 -7
@@ -0,0 +1,4032 @@
1
+ import { C as schedule, I as logger, L as isDevelopmentEnvironment, M as getOwner, N as runWithOwner, P as throwCollected, R as isProductionEnvironment, S as queueTask, _ as batch, c as setRenderHost, d as recordUndo, f as rewindJournal, g as outsideRendering, i as getContextEpoch, j as Owner, l as createRenderJournal, m as withRenderJournal, n as HookOrderChangeError, p as settleJournal, t as ComponentInstance, u as journalMark, v as effectScheduler, w as Computation, y as flushSync } from "./instance-DL-YKrj6.js";
2
+ import { n as readValue } from "./readable-DFWOsTac.js";
3
+ import { n as Fragment, t as ELEMENT_TYPE } from "./jsx-DRS-qjiN.js";
4
+ import { _ as RenderDepthError, b as reportUncaughtErrorLater, g as routeError, h as noteErrorOrigin, i as currentComponent, l as provideAppRuntime, m as deliverToBoundary, p as clarifyRenderError, v as clarifyRenderOverflow, y as reportUncaughtError } from "./hooks-kjLigwDJ.js";
5
+ import { r as validateCspNonce, t as CspNonceScope } from "./csp-nonce-D-XM_uuJ.js";
6
+ import { b as isPageRenderEnvelope, d as startHydrationRenderPhase, p as stopHydrationRenderPhase, x as pageRenderEnvelope } from "./render-context-DGHnEveC.js";
7
+ import { a as descriptorType, o as functionChildOutput, s as normalizeChildren } from "./error-boundary-ChXoIFVl.js";
8
+ import { D as clearStagedAppRenderRouteLocation, E as configureRootUpdateHost, O as createAppRenderRuntime, S as unregisterAppInstance, T as setServerLocation, _ as registerAppInstance, c as reconcileRouteMeta, d as configureScrollRestoration, k as stageAppRenderRouteLocation, l as resolveRouteMeta, o as readHistoryIndex, s as loadDocument, t as initializeNavigation } from "./navigate-DsfO12hB.js";
9
+ import { t as assertSyncComponentResult } from "./promise-Dz8iIHAm.js";
10
+ import { a as hydrateDataRuntime, m as reviveDeferredValue } from "./query-registry-tzSiVrV_.js";
11
+ import { A as clearRouteState, D as _setActiveRouteAuthOptions, Y as lockRouteRegistration, a as resolveRouteRequest, gt as withoutRouteHydrationMetadata, i as hasRouteRenderData, m as withoutHydratedAuth, n as getRouteRenderContext, p as readHydratedAuth, q as hasRegisteredRoutes, r as getRouteRenderData } from "./resolution-CF1BqHLm.js";
12
+ import { c as isSSRPortalHydrationAnchor, i as provideDefaultPortal, l as isSSRPortalWriterAnchor, t as DefaultPortal } from "./portal-LYf4cbHd.js";
13
+ import { r as assertExecutionModel, t as _applyManifest } from "./manifest-BNupb86f.js";
14
+ import { a as getDefaultDataRuntime } from "./data-runtime-i-RD0Ffm.js";
15
+ import { c as resolveNavigationUrl, d as isCustomElementName, l as attributeNamespace, n as UNSAFE_URL_SCHEME_ATTRIBUTES, s as rejectUnsafeUrlAttribute, t as SCRIPT_URL_RESOURCE_ATTRIBUTES, u as getPublicAttributeName } from "./url-C9xMpMMv.js";
16
+ import { a as PROPERTY_PROP_PREFIX, c as isKnownBooleanProperty, d as booleanAttributeValue, f as isSkippedProp, h as styleValueText, i as ATTRIBUTE_PROP_PREFIX, m as normalizeStylePropertyName, o as domPropertyValue, p as keepsFalseValue, r as sanitizeCssValue, s as getDomPropertyName, u as propertyReflectsAttribute } from "./ssr-DPfdcq0e.js";
17
+ //#region src/boot/root-handler.ts
18
+ const AUTO_PORTAL = {
19
+ $$typeof: ELEMENT_TYPE,
20
+ type: DefaultPortal,
21
+ props: { __askrAutoDefaultPortal: true },
22
+ key: "__default_portal"
23
+ };
24
+ /**
25
+ * Wrap a root's component: its output is followed by the automatic default
26
+ * portal host, and the CSP nonce (if any) is provided to everything below.
27
+ */
28
+ function wrapRootRouteHandler(componentFn, cspNonce) {
29
+ const wrappedFn = (props, ctx) => {
30
+ const out = componentFn(props, ctx);
31
+ assertSyncComponentResult(out);
32
+ const root = {
33
+ $$typeof: ELEMENT_TYPE,
34
+ type: Fragment,
35
+ props: { children: out === void 0 || out === null ? [AUTO_PORTAL] : [out, AUTO_PORTAL] }
36
+ };
37
+ return cspNonce === void 0 ? root : CspNonceScope({
38
+ value: cspNonce,
39
+ children: root
40
+ });
41
+ };
42
+ Object.defineProperty(wrappedFn, "name", { value: componentFn.name || "Component" });
43
+ return wrappedFn;
44
+ }
45
+ //#endregion
46
+ //#region src/boot/root-update.ts
47
+ /**
48
+ * Route updates for application roots.
49
+ *
50
+ * `apply()` renders the destination (render phase only); `publish()` commits
51
+ * it; `rollback()` discards it and restores the root's previous route state.
52
+ * A navigation applies every root first and publishes only if all succeeded.
53
+ * The destination stays staged through lifecycle-driven rerenders until
54
+ * `complete()` releases it after the navigation settles browser history.
55
+ */
56
+ function prepare(root, input) {
57
+ const app = root;
58
+ const current = app.appRuntime;
59
+ const runtime = createAppRenderRuntime({
60
+ framework: withoutRouteHydrationMetadata(current?.framework),
61
+ dataRuntime: current?.dataRuntime,
62
+ routeRegistry: current?.routeRegistry,
63
+ routeAuth: current?.routeAuth,
64
+ route: input.routeData,
65
+ hasRoute: input.hasRouteData,
66
+ lifetime: current?.lifetime
67
+ });
68
+ const previous = {
69
+ appRuntime: app.appRuntime,
70
+ component: app.component,
71
+ handler: app.handler,
72
+ generation: app.generation
73
+ };
74
+ let prepared = null;
75
+ let settled = false;
76
+ const restore = () => {
77
+ app.appRuntime = previous.appRuntime;
78
+ app.component = previous.component;
79
+ app.handler = previous.handler;
80
+ app.generation = previous.generation;
81
+ };
82
+ return {
83
+ apply() {
84
+ if (prepared || settled) return;
85
+ stageAppRenderRouteLocation(runtime, input.href, input.locationState);
86
+ app.appRuntime = runtime;
87
+ if (input.replaceLifetime) {
88
+ app.component = input.handler;
89
+ app.handler = wrapRootRouteHandler(input.handler, app.cspNonce);
90
+ app.generation++;
91
+ runtime.lifetime = app.generation;
92
+ }
93
+ try {
94
+ prepared = app.root.prepare(app.view());
95
+ prepared.apply();
96
+ } catch (error) {
97
+ restore();
98
+ clearStagedAppRenderRouteLocation(runtime);
99
+ throw error;
100
+ }
101
+ },
102
+ publish() {
103
+ if (settled) return {
104
+ aborted: false,
105
+ errors: []
106
+ };
107
+ settled = true;
108
+ try {
109
+ prepared?.publish();
110
+ return {
111
+ aborted: false,
112
+ errors: []
113
+ };
114
+ } catch (error) {
115
+ const aborted = prepared?.aborted === true;
116
+ if (aborted) restore();
117
+ return {
118
+ aborted,
119
+ errors: [error]
120
+ };
121
+ }
122
+ },
123
+ rollback() {
124
+ if (settled) return [];
125
+ settled = true;
126
+ clearStagedAppRenderRouteLocation(runtime);
127
+ const errors = prepared?.rollback() ?? [];
128
+ restore();
129
+ return errors;
130
+ },
131
+ retire() {
132
+ return [];
133
+ },
134
+ complete() {
135
+ clearStagedAppRenderRouteLocation(runtime);
136
+ }
137
+ };
138
+ }
139
+ function installRootUpdateHost() {
140
+ configureRootUpdateHost({ prepare });
141
+ }
142
+ //#endregion
143
+ //#region src/boot/composition.ts
144
+ let composed = false;
145
+ /**
146
+ * Browser composition root, owned by the boot entry: connects route updates
147
+ * from the router to application roots.
148
+ */
149
+ function composeBrowserRuntime() {
150
+ if (composed) return;
151
+ composed = true;
152
+ installRootUpdateHost();
153
+ }
154
+ /** Compose whatever is still missing, for a mount reached without the boot entry. */
155
+ function ensureBrowserRuntime() {
156
+ composeBrowserRuntime();
157
+ }
158
+ //#endregion
159
+ //#region src/core/dom/pass.ts
160
+ /**
161
+ * A render pass.
162
+ *
163
+ * The render phase runs components, diffs their output, and builds new DOM
164
+ * off-document. Anything that would change live DOM or committed renderer
165
+ * state is recorded as an operation instead. `commit()` applies the
166
+ * operations in order; `discard()` drops them, disposes every owner the pass
167
+ * created, and rewinds the render journal (props and scope values set during
168
+ * render). A failed structural DOM write restores child lists that this pass
169
+ * already applied, then discards its provisional owners and subscriptions.
170
+ *
171
+ * Operations are recorded parent-first: a reconcile reserves its slot before
172
+ * its children record theirs, so a parent places its children before the
173
+ * children update their own contents.
174
+ */
175
+ /** A structural DOM write failed before its render pass could publish. */
176
+ var CommitMutationError = class extends Error {
177
+ constructor(failure) {
178
+ super("DOM commit failed");
179
+ this.failure = failure;
180
+ }
181
+ };
182
+ var Pass = class {
183
+ constructor() {
184
+ this.hasDormantPortalWriter = false;
185
+ this.commitAborted = false;
186
+ this.ops = [];
187
+ this.created = [];
188
+ this.renderedInstances = [];
189
+ this.afterCommit = [];
190
+ this.beforeSettle = [];
191
+ this.commitUndo = [];
192
+ this.commitSettle = [];
193
+ this.journal = createRenderJournal();
194
+ this.phase = "prepared";
195
+ this.failures = [];
196
+ }
197
+ /** Run render work with this pass's provisional undo journal active. */
198
+ run(render) {
199
+ return withRenderJournal(this.journal, render);
200
+ }
201
+ /** Record an operation on committed state. */
202
+ op(fn) {
203
+ this.ops.push(fn);
204
+ }
205
+ /** Reserve an operation slot to fill after nested work records its own. */
206
+ reserve() {
207
+ this.ops.push(null);
208
+ return this.ops.length - 1;
209
+ }
210
+ fill(slot, fn) {
211
+ this.ops[slot] = fn;
212
+ }
213
+ /** An owner this pass created; disposed if the pass is discarded. */
214
+ own(owner) {
215
+ this.created.push(owner);
216
+ }
217
+ /** An instance rendered by this pass; it commits (and mounts) with it. */
218
+ markRendered(instance) {
219
+ this.renderedInstances.push(instance);
220
+ }
221
+ /** Undo a provisional render-time change if this work is discarded. */
222
+ onDiscard(fn) {
223
+ recordUndo(fn, this.journal);
224
+ }
225
+ /** Run after all operations are applied (refs). */
226
+ after(fn) {
227
+ this.afterCommit.push(fn);
228
+ }
229
+ /** Run after DOM operations while the pass can still be rolled back. */
230
+ beforeJournalSettle(fn) {
231
+ this.beforeSettle.push(fn);
232
+ }
233
+ /** Restore a reversible write on abort; settle its old lifetime on success. */
234
+ onReversibleCommit(undo, settle = () => {}) {
235
+ this.commitUndo.push(undo);
236
+ this.commitSettle.push(settle);
237
+ }
238
+ mark() {
239
+ return {
240
+ ops: this.ops.length,
241
+ created: this.created.length,
242
+ rendered: this.renderedInstances.length,
243
+ journal: journalMark(this.journal),
244
+ after: this.afterCommit.length,
245
+ beforeSettle: this.beforeSettle.length,
246
+ commitUndo: this.commitUndo.length,
247
+ commitSettle: this.commitSettle.length,
248
+ dormantPortalWriter: this.hasDormantPortalWriter
249
+ };
250
+ }
251
+ /** Discard everything recorded since `mark` (an error boundary caught). */
252
+ rewind(mark) {
253
+ const errors = [];
254
+ this.ops.length = mark.ops;
255
+ this.afterCommit.length = mark.after;
256
+ this.renderedInstances.length = mark.rendered;
257
+ this.hasDormantPortalWriter = mark.dormantPortalWriter;
258
+ this.beforeSettle.length = mark.beforeSettle;
259
+ this.commitUndo.length = mark.commitUndo;
260
+ this.commitSettle.length = mark.commitSettle;
261
+ rewindJournal(this.journal, mark.journal, errors);
262
+ for (const owner of this.created.splice(mark.created).reverse()) owner.dispose(errors);
263
+ return errors;
264
+ }
265
+ discard() {
266
+ if (this.phase === "applied") return this.rollback();
267
+ if (this.phase === "published" || this.phase === "discarded") return [];
268
+ this.phase = "discarded";
269
+ return this.rewind({
270
+ ops: 0,
271
+ created: 0,
272
+ rendered: 0,
273
+ journal: 0,
274
+ after: 0,
275
+ beforeSettle: 0,
276
+ commitUndo: 0,
277
+ commitSettle: 0,
278
+ dormantPortalWriter: false
279
+ });
280
+ }
281
+ /** Apply reversible operations while retaining undo work for rollback. */
282
+ apply() {
283
+ if (this.phase === "applied") return;
284
+ if (this.phase !== "prepared") throw new Error("[Askr] Cannot apply a settled render pass.");
285
+ const abort = (failure) => {
286
+ this.commitAborted = true;
287
+ for (const error of this.rollback()) reportUncaughtErrorLater(error);
288
+ throw failure;
289
+ };
290
+ this.phase = "applied";
291
+ for (const op of this.ops) {
292
+ if (!op) continue;
293
+ try {
294
+ op();
295
+ } catch (error) {
296
+ if (error instanceof CommitMutationError) abort(error.failure);
297
+ this.failures.push(error);
298
+ }
299
+ }
300
+ for (const fn of this.beforeSettle) try {
301
+ fn();
302
+ } catch (error) {
303
+ if (error instanceof CommitMutationError) abort(error.failure);
304
+ this.failures.push(error);
305
+ }
306
+ }
307
+ /** Publish applied work and run irreversible cleanup, refs, and lifecycles. */
308
+ publish() {
309
+ if (this.phase === "prepared") this.apply();
310
+ if (this.phase !== "applied") throw new Error("[Askr] Cannot publish a render pass that was not applied.");
311
+ this.phase = "published";
312
+ settleJournal(this.journal, 0);
313
+ for (const settle of this.commitSettle) try {
314
+ settle();
315
+ } catch (error) {
316
+ this.failures.push(error);
317
+ }
318
+ for (const fn of this.afterCommit) try {
319
+ fn();
320
+ } catch (error) {
321
+ this.failures.push(error);
322
+ }
323
+ for (const instance of this.renderedInstances) if (!instance.disposed) try {
324
+ mount(instance);
325
+ } catch (error) {
326
+ this.failures.push(error);
327
+ }
328
+ if (this.failures.length === 1) throw this.failures[0];
329
+ if (this.failures.length > 1) throw new AggregateError(this.failures, "Commit failed");
330
+ }
331
+ /** Undo applied operations and discard every provisional render mutation. */
332
+ rollback() {
333
+ if (this.phase === "published" || this.phase === "discarded") return [];
334
+ const errors = [];
335
+ if (this.phase === "applied") for (let index = this.commitUndo.length - 1; index >= 0; index--) try {
336
+ this.commitUndo[index]();
337
+ } catch (error) {
338
+ errors.push(error);
339
+ }
340
+ this.phase = "prepared";
341
+ errors.push(...this.discard());
342
+ return errors;
343
+ }
344
+ /** Apply and publish immediately for a standalone root render. */
345
+ commit() {
346
+ this.apply();
347
+ this.publish();
348
+ }
349
+ };
350
+ /** Mark an instance committed and schedule its post-commit work. */
351
+ function mount(instance) {
352
+ instance.mounted = true;
353
+ const syncQueue = instance.commitSyncQueue;
354
+ instance.commitSyncQueue = null;
355
+ for (const fn of syncQueue ?? []) fn();
356
+ const queue = instance.commitQueue;
357
+ if (!queue) return;
358
+ instance.commitQueue = null;
359
+ queueTask(() => {
360
+ if (instance.disposed) return;
361
+ for (const fn of queue) try {
362
+ const cleanup = fn();
363
+ if (typeof cleanup === "function") instance.onCleanup(cleanup);
364
+ } catch (error) {
365
+ reportUncaughtErrorLater(error);
366
+ }
367
+ });
368
+ }
369
+ //#endregion
370
+ //#region src/core/dom/element-attributes.ts
371
+ /**
372
+ * Low-level attribute access for rendered elements: the attribute name a prop
373
+ * renders as, namespaced writes, and class names on HTML and SVG elements.
374
+ */
375
+ const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
376
+ function isSVGDomElement(el) {
377
+ return typeof SVGElement !== "undefined" && el instanceof SVGElement;
378
+ }
379
+ function getRenderedAttributeName(el, propName) {
380
+ const attributeName = getPublicAttributeName(propName, isCustomElementName(el.localName));
381
+ return el.namespaceURI === SVG_NAMESPACE ? attributeName : attributeName.toLowerCase();
382
+ }
383
+ /**
384
+ * Write an attribute, placing `xlink:`/`xml:`/`xmlns:` names on SVG and MathML
385
+ * elements in their namespace.
386
+ */
387
+ function writeAttribute(el, attributeName, value, namespace = attributeNamespace(el.namespaceURI, attributeName)) {
388
+ if (namespace === null) el.setAttribute(attributeName, value);
389
+ else el.setAttributeNS(namespace, attributeName, value);
390
+ }
391
+ function setRenderedAttribute(el, propName, value) {
392
+ writeAttribute(el, getRenderedAttributeName(el, propName), value);
393
+ }
394
+ function removeRenderedAttribute(el, propName) {
395
+ el.removeAttribute(getRenderedAttributeName(el, propName));
396
+ }
397
+ function readElementClassName(el) {
398
+ if (isSVGDomElement(el)) return el.getAttribute("class") ?? "";
399
+ return el.className;
400
+ }
401
+ function writeElementClassName(el, value) {
402
+ if (isSVGDomElement(el)) {
403
+ if (value.length > 0) el.setAttribute("class", value);
404
+ else el.removeAttribute("class");
405
+ return;
406
+ }
407
+ el.className = value;
408
+ }
409
+ function tagNamesEqualIgnoreCase(a, b) {
410
+ if (a === b) return true;
411
+ if (a.length !== b.length) return false;
412
+ for (let index = 0; index < a.length; index += 1) {
413
+ const aCode = a.charCodeAt(index);
414
+ const bCode = b.charCodeAt(index);
415
+ if (aCode === bCode) continue;
416
+ if ((aCode >= 65 && aCode <= 90 ? aCode + 32 : aCode) !== (bCode >= 65 && bCode <= 90 ? bCode + 32 : bCode)) return false;
417
+ }
418
+ return true;
419
+ }
420
+ //#endregion
421
+ //#region src/core/dom/events.ts
422
+ /**
423
+ * Event handler props on rendered elements.
424
+ *
425
+ * Bubbling UI events (`click`, `input`, keyboard, mouse, touch) are
426
+ * delegated: each app root, plus `document.body` for content rendered outside
427
+ * any root (portals), listens once per event type. When an event reaches a
428
+ * container, its handlers along the event path are snapshotted and run
429
+ * target-first in one batch, so state writes flush once, synchronously, after
430
+ * the last handler, and a handler that removes an ancestor does not stop that
431
+ * ancestor's handler for the same event. Other events and capture handlers
432
+ * get one direct listener per element, run in their own batch.
433
+ *
434
+ * A handler's handler function can be swapped without re-registering. It runs
435
+ * with its component as the current owner, and a throwing handler is reported
436
+ * like an uncaught listener exception.
437
+ */
438
+ /** Root container visible to listeners through their component owner. */
439
+ const EVENT_ROOT_CONTAINER = Symbol("askr.event-root-container");
440
+ const DELEGATED_EVENTS = /* @__PURE__ */ new Set([
441
+ "click",
442
+ "dblclick",
443
+ "mousedown",
444
+ "mouseup",
445
+ "mouseover",
446
+ "mouseout",
447
+ "mousemove",
448
+ "touchend",
449
+ "touchcancel",
450
+ "keydown",
451
+ "keyup",
452
+ "keypress",
453
+ "input"
454
+ ]);
455
+ /** `onClick` -> click, `onKeyDownCapture` -> keydown (capture); else null. */
456
+ function parseEventProp(propName) {
457
+ if (propName.length <= 2 || !propName.startsWith("on")) return null;
458
+ const capture = propName.endsWith("Capture") && !propName.endsWith("PointerCapture") && propName.length > 9;
459
+ const name = capture ? propName.slice(0, -7) : propName;
460
+ if (name.length <= 2) return null;
461
+ return {
462
+ eventName: name.slice(2).toLowerCase(),
463
+ capture
464
+ };
465
+ }
466
+ /**
467
+ * `wheel`, `touchstart` and `touchmove` opt out of passive defaults so a
468
+ * handler can call `preventDefault()`; `scroll` is always passive.
469
+ */
470
+ function listenerOptions(eventName, capture) {
471
+ let options;
472
+ if (eventName === "scroll") options = { passive: true };
473
+ else if (eventName === "wheel" || eventName === "touchstart" || eventName === "touchmove") options = { passive: false };
474
+ if (!capture) return options;
475
+ return {
476
+ ...options,
477
+ capture: true
478
+ };
479
+ }
480
+ function invoke(listener, el, event) {
481
+ runWithOwner(listener.owner, () => {
482
+ try {
483
+ listener.handler.call(el, event);
484
+ } catch (error) {
485
+ reportUncaughtError(error);
486
+ }
487
+ });
488
+ }
489
+ /** Delegated handlers per element, by event type. */
490
+ const delegatedHandlers = /* @__PURE__ */ new WeakMap();
491
+ /** Elements holding a delegated handler, per event type. */
492
+ const delegatedUsage = /* @__PURE__ */ new Map();
493
+ /** Registered app roots, each counted once per live root using it. */
494
+ const roots = /* @__PURE__ */ new Map();
495
+ /** Container listeners, per container, by event type. */
496
+ const containerListeners = /* @__PURE__ */ new Map();
497
+ function fallbackContainer() {
498
+ return typeof document === "undefined" ? null : document.body;
499
+ }
500
+ function listen(container, eventName) {
501
+ let listeners = containerListeners.get(container);
502
+ if (listeners?.has(eventName)) return;
503
+ const listener = (event) => dispatch(event, eventName);
504
+ container.addEventListener(eventName, listener);
505
+ if (!listeners) containerListeners.set(container, listeners = /* @__PURE__ */ new Map());
506
+ listeners.set(eventName, listener);
507
+ }
508
+ function unlisten(container, eventName) {
509
+ const listeners = containerListeners.get(container);
510
+ const listener = listeners?.get(eventName);
511
+ if (!listener) return;
512
+ container.removeEventListener(eventName, listener);
513
+ listeners.delete(eventName);
514
+ if (listeners.size === 0) containerListeners.delete(container);
515
+ }
516
+ function containers() {
517
+ const all = [...roots.keys()];
518
+ const fallback = fallbackContainer();
519
+ if (fallback && !roots.has(fallback)) all.push(fallback);
520
+ return all;
521
+ }
522
+ /** Delegate events rendered under `root` at `root`, until released. */
523
+ function registerEventRoot(root) {
524
+ roots.set(root, (roots.get(root) ?? 0) + 1);
525
+ for (const eventName of delegatedUsage.keys()) listen(root, eventName);
526
+ let released = false;
527
+ return () => {
528
+ if (released) return;
529
+ released = true;
530
+ const count = roots.get(root) - 1;
531
+ if (count > 0) {
532
+ roots.set(root, count);
533
+ return;
534
+ }
535
+ roots.delete(root);
536
+ if (root === fallbackContainer()) return;
537
+ for (const eventName of delegatedUsage.keys()) unlisten(root, eventName);
538
+ };
539
+ }
540
+ function isShadowRoot(node) {
541
+ return node.nodeType === 11 && node.host != null;
542
+ }
543
+ /**
544
+ * Index of `container` in `composed`, or -1 when the composed path cannot be
545
+ * used: it lacks the container, or it skips an ancestor of the (retargeted)
546
+ * target below the container, as some browser hosts do.
547
+ */
548
+ function composedPathEnd(composed, target, container) {
549
+ const end = composed.indexOf(container);
550
+ if (end === -1) return -1;
551
+ let expected = target;
552
+ for (let i = 0; i < end && expected; i++) if (composed[i] === expected) expected = expected.parentNode;
553
+ return expected === container ? end : -1;
554
+ }
555
+ /**
556
+ * The target's parent chain up to `container`, supplemented with any
557
+ * composed path nodes it misses, for hosts whose composed path is unusable.
558
+ */
559
+ function ancestryPath(event, container) {
560
+ const path = [];
561
+ const seen = /* @__PURE__ */ new Set();
562
+ for (let node = event.target; node; node = node.parentNode) {
563
+ seen.add(node);
564
+ path.push(node);
565
+ if (node === container) return path;
566
+ }
567
+ for (const node of event.composedPath?.() ?? []) if (!seen.has(node)) {
568
+ seen.add(node);
569
+ path.push(node);
570
+ }
571
+ return path;
572
+ }
573
+ /** The event as seen by one element's handler. */
574
+ function eventFor(event, currentTarget, target) {
575
+ return new Proxy(event, { get(native, property) {
576
+ if (property === "currentTarget") return currentTarget;
577
+ if (property === "target") return target;
578
+ const value = Reflect.get(native, property, native);
579
+ return typeof value === "function" ? value.bind(native) : value;
580
+ } });
581
+ }
582
+ function dispatch(event, eventName) {
583
+ const container = event.currentTarget;
584
+ const composed = event.composedPath?.() ?? [];
585
+ const end = composedPathEnd(composed, event.target, container);
586
+ const path = end === -1 ? ancestryPath(event, container) : composed;
587
+ const last = end === -1 ? path.length : end;
588
+ const retarget = end > 0 && composed[0] !== event.target;
589
+ let target = retarget ? composed[0] : event.target;
590
+ let depth = 0;
591
+ let targetDepth = 0;
592
+ const handlers = [];
593
+ for (let i = 0; i < last; i++) {
594
+ const node = path[i];
595
+ if (node === container) break;
596
+ if (retarget) {
597
+ if (isShadowRoot(node)) {
598
+ if (--depth < targetDepth) {
599
+ target = node.host;
600
+ targetDepth = depth;
601
+ }
602
+ } else if (i > 0 && path[i - 1].assignedSlot === node) depth++;
603
+ }
604
+ if (node.nodeType !== 1) continue;
605
+ if (containerListeners.get(node)?.has(eventName)) handlers.length = 0;
606
+ const listener = delegatedHandlers.get(node)?.get(eventName);
607
+ if (listener) handlers.push({
608
+ el: node,
609
+ listener,
610
+ target
611
+ });
612
+ }
613
+ if (handlers.length === 0) return;
614
+ outsideRendering(() => batch(() => {
615
+ for (const { el, listener, target } of handlers) {
616
+ invoke(listener, el, eventFor(event, el, target));
617
+ if (event.cancelBubble) break;
618
+ }
619
+ }));
620
+ }
621
+ function delegate(el, listener) {
622
+ let byEvent = delegatedHandlers.get(el);
623
+ if (!byEvent) delegatedHandlers.set(el, byEvent = /* @__PURE__ */ new Map());
624
+ byEvent.set(listener.eventName, listener);
625
+ const usage = delegatedUsage.get(listener.eventName) ?? 0;
626
+ delegatedUsage.set(listener.eventName, usage + 1);
627
+ if (usage === 0) for (const container of containers()) listen(container, listener.eventName);
628
+ }
629
+ function undelegate(el, eventName) {
630
+ const byEvent = delegatedHandlers.get(el);
631
+ if (!byEvent?.delete(eventName)) return;
632
+ if (byEvent.size === 0) delegatedHandlers.delete(el);
633
+ const usage = delegatedUsage.get(eventName) - 1;
634
+ if (usage > 0) {
635
+ delegatedUsage.set(eventName, usage);
636
+ return;
637
+ }
638
+ delegatedUsage.delete(eventName);
639
+ for (const container of containerListeners.keys()) unlisten(container, eventName);
640
+ }
641
+ function setListener(listeners, el, propName, event, handler, owner) {
642
+ const existing = listeners.get(propName);
643
+ if (existing) {
644
+ existing.handler = handler;
645
+ return;
646
+ }
647
+ if (!event.capture && DELEGATED_EVENTS.has(event.eventName) && (roots.size > 0 || fallbackContainer())) {
648
+ const listener = {
649
+ handler,
650
+ owner,
651
+ eventName: event.eventName,
652
+ wrapped: null,
653
+ options: void 0
654
+ };
655
+ listeners.set(propName, listener);
656
+ delegate(el, listener);
657
+ return;
658
+ }
659
+ const options = listenerOptions(event.eventName, event.capture);
660
+ const listener = {
661
+ handler,
662
+ owner,
663
+ eventName: event.eventName,
664
+ options,
665
+ wrapped: (nativeEvent) => {
666
+ outsideRendering(() => batch(() => invoke(listener, el, nativeEvent)));
667
+ }
668
+ };
669
+ listeners.set(propName, listener);
670
+ el.addEventListener(event.eventName, listener.wrapped, options);
671
+ }
672
+ function removeListener(listeners, el, propName) {
673
+ const listener = listeners.get(propName);
674
+ if (!listener) return;
675
+ listeners.delete(propName);
676
+ if (!listener.wrapped) {
677
+ undelegate(el, listener.eventName);
678
+ return;
679
+ }
680
+ el.removeEventListener(listener.eventName, listener.wrapped, listener.options);
681
+ }
682
+ function removeAllListeners(listeners, el, errors) {
683
+ for (const propName of Array.from(listeners.keys())) try {
684
+ removeListener(listeners, el, propName);
685
+ } catch (error) {
686
+ errors.push(error);
687
+ }
688
+ }
689
+ //#endregion
690
+ //#region src/core/dom/hydration.ts
691
+ /**
692
+ * Hydration: rendering over server markup with a cursor.
693
+ *
694
+ * While a root's first render hydrates, each container element has a cursor
695
+ * at its next unclaimed child. Creating a text or element node first tries to
696
+ * claim the node under the cursor: an element with the same tag, or a text
697
+ * node (split when the server merged adjacent texts). A node that does not
698
+ * match is created fresh instead. When a container's children are rendered,
699
+ * one commit operation puts the claimed and created nodes in order and
700
+ * removes whatever the server rendered that the client did not.
701
+ */
702
+ var HydrationCursor = class HydrationCursor {
703
+ constructor(stopAt = null, successors = /* @__PURE__ */ new WeakMap()) {
704
+ this.stopAt = stopAt;
705
+ this.successors = successors;
706
+ this.next = /* @__PURE__ */ new Map();
707
+ this.deferred = [];
708
+ }
709
+ /** Detached text suffixes retain the server sibling that follows them. */
710
+ followingSibling(node) {
711
+ return this.successors.has(node) ? this.successors.get(node) : node.nextSibling;
712
+ }
713
+ /** Consume private SSR metadata emitted immediately before a component output. */
714
+ claimResourceSlots(container) {
715
+ let node = this.next.has(container) ? this.next.get(container) : container.firstChild;
716
+ for (; node && node !== this.stopAt; node = this.followingSibling(node)) {
717
+ if (node.nodeType === 3 && isInsignificantWhitespace(node)) continue;
718
+ if (node.nodeType !== 8) return null;
719
+ const match = /^askr-resource:(r:\d+(?:,r:\d+)*)$/.exec(node.data);
720
+ if (match) {
721
+ this.advance(container, node);
722
+ return match[1].split(",");
723
+ }
724
+ if (isRangeMarker(node, "askr-range-start") || isRangeMarker(node, "askr-range-end")) return null;
725
+ }
726
+ return null;
727
+ }
728
+ /** Server nodes reserved for a portal host whose writer has not hydrated. */
729
+ heldNodes(container) {
730
+ if (!this.stopAt) return [];
731
+ const nodes = [];
732
+ let node = this.next.has(container) ? this.next.get(container) : container.firstChild;
733
+ while (node && node !== this.stopAt) {
734
+ if (node.nodeType !== 8) nodes.push(node);
735
+ node = this.followingSibling(node);
736
+ }
737
+ return nodes;
738
+ }
739
+ /**
740
+ * Reserve the server nodes at `container`'s cursor for a render that runs
741
+ * later. A server range (`askr-range-start` ... `askr-range-end`) is skipped
742
+ * as a whole; the returned cursor claims inside it, or from the reserved
743
+ * position onward when there is no range.
744
+ */
745
+ reserve(container) {
746
+ let node = this.next.has(container) ? this.next.get(container) : container.firstChild;
747
+ while (node && node !== this.stopAt && node.nodeType === 3 && isInsignificantWhitespace(node)) node = this.followingSibling(node);
748
+ if (node && isRangeMarker(node, "askr-range-start")) {
749
+ let depth = 0;
750
+ let end = node;
751
+ for (; end && end !== this.stopAt; end = this.followingSibling(end)) if (isRangeMarker(end, "askr-range-start")) depth++;
752
+ else if (isRangeMarker(end, "askr-range-end") && --depth === 0) break;
753
+ if (end && end !== this.stopAt) {
754
+ this.next.set(container, this.followingSibling(end));
755
+ const inner = new HydrationCursor(end, this.successors);
756
+ inner.next.set(container, this.followingSibling(node));
757
+ return inner;
758
+ }
759
+ }
760
+ const later = new HydrationCursor(this.stopAt, this.successors);
761
+ later.next.set(container, node);
762
+ return later;
763
+ }
764
+ peek(container) {
765
+ let node = this.next.has(container) ? this.next.get(container) : container.firstChild;
766
+ while (node && node !== this.stopAt && (node.nodeType === 8 || node.nodeType === 3 && isInsignificantWhitespace(node))) node = this.followingSibling(node);
767
+ return node === this.stopAt ? null : node;
768
+ }
769
+ advance(container, node) {
770
+ this.next.set(container, this.followingSibling(node));
771
+ }
772
+ claimElement(container, tag, namespace) {
773
+ const node = this.peek(container);
774
+ if (!node || node.nodeType !== 1) return null;
775
+ const el = node;
776
+ const expectedNs = namespace ?? "http://www.w3.org/1999/xhtml";
777
+ if (!(expectedNs === "http://www.w3.org/1999/xhtml" ? el.localName === tag.toLowerCase() : el.localName === tag)) return null;
778
+ if ((el.namespaceURI ?? expectedNs) !== expectedNs) return null;
779
+ this.advance(container, el);
780
+ return el;
781
+ }
782
+ /**
783
+ * Claim a text node for `text`. A server text that begins with `text` is
784
+ * split so the remainder stays claimable by the next text; any other
785
+ * server text is claimed and corrected at commit.
786
+ */
787
+ claimText(container, text) {
788
+ const node = this.peek(container);
789
+ if (!node || node.nodeType !== 3) return null;
790
+ const textNode = node;
791
+ if (text.length > 0 && textNode.data.length > text.length && textNode.data.startsWith(text)) {
792
+ const remainder = document.createTextNode(textNode.data.slice(text.length));
793
+ this.successors.set(remainder, this.followingSibling(textNode));
794
+ this.next.set(container, remainder);
795
+ return textNode;
796
+ }
797
+ this.advance(container, textNode);
798
+ return textNode;
799
+ }
800
+ };
801
+ function isRangeMarker(node, data) {
802
+ return node.nodeType === 8 && node.data === data;
803
+ }
804
+ function isInsignificantWhitespace(node) {
805
+ return node.data.trim() === "" && node.data.includes("\n");
806
+ }
807
+ /**
808
+ * Put `container`'s content in the order `expected`, removing any server
809
+ * node that was not claimed. Nodes after `stopAt` are left alone.
810
+ */
811
+ function syncChildren(pass, container, expected, stopAt = null) {
812
+ const previous = Array.from(container.childNodes);
813
+ pass.onReversibleCommit(() => restoreChildren(container, previous));
814
+ try {
815
+ const skipAnchors = (node) => {
816
+ while (node && node !== stopAt && isSSRPortalHydrationAnchor(node)) node = node.nextSibling;
817
+ return node;
818
+ };
819
+ let cursor = skipAnchors(container.firstChild);
820
+ for (const node of expected) if (node === cursor) cursor = skipAnchors(cursor.nextSibling);
821
+ else container.insertBefore(node, cursor);
822
+ while (cursor && cursor !== stopAt) {
823
+ const next = cursor.nextSibling;
824
+ if (!isSSRPortalHydrationAnchor(cursor)) container.removeChild(cursor);
825
+ cursor = next;
826
+ }
827
+ } catch (error) {
828
+ throw new CommitMutationError(error);
829
+ }
830
+ }
831
+ function restoreChildren(container, previous) {
832
+ for (let index = 0; index < previous.length; index++) {
833
+ const current = container.childNodes[index] ?? null;
834
+ if (current !== previous[index]) container.insertBefore(previous[index], current);
835
+ }
836
+ while (container.childNodes.length > previous.length) container.removeChild(container.lastChild);
837
+ }
838
+ /** Attribute names `props` renders on `el`. */
839
+ function renderedAttributes(el, props) {
840
+ const names = /* @__PURE__ */ new Set();
841
+ if (el.localName === "option") {
842
+ const parent = el.parentElement;
843
+ const select = parent?.localName === "optgroup" ? parent.parentElement : parent;
844
+ if (select?.localName === "select" && select.hasAttribute("value")) names.add("selected");
845
+ }
846
+ for (const key in props) {
847
+ if (isSkippedProp(key) || parseEventProp(key)) continue;
848
+ const value = props[key];
849
+ if (typeof value !== "function") {
850
+ if (value === null || value === void 0) continue;
851
+ if (value === false && !keepsFalseValue(key)) continue;
852
+ }
853
+ if (key === "className") {
854
+ names.add("class");
855
+ continue;
856
+ }
857
+ const name = key.startsWith("attr:") ? key.slice(ATTRIBUTE_PROP_PREFIX.length) : key;
858
+ names.add(getRenderedAttributeName(el, name));
859
+ }
860
+ return names;
861
+ }
862
+ /**
863
+ * Server attributes the client does not render are removed from an adopted
864
+ * element, so a mismatched attribute does not survive hydration.
865
+ */
866
+ function removeUnrenderedAttributes(el, props, beforeRemove) {
867
+ const keep = renderedAttributes(el, props);
868
+ const removed = Array.from(el.attributes).filter((attribute) => !keep.has(attribute.name));
869
+ beforeRemove?.(removed);
870
+ for (const attribute of removed) el.removeAttribute(attribute.name);
871
+ }
872
+ //#endregion
873
+ //#region src/core/dom/dom-properties.ts
874
+ /**
875
+ * Per element, the props Askr wrote as DOM properties, mapped to the property
876
+ * name. A property cannot be "removed" like an attribute, so this is what lets
877
+ * an absent prop reset its property.
878
+ */
879
+ const writtenDomProperties = /* @__PURE__ */ new WeakMap();
880
+ /**
881
+ * Property names `prop:` never assigns: raw HTML sinks (use
882
+ * `dangerouslySetInnerHTML`) and names that would change the prototype chain.
883
+ */
884
+ const BLOCKED_PROPERTY_NAMES = /* @__PURE__ */ new Set([
885
+ "innerHTML",
886
+ "outerHTML",
887
+ "srcdoc",
888
+ "__proto__",
889
+ "constructor",
890
+ "prototype"
891
+ ]);
892
+ function isBlockedPropertyName(name) {
893
+ if (!BLOCKED_PROPERTY_NAMES.has(name)) return false;
894
+ if (isDevelopmentEnvironment()) logger.warn(`[Askr] ${PROPERTY_PROP_PREFIX}${name} is ignored.`);
895
+ return true;
896
+ }
897
+ function isUrlPropertyName(name) {
898
+ const lower = name.toLowerCase();
899
+ return UNSAFE_URL_SCHEME_ATTRIBUTES.has(lower) || SCRIPT_URL_RESOURCE_ATTRIBUTES.has(lower);
900
+ }
901
+ function writeProperty(el, name, value) {
902
+ const host = el;
903
+ if (Object.is(host[name], value)) return;
904
+ host[name] = value;
905
+ }
906
+ /** Capture property and ownership state before a pass writes a DOM property. */
907
+ function captureDomPropertyUndo(el, key, value, tagName) {
908
+ const name = getDomPropertyName(tagName, key, value);
909
+ const written = writtenDomProperties.get(el);
910
+ const previousName = written?.get(key);
911
+ if (name === null && previousName === void 0) return null;
912
+ const names = /* @__PURE__ */ new Set();
913
+ if (name !== null && !BLOCKED_PROPERTY_NAMES.has(name)) names.add(name);
914
+ if (previousName !== void 0) names.add(previousName);
915
+ const host = el;
916
+ const properties = Array.from(names, (property) => ({
917
+ property,
918
+ descriptor: Object.getOwnPropertyDescriptor(host, property),
919
+ value: host[property]
920
+ }));
921
+ const attributeNames = /* @__PURE__ */ new Set();
922
+ for (const property of names) attributeNames.add(getRenderedAttributeName(el, property));
923
+ if (!key.startsWith("prop:")) attributeNames.add(getRenderedAttributeName(el, key));
924
+ const attributes = Array.from(attributeNames, (attribute) => ({
925
+ attribute,
926
+ value: el.getAttribute(attribute)
927
+ }));
928
+ const hadWrittenMap = written !== void 0;
929
+ const writtenBefore = written ? new Map(written) : null;
930
+ return () => {
931
+ const failures = [];
932
+ for (const { property, descriptor, value: before } of properties) try {
933
+ if (descriptor && "value" in descriptor) {
934
+ Object.defineProperty(host, property, descriptor);
935
+ continue;
936
+ }
937
+ if (!Reflect.set(host, property, before)) throw new TypeError(`Could not restore DOM property ${property}`);
938
+ if (descriptor) Object.defineProperty(host, property, descriptor);
939
+ else delete host[property];
940
+ } catch (error) {
941
+ failures.push(error);
942
+ }
943
+ for (const { attribute, value: before } of attributes) try {
944
+ if (before === null) el.removeAttribute(attribute);
945
+ else el.setAttribute(attribute, before);
946
+ } catch (error) {
947
+ failures.push(error);
948
+ }
949
+ try {
950
+ if (hadWrittenMap) writtenDomProperties.set(el, new Map(writtenBefore));
951
+ else writtenDomProperties.delete(el);
952
+ } catch (error) {
953
+ failures.push(error);
954
+ }
955
+ if (failures.length === 1) throw failures[0];
956
+ if (failures.length > 1) throw new AggregateError(failures, "DOM property rollback failed");
957
+ };
958
+ }
959
+ function hasDomPropertyWrite(el, key, value, tagName) {
960
+ return getDomPropertyName(tagName, key, value) !== null || writtenDomProperties.get(el)?.has(key) === true;
961
+ }
962
+ /**
963
+ * Put a property back to its default once Askr no longer sets it.
964
+ *
965
+ * - `muted`/`indeterminate` become `false`.
966
+ * - A property with a same-named attribute (`href`, `title`, `hidden`,
967
+ * `className` -> `class`, ...) has that attribute removed, which restores
968
+ * the element's own default.
969
+ * - An own property (an expando, or a value set before a custom element
970
+ * upgraded) is deleted.
971
+ * - Otherwise strings become `''` and booleans `false`. Numbers keep their
972
+ * value (there is no safe generic default), and anything else becomes
973
+ * `undefined`.
974
+ */
975
+ function resetProperty(el, key, name) {
976
+ const host = el;
977
+ if (isKnownBooleanProperty(key)) {
978
+ writeProperty(el, name, false);
979
+ return;
980
+ }
981
+ const attributeName = getRenderedAttributeName(el, name);
982
+ if (el.hasAttribute(attributeName)) {
983
+ el.removeAttribute(attributeName);
984
+ return;
985
+ }
986
+ if (Object.prototype.hasOwnProperty.call(host, name)) {
987
+ delete host[name];
988
+ return;
989
+ }
990
+ const current = host[name];
991
+ if (typeof current === "number") return;
992
+ writeProperty(el, name, typeof current === "string" ? "" : typeof current === "boolean" ? false : void 0);
993
+ }
994
+ function forgetProperty(el, key) {
995
+ const written = writtenDomProperties.get(el);
996
+ const name = written?.get(key);
997
+ if (name === void 0) return;
998
+ written.delete(key);
999
+ resetProperty(el, key, name);
1000
+ }
1001
+ /**
1002
+ * Write a prop that targets a DOM property.
1003
+ *
1004
+ * Returns `true` when the prop is fully handled, and `false` when the caller
1005
+ * must still run the attribute path: for attribute props, and for `muted`,
1006
+ * which keeps its attribute alongside the property. A prop that was a
1007
+ * property and is now an attribute (a custom element prop that went from an
1008
+ * object to a string or `null`) has its property reset first.
1009
+ */
1010
+ function applyDomPropertyProp(el, key, value, tagName) {
1011
+ const name = getDomPropertyName(tagName, key, value);
1012
+ if (name === null) {
1013
+ if (isCustomElementName(tagName)) forgetProperty(el, key);
1014
+ return false;
1015
+ }
1016
+ if (isBlockedPropertyName(name)) return true;
1017
+ if (value === null || value === void 0) {
1018
+ forgetProperty(el, key);
1019
+ return true;
1020
+ }
1021
+ let next = domPropertyValue(key, value);
1022
+ if (next !== null && next !== void 0 && isUrlPropertyName(name)) {
1023
+ const text = String(next);
1024
+ if (rejectUnsafeUrlAttribute(name, text)) {
1025
+ forgetProperty(el, key);
1026
+ return true;
1027
+ }
1028
+ if (!isCustomElementName(tagName)) next = text;
1029
+ }
1030
+ let written = writtenDomProperties.get(el);
1031
+ if (!written) {
1032
+ written = /* @__PURE__ */ new Map();
1033
+ writtenDomProperties.set(el, written);
1034
+ }
1035
+ if (!written.has(key)) {
1036
+ written.set(key, name);
1037
+ if (!key.startsWith("prop:") && !isKnownBooleanProperty(key)) {
1038
+ const attributeName = getRenderedAttributeName(el, key);
1039
+ if (el.hasAttribute(attributeName)) el.removeAttribute(attributeName);
1040
+ }
1041
+ }
1042
+ writeProperty(el, name, next);
1043
+ return !propertyReflectsAttribute(key);
1044
+ }
1045
+ //#endregion
1046
+ //#region src/core/dom/prop-values.ts
1047
+ /**
1048
+ * How a scalar prop value is written to an element: class tokens, owned style
1049
+ * properties, form state, DOM properties, URL-guarded attributes, and the
1050
+ * `attr:`/`prop:` escape hatches. Shared semantics with SSR attribute
1051
+ * serialization; the renderer decides when these run.
1052
+ */
1053
+ /** Props whose live DOM property must be synced alongside the attribute. */
1054
+ function isFormControlProp(key) {
1055
+ return key === "value" || key === "checked" || key === "selected";
1056
+ }
1057
+ /** Attribute text for a scalar prop, rendering HTML booleans bare. */
1058
+ function renderedScalarValue(el, key, value) {
1059
+ return value === true ? booleanAttributeValue(getRenderedAttributeName(el, key)) : String(value);
1060
+ }
1061
+ /**
1062
+ * The text to write for a scalar attribute prop, or `null` when the renderer
1063
+ * never writes it: unsafe URLs, and inline event handler or object values
1064
+ * requested through the `attr:` escape hatch (SSR skips those too). The value
1065
+ * is converted once, and the checked text is the text written, so a
1066
+ * `toString()` cannot pass the URL check and then return something else.
1067
+ */
1068
+ function renderableAttributeText(el, key, value) {
1069
+ let name = key;
1070
+ if (key.startsWith("attr:")) {
1071
+ name = key.slice(ATTRIBUTE_PROP_PREFIX.length);
1072
+ if (value !== null && typeof value === "object" || name.slice(0, 2).toLowerCase() === "on") return null;
1073
+ }
1074
+ const text = renderedScalarValue(el, key, value);
1075
+ return rejectUnsafeUrlAttribute(name, text) ? null : text;
1076
+ }
1077
+ function isDangerousInnerHTMLPayload(value) {
1078
+ return value !== null && typeof value === "object" && "__html" in value;
1079
+ }
1080
+ function applyDangerousInnerHTMLValue(el, value) {
1081
+ if (!isDangerousInnerHTMLPayload(value)) return;
1082
+ if (isDevelopmentEnvironment()) logger.warn("[Askr] dangerouslySetInnerHTML is being used, which bypasses the framework's normal rendering and can introduce XSS vulnerabilities if the HTML is derived from untrusted input. Make sure the value is sanitized.");
1083
+ const html = value.__html;
1084
+ const nextHtml = html === null || html === void 0 ? "" : String(html);
1085
+ if (hasMatchingInnerHTML(el, nextHtml)) return;
1086
+ el.innerHTML = nextHtml;
1087
+ }
1088
+ /** Compare parsed HTML without replacing the live descendants. */
1089
+ function hasMatchingInnerHTML(el, html) {
1090
+ try {
1091
+ if (el.innerHTML === html) return true;
1092
+ if (el.childNodes.length === 0 || !el.namespaceURI) return false;
1093
+ const probe = el.ownerDocument.createElement("template").content.ownerDocument.createElementNS(el.namespaceURI, el.localName);
1094
+ probe.innerHTML = html;
1095
+ return probe.innerHTML === el.innerHTML;
1096
+ } catch {
1097
+ return false;
1098
+ }
1099
+ }
1100
+ function applyFormControlProp(el, key, value, tagName) {
1101
+ if (key === "value") {
1102
+ const stringValue = String(value);
1103
+ if (tagNamesEqualIgnoreCase(tagName, "select")) {
1104
+ const select = el;
1105
+ const selectedValues = new Set(select.multiple && Array.isArray(value) ? value.map(String) : [stringValue]);
1106
+ let firstMatch = -1;
1107
+ for (let index = 0; index < select.options.length; index++) {
1108
+ const option = select.options[index];
1109
+ const selected = selectedValues.has(option.value) && (select.multiple || firstMatch < 0);
1110
+ if (selected && firstMatch < 0) firstMatch = index;
1111
+ if (option.hasAttribute("selected") !== selected) {
1112
+ if (selected) option.setAttribute("selected", "");
1113
+ else option.removeAttribute("selected");
1114
+ }
1115
+ if (select.multiple && option.selected !== selected) option.selected = selected;
1116
+ }
1117
+ if (!select.multiple && select.selectedIndex !== firstMatch) select.selectedIndex = firstMatch;
1118
+ } else if (tagNamesEqualIgnoreCase(tagName, "input") || tagNamesEqualIgnoreCase(tagName, "textarea")) {
1119
+ const control = el;
1120
+ if (control.value !== stringValue) control.value = stringValue;
1121
+ }
1122
+ if (el.getAttribute("value") !== stringValue) el.setAttribute("value", stringValue);
1123
+ return;
1124
+ }
1125
+ if (key === "selected") {
1126
+ const selected = Boolean(value);
1127
+ if (tagNamesEqualIgnoreCase(tagName, "option")) {
1128
+ const option = el;
1129
+ if (option.selected !== selected) option.selected = selected;
1130
+ }
1131
+ if (selected) {
1132
+ if (!el.hasAttribute("selected")) el.setAttribute("selected", "");
1133
+ } else if (el.hasAttribute("selected")) el.removeAttribute("selected");
1134
+ return;
1135
+ }
1136
+ if (key === "checked") {
1137
+ if (tagNamesEqualIgnoreCase(tagName, "input")) {
1138
+ const checked = Boolean(value);
1139
+ const input = el;
1140
+ if (input.checked !== checked) input.checked = checked;
1141
+ if (checked) {
1142
+ if (!el.hasAttribute("checked")) el.setAttribute("checked", "");
1143
+ } else if (el.hasAttribute("checked")) el.removeAttribute("checked");
1144
+ } else if (value) {
1145
+ if (!el.hasAttribute("checked")) el.setAttribute("checked", "");
1146
+ } else if (el.hasAttribute("checked")) el.removeAttribute("checked");
1147
+ }
1148
+ }
1149
+ const IMPORTANT_SUFFIX = " !important";
1150
+ function readStyleEntry(style, propertyName) {
1151
+ return style.getPropertyValue(propertyName) + (style.getPropertyPriority(propertyName) ? IMPORTANT_SUFFIX : "");
1152
+ }
1153
+ function collectStyleEntries(style) {
1154
+ const entries = /* @__PURE__ */ new Map();
1155
+ for (let index = 0; index < style.length; index += 1) {
1156
+ const propertyName = style.item(index);
1157
+ entries.set(propertyName, readStyleEntry(style, propertyName));
1158
+ }
1159
+ return entries;
1160
+ }
1161
+ function normalizeStyleEntries(value) {
1162
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
1163
+ const entries = /* @__PURE__ */ new Map();
1164
+ for (const [key, entryValue] of Object.entries(value)) {
1165
+ if (entryValue === void 0 || entryValue === null || entryValue === false) continue;
1166
+ const propertyName = normalizeStylePropertyName(key);
1167
+ const safeValue = sanitizeCssValue(styleValueText(propertyName, entryValue));
1168
+ if (safeValue) entries.set(propertyName, safeValue);
1169
+ }
1170
+ return entries;
1171
+ }
1172
+ let scratchStyle = null;
1173
+ /** Style entries a prop value renders; `null` when it is not expressible. */
1174
+ function readStyleEntries(el, value) {
1175
+ if (value === null || value === void 0 || value === false) return /* @__PURE__ */ new Map();
1176
+ if (typeof value !== "string") return normalizeStyleEntries(value);
1177
+ scratchStyle ??= el.ownerDocument.createElement("div").style;
1178
+ scratchStyle.cssText = value;
1179
+ const entries = collectStyleEntries(scratchStyle);
1180
+ scratchStyle.cssText = "";
1181
+ return entries;
1182
+ }
1183
+ /**
1184
+ * Apply a `style` prop. `previousValue` is the value Askr last applied
1185
+ * (`null` when none): only the properties it owns are removed or rewritten, so
1186
+ * properties set by other code survive. When `previousValue` is `undefined`
1187
+ * the element's whole style is treated as Askr-owned.
1188
+ */
1189
+ function applyStylePropValue(el, value, previousValue) {
1190
+ const style = el.style;
1191
+ const isEmpty = value === null || value === void 0 || value === false;
1192
+ if (!style) {
1193
+ if (isEmpty) {
1194
+ el.removeAttribute("style");
1195
+ return;
1196
+ }
1197
+ el.setAttribute("style", String(value));
1198
+ return;
1199
+ }
1200
+ if ((el.getAttribute("style") ?? "") === (isEmpty ? "" : value) || isEmpty && previousValue === null) return;
1201
+ if (previousValue === void 0 && (isEmpty || typeof value === "string")) {
1202
+ style.cssText = isEmpty ? "" : value;
1203
+ if (isEmpty) el.removeAttribute("style");
1204
+ return;
1205
+ }
1206
+ const nextEntries = readStyleEntries(el, value);
1207
+ if (!nextEntries) {
1208
+ style.cssText = String(value);
1209
+ return;
1210
+ }
1211
+ const previousEntries = (previousValue === void 0 ? null : previousValue === value ? nextEntries : readStyleEntries(el, previousValue)) ?? collectStyleEntries(style);
1212
+ let didWrite = false;
1213
+ for (const [propertyName] of previousEntries) {
1214
+ if (nextEntries.has(propertyName) || !style.getPropertyValue(propertyName)) continue;
1215
+ style.removeProperty(propertyName);
1216
+ didWrite = true;
1217
+ }
1218
+ for (const [propertyName, propertyValue] of nextEntries) {
1219
+ if (readStyleEntry(style, propertyName) === propertyValue) continue;
1220
+ const important = propertyValue.endsWith(IMPORTANT_SUFFIX);
1221
+ style.setProperty(propertyName, important ? propertyValue.slice(0, -11) : propertyValue, important ? "important" : "");
1222
+ didWrite = true;
1223
+ }
1224
+ if (isEmpty && style.length === 0) {
1225
+ el.removeAttribute("style");
1226
+ didWrite = true;
1227
+ }
1228
+ if (!didWrite) {}
1229
+ }
1230
+ function applyStaticScalarPropsToElement(el, props, tagName) {
1231
+ for (const key in props) {
1232
+ if (isSkippedProp(key)) continue;
1233
+ const value = props[key];
1234
+ if (applyDomPropertyProp(el, key, value, tagName)) continue;
1235
+ if (value === void 0 || value === null || value === false && !keepsFalseValue(key)) continue;
1236
+ if (key === "class" || key === "className") writeElementClassName(el, String(value));
1237
+ else if (key === "style") applyStylePropValue(el, value);
1238
+ else if (isFormControlProp(key)) applyFormControlProp(el, key, value, tagName);
1239
+ else if (key === "dangerouslySetInnerHTML") applyDangerousInnerHTMLValue(el, value);
1240
+ else {
1241
+ const text = renderableAttributeText(el, key, value);
1242
+ if (text === null) removeRenderedAttribute(el, key);
1243
+ else setRenderedAttribute(el, key, text);
1244
+ }
1245
+ }
1246
+ }
1247
+ const EMPTY_CLASS_TOKENS = [];
1248
+ /** Class tokens Askr last applied; `null` means unknown. */
1249
+ function previousClassTokens(previousValue) {
1250
+ return previousValue === null || previousValue === false ? EMPTY_CLASS_TOKENS : tokenizeClassValue(previousValue);
1251
+ }
1252
+ function tokenizeClassValue(value) {
1253
+ if (typeof value !== "string") return null;
1254
+ const trimmed = value.trim();
1255
+ if (trimmed.length === 0) return [];
1256
+ return trimmed.split(/\s+/);
1257
+ }
1258
+ function patchClassList(el, previousTokens, nextTokens) {
1259
+ if (previousTokens.length === nextTokens.length) {
1260
+ let identical = true;
1261
+ for (let index = 0; index < previousTokens.length; index += 1) if (previousTokens[index] !== nextTokens[index]) {
1262
+ identical = false;
1263
+ break;
1264
+ }
1265
+ if (identical) {
1266
+ for (const token of nextTokens) if (!el.classList.contains(token)) el.classList.add(token);
1267
+ return;
1268
+ }
1269
+ }
1270
+ if (previousTokens.length === 0) {
1271
+ if (nextTokens.length === 0) return;
1272
+ el.classList.add(...nextTokens);
1273
+ return;
1274
+ }
1275
+ if (nextTokens.length === 0) {
1276
+ el.classList.remove(...previousTokens);
1277
+ return;
1278
+ }
1279
+ if (previousTokens.length === 1 && nextTokens.length === 1) {
1280
+ el.classList.remove(previousTokens[0]);
1281
+ el.classList.add(nextTokens[0]);
1282
+ return;
1283
+ }
1284
+ const nextSet = new Set(nextTokens);
1285
+ const previousSet = new Set(previousTokens);
1286
+ for (const token of previousTokens) if (!nextSet.has(token)) el.classList.remove(token);
1287
+ for (const token of nextTokens) if (!previousSet.has(token)) el.classList.add(token);
1288
+ }
1289
+ function applyClassPropValue(el, value, previousValue, descriptor) {
1290
+ const nextString = String(value);
1291
+ if (!descriptor && nextString.length > 0 && readElementClassName(el) === nextString) return;
1292
+ const nextTokens = tokenizeClassValue(nextString);
1293
+ const previousTokens = descriptor?.lastClassTokens ?? previousClassTokens(previousValue);
1294
+ if (nextTokens && previousTokens) {
1295
+ if (Object.is(value, previousValue)) {
1296
+ for (const token of nextTokens) if (!el.classList.contains(token)) el.classList.add(token);
1297
+ return;
1298
+ }
1299
+ patchClassList(el, previousTokens, nextTokens);
1300
+ dropEmptySvgClass(el);
1301
+ if (descriptor) descriptor.lastClassTokens = nextTokens;
1302
+ return;
1303
+ }
1304
+ writeElementClassName(el, nextString);
1305
+ if (descriptor) descriptor.lastClassTokens = nextTokens;
1306
+ }
1307
+ /** An SVG element renders no `class` attribute for an empty class list. */
1308
+ function dropEmptySvgClass(el) {
1309
+ if (isSVGDomElement(el) && el.getAttribute("class") === "") el.removeAttribute("class");
1310
+ }
1311
+ function applyScalarPropValue(el, key, value, tagName, previousValue, descriptor) {
1312
+ if (key === "dangerouslySetInnerHTML" && !isDangerousInnerHTMLPayload(value)) return;
1313
+ if (applyDomPropertyProp(el, key, value, tagName)) return;
1314
+ if (value === void 0 || value === null || value === false && !keepsFalseValue(key)) {
1315
+ if (key === "class" || key === "className") {
1316
+ const previousTokens = descriptor ? descriptor.lastClassTokens : previousClassTokens(previousValue);
1317
+ if (previousTokens === null) writeElementClassName(el, "");
1318
+ else if (previousTokens.length > 0) {
1319
+ el.classList.remove(...previousTokens);
1320
+ dropEmptySvgClass(el);
1321
+ }
1322
+ if (descriptor) descriptor.lastClassTokens = [];
1323
+ } else if (key === "value") applyFormControlProp(el, key, "", tagName);
1324
+ else if (key === "checked" || key === "selected") applyFormControlProp(el, key, false, tagName);
1325
+ else if (key === "style") applyStylePropValue(el, null, previousValue);
1326
+ else removeRenderedAttribute(el, key);
1327
+ return;
1328
+ }
1329
+ if (key === "class" || key === "className") applyClassPropValue(el, value, previousValue, descriptor);
1330
+ else if (key === "style") applyStylePropValue(el, value, previousValue);
1331
+ else if (isFormControlProp(key)) applyFormControlProp(el, key, value, tagName);
1332
+ else if (key === "dangerouslySetInnerHTML") applyDangerousInnerHTMLValue(el, value);
1333
+ else {
1334
+ const nextValue = renderableAttributeText(el, key, value);
1335
+ if (nextValue === null) {
1336
+ removeRenderedAttribute(el, key);
1337
+ return;
1338
+ }
1339
+ const attributeName = getRenderedAttributeName(el, key);
1340
+ if (el.getAttribute(attributeName) === nextValue) return;
1341
+ writeAttribute(el, attributeName, nextValue);
1342
+ }
1343
+ }
1344
+ //#endregion
1345
+ //#region src/core/dom/refs.ts
1346
+ /**
1347
+ * Refs: a callback receives the element (then null on removal); an object
1348
+ * ref's `current` is assigned. A read-only object ref is ignored.
1349
+ */
1350
+ function setRef(ref, value) {
1351
+ if (!ref) return;
1352
+ if (typeof ref === "function") {
1353
+ ref(value);
1354
+ return;
1355
+ }
1356
+ try {
1357
+ ref.current = value;
1358
+ } catch {}
1359
+ }
1360
+ //#endregion
1361
+ //#region src/core/dom/tree.ts
1362
+ const HOST = 0;
1363
+ const DYNAMIC = 4;
1364
+ /** Parents whose children are DOM children of one element. */
1365
+ function isContainer(parent) {
1366
+ return parent.kind === HOST || parent.kind === 5;
1367
+ }
1368
+ function containerElement(parent) {
1369
+ return parent.el;
1370
+ }
1371
+ /** The element that directly contains `parent`'s DOM. */
1372
+ function containerOf(parent) {
1373
+ for (let p = parent; p; p = p.parent) if (isContainer(p)) return containerElement(p);
1374
+ throw new Error("[Askr] Rendered node is not attached to a container.");
1375
+ }
1376
+ function firstDom(node) {
1377
+ const pending = [node];
1378
+ while (pending.length) {
1379
+ const current = pending.pop();
1380
+ switch (current.kind) {
1381
+ case HOST: return current.el;
1382
+ case 1:
1383
+ case 7: return current.node;
1384
+ default: for (let i = current.children.length - 1; i >= 0; i--) pending.push(current.children[i]);
1385
+ }
1386
+ }
1387
+ return null;
1388
+ }
1389
+ function collectDom(node, out = []) {
1390
+ const pending = [node];
1391
+ while (pending.length) {
1392
+ const current = pending.pop();
1393
+ switch (current.kind) {
1394
+ case HOST:
1395
+ out.push(current.el);
1396
+ break;
1397
+ case 1:
1398
+ case 7:
1399
+ out.push(current.node);
1400
+ break;
1401
+ default: for (let i = current.children.length - 1; i >= 0; i--) pending.push(current.children[i]);
1402
+ }
1403
+ }
1404
+ return out;
1405
+ }
1406
+ /**
1407
+ * The DOM node that content at the end of `parent` must be inserted before:
1408
+ * null for an element's children, otherwise the first DOM after `parent`.
1409
+ */
1410
+ function endOf(parent) {
1411
+ if (parent.kind === 5) return parent.tail?.parentNode === parent.el ? parent.tail : null;
1412
+ if (isContainer(parent)) return null;
1413
+ return nextDomAfter(parent);
1414
+ }
1415
+ /** The first DOM node after `node`'s own DOM within its container. */
1416
+ function nextDomAfter(node) {
1417
+ let current = node;
1418
+ for (let parent = current.parent; parent; parent = current.parent) {
1419
+ const siblings = parent.children;
1420
+ for (let i = siblings.indexOf(current) + 1; i < siblings.length; i++) {
1421
+ const dom = firstDom(siblings[i]);
1422
+ if (dom) return dom;
1423
+ }
1424
+ if (parent.kind === 5) return parent.tail?.parentNode === parent.el ? parent.tail : null;
1425
+ if (isContainer(parent)) return null;
1426
+ current = parent;
1427
+ }
1428
+ return null;
1429
+ }
1430
+ //#endregion
1431
+ //#region src/core/dom/teardown.ts
1432
+ /**
1433
+ * Failures while tearing down removed content are reported after the update,
1434
+ * never thrown into it: the DOM change has already happened and is not
1435
+ * rolled back.
1436
+ */
1437
+ function reportTeardown(errors) {
1438
+ if (errors.length === 1) reportUncaughtErrorLater(errors[0]);
1439
+ else if (errors.length > 1) reportUncaughtErrorLater(new AggregateError(errors, "Cleanup failed while removing content"));
1440
+ }
1441
+ //#endregion
1442
+ //#region src/core/dom/props.ts
1443
+ function isBinding(key, value) {
1444
+ return typeof value === "function" && key !== "ref" && key !== "dangerouslySetInnerHTML";
1445
+ }
1446
+ /** A `<select>` value can only select options that already exist. */
1447
+ function followsChildren(tag, key) {
1448
+ return key === "value" && tag === "select";
1449
+ }
1450
+ function isSimpleAttribute(tag, key, value) {
1451
+ return !isCustomElementName(tag) && !key.includes(":") && getDomPropertyName(tag, key, value) === null && key !== "class" && key !== "className" && key !== "style" && key !== "value" && key !== "checked" && key !== "selected" && key !== "dangerouslySetInnerHTML";
1452
+ }
1453
+ function isInputValue(tag, key) {
1454
+ return key === "value" && (tag === "input" || tag === "textarea");
1455
+ }
1456
+ function isBooleanControl(tag, key) {
1457
+ return tag === "input" && key === "checked" || tag === "option" && key === "selected";
1458
+ }
1459
+ /** Record the live attribute before applying a reversible scalar write. */
1460
+ function writeSimpleAttribute(pass, node, key, value, previous, beforeApply) {
1461
+ const { el, tag } = node;
1462
+ const name = getRenderedAttributeName(el, key);
1463
+ pass.op(() => {
1464
+ recordAttributeUndo(pass, el, name);
1465
+ try {
1466
+ beforeApply?.();
1467
+ applyScalarPropValue(el, key, value, tag, previous);
1468
+ } catch (error) {
1469
+ throw new CommitMutationError(error);
1470
+ }
1471
+ });
1472
+ }
1473
+ /** Record a reflected class or style write for an abortable commit. */
1474
+ function writeReflectedProp(pass, node, key, apply, beforeApply) {
1475
+ const { el } = node;
1476
+ const name = getRenderedAttributeName(el, key);
1477
+ pass.op(() => {
1478
+ recordAttributeUndo(pass, el, name);
1479
+ try {
1480
+ beforeApply?.();
1481
+ apply();
1482
+ } catch (error) {
1483
+ throw new CommitMutationError(error);
1484
+ }
1485
+ });
1486
+ }
1487
+ function recordAttributeUndo(pass, el, name) {
1488
+ const namespace = attributeNamespace(el.namespaceURI, name);
1489
+ const colon = name.indexOf(":");
1490
+ const localName = colon === -1 ? name : name.slice(colon + 1);
1491
+ const before = namespace ? el.getAttributeNS(namespace, localName) : el.getAttribute(name);
1492
+ pass.onReversibleCommit(() => {
1493
+ if (before === null) {
1494
+ if (namespace) el.removeAttributeNS(namespace, localName);
1495
+ else el.removeAttribute(name);
1496
+ } else if (namespace) el.setAttributeNS(namespace, name, before);
1497
+ else el.setAttribute(name, before);
1498
+ });
1499
+ }
1500
+ /** Restore exact child identities without needlessly disconnecting unchanged nodes. */
1501
+ function restoreElementChildren(el, children) {
1502
+ if (el.childNodes.length === children.length) {
1503
+ let unchanged = true;
1504
+ for (let index = 0; index < children.length; index += 1) if (el.childNodes[index] !== children[index]) {
1505
+ unchanged = false;
1506
+ break;
1507
+ }
1508
+ if (unchanged) return;
1509
+ }
1510
+ el.replaceChildren(...children);
1511
+ }
1512
+ /** Make property-backed scalar writes part of the pass rollback journal. */
1513
+ function writePropertyScalar(pass, node, key, value, apply) {
1514
+ pass.op(() => {
1515
+ try {
1516
+ const undo = captureDomPropertyUndo(node.el, key, value, node.tag);
1517
+ if (undo) pass.onReversibleCommit(undo);
1518
+ apply();
1519
+ } catch (error) {
1520
+ throw new CommitMutationError(error);
1521
+ }
1522
+ });
1523
+ }
1524
+ function queueScalarProp(pass, node, key, value, apply) {
1525
+ if (key === "dangerouslySetInnerHTML") {
1526
+ if (!isDangerousInnerHTMLPayload(value)) {
1527
+ pass.op(apply);
1528
+ return;
1529
+ }
1530
+ pass.op(() => {
1531
+ const children = Array.from(node.el.childNodes);
1532
+ pass.onReversibleCommit(() => restoreElementChildren(node.el, children));
1533
+ try {
1534
+ apply();
1535
+ } catch (error) {
1536
+ if (error instanceof CommitMutationError) throw error;
1537
+ throw new CommitMutationError(error);
1538
+ }
1539
+ });
1540
+ return;
1541
+ }
1542
+ if (hasDomPropertyWrite(node.el, key, value, node.tag)) writePropertyScalar(pass, node, key, value, apply);
1543
+ else pass.op(() => {
1544
+ recordAttributeUndo(pass, node.el, getRenderedAttributeName(node.el, key));
1545
+ try {
1546
+ apply();
1547
+ } catch (error) {
1548
+ if (error instanceof CommitMutationError) throw error;
1549
+ throw new CommitMutationError(error);
1550
+ }
1551
+ });
1552
+ }
1553
+ /** Remove a committed binding provisionally and restore it if the pass aborts. */
1554
+ function retireBindingForCommit(pass, node, key) {
1555
+ const binding = node.bindings?.get(key);
1556
+ if (!binding) return void 0;
1557
+ node.bindings.delete(key);
1558
+ pass.onReversibleCommit(() => {
1559
+ (node.bindings ??= /* @__PURE__ */ new Map()).set(key, binding);
1560
+ }, () => reportTeardown(binding.dispose()));
1561
+ return binding;
1562
+ }
1563
+ /** Restore or remove a binding-map entry when a provisional install aborts. */
1564
+ function recordBindingInstallUndo(pass, node, key, binding, previous) {
1565
+ pass.onReversibleCommit(() => {
1566
+ if (node.bindings?.get(key) !== binding) return;
1567
+ if (previous) node.bindings.set(key, previous);
1568
+ else {
1569
+ node.bindings.delete(key);
1570
+ if (node.bindings.size === 0) node.bindings = null;
1571
+ }
1572
+ });
1573
+ }
1574
+ /** Restore both the reflected attribute and the live form value on abort. */
1575
+ function writeInputValue(pass, node, value, previous) {
1576
+ const { el, tag } = node;
1577
+ pass.op(() => {
1578
+ recordInputValueUndo(pass, node);
1579
+ try {
1580
+ applyScalarPropValue(el, "value", value, tag, previous);
1581
+ } catch (error) {
1582
+ throw new CommitMutationError(error);
1583
+ }
1584
+ });
1585
+ }
1586
+ /** Restore a checkbox or option's live state and reflected attribute. */
1587
+ function writeBooleanControl(pass, node, key, value, previous) {
1588
+ const { el, tag } = node;
1589
+ pass.op(() => {
1590
+ recordBooleanControlUndo(pass, node, key);
1591
+ try {
1592
+ applyScalarPropValue(el, key, value, tag, previous);
1593
+ } catch (error) {
1594
+ throw new CommitMutationError(error);
1595
+ }
1596
+ });
1597
+ }
1598
+ function recordInputValueUndo(pass, node) {
1599
+ const control = node.el;
1600
+ const beforeValue = control.value;
1601
+ const beforeAttribute = node.el.getAttribute("value");
1602
+ pass.onReversibleCommit(() => {
1603
+ if (beforeAttribute === null) node.el.removeAttribute("value");
1604
+ else node.el.setAttribute("value", beforeAttribute);
1605
+ control.value = beforeValue;
1606
+ });
1607
+ }
1608
+ function recordBooleanControlUndo(pass, node, key) {
1609
+ const control = node.el;
1610
+ const beforeValue = control[key];
1611
+ const beforeAttribute = node.el.getAttribute(key);
1612
+ pass.onReversibleCommit(() => {
1613
+ if (beforeAttribute === null) node.el.removeAttribute(key);
1614
+ else node.el.setAttribute(key, beforeAttribute);
1615
+ control[key] = beforeValue;
1616
+ });
1617
+ }
1618
+ /** The last value each binding applied, for diffing its next write. */
1619
+ const appliedByBinding = /* @__PURE__ */ new WeakMap();
1620
+ function lastApplied(binding) {
1621
+ return binding ? appliedByBinding.get(binding) : void 0;
1622
+ }
1623
+ function setHandler(node, key, value) {
1624
+ const event = parseEventProp(key);
1625
+ if (typeof value === "function") setListener(node.listeners ??= /* @__PURE__ */ new Map(), node.el, key, event, value, node.owner);
1626
+ else if (node.listeners) removeListener(node.listeners, node.el, key);
1627
+ }
1628
+ function setHandlerForCommit(pass, node, key, value, previous) {
1629
+ pass.op(() => {
1630
+ pass.onReversibleCommit(() => setHandler(node, key, previous));
1631
+ try {
1632
+ setHandler(node, key, value);
1633
+ } catch (error) {
1634
+ throw new CommitMutationError(error);
1635
+ }
1636
+ });
1637
+ }
1638
+ /**
1639
+ * Write props to an element that has no committed props yet: a new, detached
1640
+ * element (written now) or an adopted server element (`adopted`: recorded as
1641
+ * commit operations).
1642
+ */
1643
+ function applyInitialProps(pass, node, adopted = false) {
1644
+ const props = node.props;
1645
+ let scalars = null;
1646
+ const flushScalars = () => {
1647
+ if (!scalars) return;
1648
+ const batch = scalars;
1649
+ scalars = null;
1650
+ if (adopted) for (const key in batch) {
1651
+ const value = batch[key];
1652
+ if (isInputValue(node.tag, key)) writeInputValue(pass, node, value, void 0);
1653
+ else if (isBooleanControl(node.tag, key)) writeBooleanControl(pass, node, key, value, void 0);
1654
+ else if (isSimpleAttribute(node.tag, key, value)) writeSimpleAttribute(pass, node, key, value, void 0);
1655
+ else if (key === "class" || key === "className" || key === "style") writeReflectedProp(pass, node, key, () => applyStaticScalarPropsToElement(node.el, { [key]: value }, node.tag));
1656
+ else queueScalarProp(pass, node, key, value, () => applyStaticScalarPropsToElement(node.el, { [key]: value }, node.tag));
1657
+ }
1658
+ else applyStaticScalarPropsToElement(node.el, batch, node.tag);
1659
+ };
1660
+ for (const key in props) {
1661
+ if (isSkippedProp(key)) continue;
1662
+ const value = props[key];
1663
+ if (parseEventProp(key)) {
1664
+ flushScalars();
1665
+ if (adopted) setHandlerForCommit(pass, node, key, value, void 0);
1666
+ else {
1667
+ setHandler(node, key, value);
1668
+ recordUndo(() => setHandler(node, key, void 0));
1669
+ }
1670
+ } else if (isBinding(key, value)) {
1671
+ flushScalars();
1672
+ if (!followsChildren(node.tag, key)) bind(pass, node, key, value, void 0, !adopted);
1673
+ } else if (!followsChildren(node.tag, key)) (scalars ??= {})[key] = value;
1674
+ }
1675
+ flushScalars();
1676
+ }
1677
+ /** Write props that must follow the element's children. */
1678
+ function captureSelectUndo(select) {
1679
+ const beforeValue = select.getAttribute("value");
1680
+ const beforeIndex = select.selectedIndex;
1681
+ const beforeOptions = Array.from(select.options, (option) => ({
1682
+ option,
1683
+ selected: option.selected,
1684
+ attribute: option.hasAttribute("selected")
1685
+ }));
1686
+ return () => {
1687
+ if (beforeValue === null) select.removeAttribute("value");
1688
+ else select.setAttribute("value", beforeValue);
1689
+ for (const { option, attribute } of beforeOptions) if (attribute) option.setAttribute("selected", "");
1690
+ else option.removeAttribute("selected");
1691
+ if (select.multiple) for (const { option, selected } of beforeOptions) option.selected = selected;
1692
+ else select.selectedIndex = beforeIndex;
1693
+ };
1694
+ }
1695
+ function recordSelectUndo(pass, node) {
1696
+ pass.onReversibleCommit(captureSelectUndo(node.el));
1697
+ }
1698
+ function applyTrailingProps(pass, node, props, initial = false, adopted = false) {
1699
+ if (node.tag !== "select" || !("value" in props)) return;
1700
+ if (initial && adopted) pass.op(() => recordSelectUndo(pass, node));
1701
+ if (typeof props.value === "function") {
1702
+ if (initial) bind(pass, node, "value", props.value, void 0, !adopted);
1703
+ else node.bindings?.get("value")?.run();
1704
+ return;
1705
+ }
1706
+ const writeValue = () => {
1707
+ try {
1708
+ applyScalarPropValue(node.el, "value", props.value, node.tag, void 0);
1709
+ } catch (error) {
1710
+ if (initial && !adopted) throw error;
1711
+ throw new CommitMutationError(error);
1712
+ }
1713
+ };
1714
+ if (initial && adopted) pass.op(writeValue);
1715
+ else writeValue();
1716
+ }
1717
+ /** The select whose options include content under `parent`, if any. */
1718
+ function enclosingSelect(parent) {
1719
+ for (let p = parent; p; p = p.parent) {
1720
+ if (p.kind !== HOST) {
1721
+ if (p.kind === 5) return null;
1722
+ continue;
1723
+ }
1724
+ if (p.tag === "select") return p;
1725
+ if (p.tag !== "optgroup" && p.tag !== "option") return null;
1726
+ }
1727
+ return null;
1728
+ }
1729
+ /**
1730
+ * Re-apply a committed select's controlled value against its current
1731
+ * options. Its options changed without the select itself being patched.
1732
+ */
1733
+ function resyncSelect(select) {
1734
+ if (select.dormant || !("value" in select.props)) return;
1735
+ const value = select.props.value;
1736
+ const resolved = typeof value === "function" ? readValue(value) : value;
1737
+ const undo = captureSelectUndo(select.el);
1738
+ try {
1739
+ applyScalarPropValue(select.el, "value", resolved, select.tag, void 0);
1740
+ } catch (error) {
1741
+ try {
1742
+ undo();
1743
+ } catch (rollbackError) {
1744
+ throw new AggregateError([error, rollbackError], "Controlled select resynchronization and rollback failed");
1745
+ }
1746
+ throw error;
1747
+ }
1748
+ }
1749
+ /** Record the writes that turn `previous` into `next` on a committed element. */
1750
+ function patchProps(pass, node, previous, next) {
1751
+ const { el, tag } = node;
1752
+ for (const key in previous) {
1753
+ if (key in next || isSkippedProp(key)) continue;
1754
+ if (followsChildren(tag, key)) pass.op(() => recordSelectUndo(pass, node));
1755
+ const old = previous[key];
1756
+ if (parseEventProp(key)) setHandlerForCommit(pass, node, key, void 0, old);
1757
+ else if (!isBinding(key, old) && isInputValue(tag, key)) writeInputValue(pass, node, void 0, old);
1758
+ else if (!isBinding(key, old) && isBooleanControl(tag, key)) writeBooleanControl(pass, node, key, void 0, old);
1759
+ else if (!isBinding(key, old) && isSimpleAttribute(tag, key, void 0)) writeSimpleAttribute(pass, node, key, void 0, old);
1760
+ else if (!isBinding(key, old) && (key === "class" || key === "className" || key === "style")) writeReflectedProp(pass, node, key, () => applyScalarPropValue(el, key, void 0, tag, old));
1761
+ else if (isBinding(key, old) && isSimpleAttribute(tag, key, void 0)) {
1762
+ const binding = node.bindings?.get(key);
1763
+ writeSimpleAttribute(pass, node, key, void 0, lastApplied(binding), () => retireBindingForCommit(pass, node, key));
1764
+ } else if (isBinding(key, old) && (key === "class" || key === "className" || key === "style")) {
1765
+ const binding = node.bindings?.get(key);
1766
+ writeReflectedProp(pass, node, key, () => applyScalarPropValue(el, key, void 0, tag, lastApplied(binding)), () => retireBindingForCommit(pass, node, key));
1767
+ } else queueScalarProp(pass, node, key, void 0, () => {
1768
+ const binding = retireBindingForCommit(pass, node, key);
1769
+ const from = isBinding(key, old) ? lastApplied(binding) : old;
1770
+ try {
1771
+ applyScalarPropValue(el, key, void 0, tag, from);
1772
+ } catch (error) {
1773
+ if (!followsChildren(tag, key)) throw error;
1774
+ throw new CommitMutationError(error);
1775
+ }
1776
+ });
1777
+ }
1778
+ for (const key in next) {
1779
+ if (isSkippedProp(key)) continue;
1780
+ if (followsChildren(tag, key)) pass.op(() => recordSelectUndo(pass, node));
1781
+ const value = next[key];
1782
+ const old = previous[key];
1783
+ if (parseEventProp(key)) {
1784
+ if (value !== old) setHandlerForCommit(pass, node, key, value, old);
1785
+ continue;
1786
+ }
1787
+ if (isBinding(key, value)) {
1788
+ if (value === old && node.bindings?.has(key)) continue;
1789
+ const replaced = node.bindings?.get(key);
1790
+ const from = replaced ? lastApplied(replaced) : old;
1791
+ pass.op(() => retireBindingForCommit(pass, node, key));
1792
+ bind(pass, node, key, value, from, false);
1793
+ continue;
1794
+ }
1795
+ if (isBinding(key, old)) {
1796
+ const binding = node.bindings?.get(key);
1797
+ const from = lastApplied(binding);
1798
+ if (isSimpleAttribute(tag, key, value)) writeSimpleAttribute(pass, node, key, value, from, () => retireBindingForCommit(pass, node, key));
1799
+ else if (key === "class" || key === "className" || key === "style") writeReflectedProp(pass, node, key, () => applyScalarPropValue(el, key, value, tag, from), () => retireBindingForCommit(pass, node, key));
1800
+ else queueScalarProp(pass, node, key, value, () => {
1801
+ const current = retireBindingForCommit(pass, node, key);
1802
+ applyScalarPropValue(el, key, value, tag, lastApplied(current));
1803
+ });
1804
+ continue;
1805
+ }
1806
+ if (Object.is(value, old) && key === "dangerouslySetInnerHTML") continue;
1807
+ if (isInputValue(tag, key)) writeInputValue(pass, node, value, old);
1808
+ else if (isBooleanControl(tag, key)) writeBooleanControl(pass, node, key, value, old);
1809
+ else if (isSimpleAttribute(tag, key, value)) writeSimpleAttribute(pass, node, key, value, old);
1810
+ else if (key === "class" || key === "className" || key === "style") writeReflectedProp(pass, node, key, () => applyScalarPropValue(el, key, value, tag, old));
1811
+ else queueScalarProp(pass, node, key, value, () => {
1812
+ try {
1813
+ applyScalarPropValue(el, key, value, tag, old);
1814
+ } catch (error) {
1815
+ if (!followsChildren(tag, key)) throw error;
1816
+ throw new CommitMutationError(error);
1817
+ }
1818
+ });
1819
+ }
1820
+ }
1821
+ function bind(pass, node, key, read, previousValue, fresh) {
1822
+ const { el, tag } = node;
1823
+ let last = previousValue;
1824
+ let installing = true;
1825
+ let propertyBacked = false;
1826
+ const binding = new Computation(node.owner, () => {
1827
+ try {
1828
+ const value = key.startsWith("prop:") ? read() : readValue(read);
1829
+ propertyBacked = hasDomPropertyWrite(el, key, value, tag);
1830
+ const propertyUndo = propertyBacked ? captureDomPropertyUndo(el, key, value, tag) : null;
1831
+ if (propertyUndo && installing && !fresh) pass.onReversibleCommit(propertyUndo);
1832
+ try {
1833
+ applyScalarPropValue(el, key, value, tag, last);
1834
+ } catch (error) {
1835
+ if (propertyUndo && (fresh || !installing)) try {
1836
+ propertyUndo();
1837
+ } catch (rollbackError) {
1838
+ throw new AggregateError([error, rollbackError], "Bound DOM property write and rollback failed");
1839
+ }
1840
+ throw error;
1841
+ }
1842
+ const changed = !Object.is(last, value);
1843
+ last = value;
1844
+ if (changed && tag === "option" && key === "value") {
1845
+ const select = enclosingSelect(node.parent);
1846
+ if (select) resyncSelect(select);
1847
+ }
1848
+ appliedByBinding.set(binding, value);
1849
+ } catch (error) {
1850
+ if (!deliverToBoundary(node.owner, error)) throw error;
1851
+ }
1852
+ }, effectScheduler("effect", (node.owner?.depth ?? 0) + 1), null, "previous");
1853
+ pass.own(binding);
1854
+ const install = () => {
1855
+ const previous = node.bindings?.get(key);
1856
+ (node.bindings ??= /* @__PURE__ */ new Map()).set(key, binding);
1857
+ const reversibleAttribute = isSimpleAttribute(tag, key, void 0) || key === "class" || key === "className" || key === "style";
1858
+ const reversibleControl = isInputValue(tag, key) || isBooleanControl(tag, key);
1859
+ if (!fresh) {
1860
+ recordBindingInstallUndo(pass, node, key, binding, previous);
1861
+ if (reversibleAttribute) recordAttributeUndo(pass, el, getRenderedAttributeName(el, key));
1862
+ else if (isInputValue(tag, key)) recordInputValueUndo(pass, node);
1863
+ else if (isBooleanControl(tag, key)) recordBooleanControlUndo(pass, node, key);
1864
+ }
1865
+ try {
1866
+ binding.run();
1867
+ if (binding._hasError) throw binding._error;
1868
+ } catch (error) {
1869
+ if (!fresh && (reversibleAttribute || reversibleControl || propertyBacked)) throw new CommitMutationError(error);
1870
+ throw error;
1871
+ } finally {
1872
+ installing = false;
1873
+ }
1874
+ };
1875
+ if (fresh) install();
1876
+ else pass.op(install);
1877
+ }
1878
+ /** Point the element's ref at it once the pass commits. */
1879
+ function attachRef(pass, node, previous) {
1880
+ const ref = node.props.ref;
1881
+ if (ref === previous) return;
1882
+ const el = node.el;
1883
+ pass.after(() => {
1884
+ const errors = [];
1885
+ try {
1886
+ if (previous) setRef(previous, null);
1887
+ } catch (error) {
1888
+ errors.push(error);
1889
+ }
1890
+ try {
1891
+ if (ref) setRef(ref, el);
1892
+ } catch (error) {
1893
+ errors.push(error);
1894
+ }
1895
+ throwCollected(errors, "Ref replacement callbacks failed");
1896
+ });
1897
+ }
1898
+ /** End a removed element's bindings, listeners, and ref. */
1899
+ function releaseProps(node, errors) {
1900
+ if (node.bindings) {
1901
+ for (const binding of node.bindings.values()) binding.dispose(errors);
1902
+ node.bindings = null;
1903
+ }
1904
+ if (node.listeners) removeAllListeners(node.listeners, node.el, errors);
1905
+ const ref = node.props.ref;
1906
+ if (ref) try {
1907
+ setRef(ref, null);
1908
+ } catch (error) {
1909
+ errors.push(error);
1910
+ }
1911
+ }
1912
+ //#endregion
1913
+ //#region src/core/dom/reconcile.ts
1914
+ function withOwner(ctx, owner) {
1915
+ return owner === ctx.owner ? ctx : {
1916
+ ...ctx,
1917
+ owner
1918
+ };
1919
+ }
1920
+ function typeOf(node) {
1921
+ switch (node.kind) {
1922
+ case HOST: return node.tag;
1923
+ case 2: return node.instance.fn;
1924
+ case 7: return node.node;
1925
+ default: return node.kind;
1926
+ }
1927
+ }
1928
+ function matches(node, child) {
1929
+ return node.kind === child.kind && typeOf(node) === descriptorType(child);
1930
+ }
1931
+ /**
1932
+ * Reconcile `parent`'s children against `value`.
1933
+ *
1934
+ * With `fresh`, `parent` is new: its children are built and, for an element,
1935
+ * appended to it directly. Otherwise the committed list is replaced by an
1936
+ * operation. Returns the new child list.
1937
+ */
1938
+ function reconcileChildren(ctx, parent, value, fresh, checkKeys = true) {
1939
+ const next = normalizeChildren(value);
1940
+ if (fresh) {
1941
+ const seen = /* @__PURE__ */ new Set();
1942
+ for (const child of next) {
1943
+ if (child.key === void 0) continue;
1944
+ if (seen.has(child.key)) throw new Error(`[Askr] Duplicate key ${String(child.key)} among siblings.`);
1945
+ seen.add(child.key);
1946
+ }
1947
+ const result = next.map((child) => ctx.nodes.create(ctx, parent, child));
1948
+ if (parent.kind === HOST) {
1949
+ if (ctx.hydrate?.container === parent.el) {
1950
+ const el = parent.el;
1951
+ ctx.pass.op(() => syncChildren(ctx.pass, el, result.flatMap((node) => collectDom(node))));
1952
+ } else for (const node of result) for (const dom of collectDom(node)) parent.el.appendChild(dom);
1953
+ }
1954
+ return result;
1955
+ }
1956
+ const previous = parent.children;
1957
+ if (next.length === previous.length) {
1958
+ let samePositions = true;
1959
+ for (let i = 0; i < next.length; i++) if (next[i].key !== previous[i].key || !matches(previous[i], next[i])) {
1960
+ samePositions = false;
1961
+ break;
1962
+ }
1963
+ if (samePositions) {
1964
+ const result = previous.slice();
1965
+ for (let i = 0; i < next.length; i++) ctx.nodes.patch(ctx, previous[i], next[i]);
1966
+ return result;
1967
+ }
1968
+ }
1969
+ const slot = ctx.pass.reserve();
1970
+ const index = indexChildren(previous);
1971
+ const used = new Uint8Array(previous.length);
1972
+ const sources = new Int32Array(next.length).fill(-1);
1973
+ const result = [];
1974
+ const cursors = /* @__PURE__ */ new Map();
1975
+ let seen = null;
1976
+ for (let i = 0; i < next.length; i++) {
1977
+ const child = next[i];
1978
+ let match = -1;
1979
+ if (child.key !== void 0) {
1980
+ seen ??= /* @__PURE__ */ new Set();
1981
+ if (seen.has(child.key)) throw new Error(`[Askr] Duplicate key ${String(child.key)} among siblings.`);
1982
+ seen.add(child.key);
1983
+ const at = index.keyed?.get(child.key);
1984
+ if (at !== void 0 && !used[at] && matches(previous[at], child)) match = at;
1985
+ } else {
1986
+ const type = descriptorType(child);
1987
+ const bucket = index.unkeyed?.get(type);
1988
+ if (bucket) {
1989
+ let cursor = cursors.get(type) ?? 0;
1990
+ while (cursor < bucket.length && used[bucket[cursor]]) cursor++;
1991
+ if (cursor < bucket.length && matches(previous[bucket[cursor]], child)) {
1992
+ match = bucket[cursor];
1993
+ cursor++;
1994
+ }
1995
+ cursors.set(type, cursor);
1996
+ }
1997
+ }
1998
+ if (match >= 0) {
1999
+ used[match] = 1;
2000
+ sources[i] = match;
2001
+ ctx.nodes.patch(ctx, previous[match], child);
2002
+ result.push(previous[match]);
2003
+ } else result.push(ctx.nodes.create(ctx, parent, child));
2004
+ }
2005
+ const removed = previous.filter((_, i) => !used[i]);
2006
+ if (removed.length === 0 && result.length === previous.length && result.every((node, index) => node === previous[index])) return result;
2007
+ const stable = longestIncreasingSubsequence(sources);
2008
+ const nodes = ctx.nodes;
2009
+ nodes.listChanged?.(ctx, parent);
2010
+ ctx.pass.fill(slot, () => {
2011
+ const positions = [];
2012
+ const record = (node) => {
2013
+ positions.push({
2014
+ node,
2015
+ parent: node.parentNode,
2016
+ next: node.nextSibling
2017
+ });
2018
+ };
2019
+ ctx.pass.onReversibleCommit(() => {
2020
+ for (let index = positions.length - 1; index >= 0; index--) {
2021
+ const { node, parent, next } = positions[index];
2022
+ if (!parent) node.parentNode?.removeChild(node);
2023
+ else if (node.parentNode !== parent || node.nextSibling !== next) parent.insertBefore(node, next);
2024
+ }
2025
+ parent.children = previous;
2026
+ }, () => {
2027
+ const errors = [];
2028
+ for (const node of removed) nodes.release(node, errors);
2029
+ reportTeardown(errors);
2030
+ });
2031
+ try {
2032
+ placeChildren(parent, result, sources, stable, record);
2033
+ for (const node of removed) for (const dom of collectDom(node)) {
2034
+ record(dom);
2035
+ dom.parentNode?.removeChild(dom);
2036
+ }
2037
+ } catch (error) {
2038
+ throw new CommitMutationError(error);
2039
+ }
2040
+ parent.children = result;
2041
+ for (const node of result) if (node.parent !== parent) node.parent = parent;
2042
+ });
2043
+ return result;
2044
+ }
2045
+ function indexChildren(children) {
2046
+ const index = {
2047
+ keyed: null,
2048
+ unkeyed: null
2049
+ };
2050
+ for (let i = 0; i < children.length; i++) {
2051
+ const node = children[i];
2052
+ if (node.key !== void 0) {
2053
+ (index.keyed ??= /* @__PURE__ */ new Map()).set(node.key, i);
2054
+ continue;
2055
+ }
2056
+ const type = typeOf(node);
2057
+ const bucket = (index.unkeyed ??= /* @__PURE__ */ new Map()).get(type);
2058
+ if (bucket) bucket.push(i);
2059
+ else index.unkeyed.set(type, [i]);
2060
+ }
2061
+ return index;
2062
+ }
2063
+ /**
2064
+ * Put `children`'s DOM in order, walking backwards from the end so every
2065
+ * insertion has a settled reference node. Reused children on the stable
2066
+ * subsequence stay where they are.
2067
+ */
2068
+ function placeChildren(parent, children, sources, stable, record) {
2069
+ const container = containerOf(parent);
2070
+ let next = endOf(parent);
2071
+ for (let i = children.length - 1; i >= 0; i--) {
2072
+ const node = children[i];
2073
+ if (sources[i] >= 0 && stable.has(i)) {
2074
+ next = firstDom(node) ?? next;
2075
+ continue;
2076
+ }
2077
+ const nodes = collectDom(node);
2078
+ for (let j = nodes.length - 1; j >= 0; j--) {
2079
+ const dom = nodes[j];
2080
+ if (dom.parentNode !== container || dom.nextSibling !== next) {
2081
+ record(dom);
2082
+ container.insertBefore(dom, next);
2083
+ }
2084
+ next = dom;
2085
+ }
2086
+ }
2087
+ }
2088
+ /** Indices of a longest increasing run of `sources` values (ignoring -1). */
2089
+ function longestIncreasingSubsequence(sources) {
2090
+ const tails = [];
2091
+ const previous = new Int32Array(sources.length).fill(-1);
2092
+ for (let i = 0; i < sources.length; i++) {
2093
+ const value = sources[i];
2094
+ if (value < 0) continue;
2095
+ let low = 0;
2096
+ let high = tails.length;
2097
+ while (low < high) {
2098
+ const mid = low + high >> 1;
2099
+ if (sources[tails[mid]] < value) low = mid + 1;
2100
+ else high = mid;
2101
+ }
2102
+ if (low > 0) previous[i] = tails[low - 1];
2103
+ tails[low] = i;
2104
+ }
2105
+ const result = /* @__PURE__ */ new Set();
2106
+ for (let i = tails.length ? tails[tails.length - 1] : -1; i >= 0; i = previous[i]) result.add(i);
2107
+ return result;
2108
+ }
2109
+ //#endregion
2110
+ //#region src/core/dom/nodes.ts
2111
+ const SVG_NS = "http://www.w3.org/2000/svg";
2112
+ const MATHML_NS = "http://www.w3.org/1998/Math/MathML";
2113
+ /** Namespace for children of an element in namespace `ns` with tag `tag`. */
2114
+ function childNamespace(tag, ns) {
2115
+ if (tag === "svg") return SVG_NS;
2116
+ if (tag === "math") return MATHML_NS;
2117
+ if (ns === SVG_NS && tag === "foreignObject") return null;
2118
+ return ns;
2119
+ }
2120
+ function elementNamespace(tag, parentNs) {
2121
+ if (tag === "svg") return SVG_NS;
2122
+ if (tag === "math") return MATHML_NS;
2123
+ return parentNs;
2124
+ }
2125
+ /** Namespace new children of `parent` are created in. */
2126
+ function namespaceAt(parent) {
2127
+ for (let p = parent; p; p = p.parent) {
2128
+ if (p.kind === HOST) return childNamespace(p.tag, namespaceOf(p.el));
2129
+ if (p.kind === 5) return childNamespace(p.el.localName, namespaceOf(p.el));
2130
+ }
2131
+ return null;
2132
+ }
2133
+ function createRenderContext(pass, owner, ns, hydrate = null) {
2134
+ return {
2135
+ pass,
2136
+ owner,
2137
+ ns,
2138
+ nodes: domNodes,
2139
+ hydrate
2140
+ };
2141
+ }
2142
+ /** Schedules a standalone update of a function child (see `updates`). */
2143
+ let scheduleDynamicUpdate = () => {};
2144
+ function setDynamicUpdateScheduler(schedule) {
2145
+ scheduleDynamicUpdate = schedule;
2146
+ }
2147
+ function nearestInstance(owner) {
2148
+ for (let o = owner; o; o = o.parent) if (o instanceof ComponentInstance) return o;
2149
+ return null;
2150
+ }
2151
+ function createHost(ctx, parent, key, tag, props) {
2152
+ const ns = elementNamespace(tag, ctx.ns);
2153
+ const hydrate = ctx.hydrate;
2154
+ const adopted = hydrate ? hydrate.cursor.claimElement(hydrate.container, tag, ns) : null;
2155
+ const el = adopted ?? (ns ? document.createElementNS(ns, tag) : document.createElement(tag));
2156
+ const node = {
2157
+ kind: HOST,
2158
+ parent,
2159
+ key,
2160
+ el,
2161
+ tag,
2162
+ props,
2163
+ children: [],
2164
+ bindings: null,
2165
+ listeners: null,
2166
+ owner: nearestInstance(ctx.owner)
2167
+ };
2168
+ if (adopted?.hasAttribute(SKIP_HYDRATE)) {
2169
+ ctx.pass.hasDormantPortalWriter ||= containsPortalWriterAnchor(adopted);
2170
+ node.dormant = {
2171
+ owner: ctx.owner,
2172
+ ns
2173
+ };
2174
+ ctx.pass.op(() => {
2175
+ const previous = dormantHosts.get(adopted);
2176
+ ctx.pass.onReversibleCommit(() => {
2177
+ if (previous) dormantHosts.set(adopted, previous);
2178
+ else dormantHosts.delete(adopted);
2179
+ });
2180
+ dormantHosts.set(adopted, node);
2181
+ });
2182
+ return node;
2183
+ }
2184
+ applyInitialProps(ctx.pass, node, adopted !== null);
2185
+ if (adopted) ctx.pass.op(() => {
2186
+ try {
2187
+ removeUnrenderedAttributes(adopted, props, (attributes) => {
2188
+ ctx.pass.onReversibleCommit(() => {
2189
+ for (const attribute of attributes) adopted.setAttributeNode(attribute);
2190
+ });
2191
+ });
2192
+ } catch (error) {
2193
+ throw new CommitMutationError(error);
2194
+ }
2195
+ });
2196
+ if (!isDangerousInnerHTMLPayload(props.dangerouslySetInnerHTML)) node.children = reconcileChildren({
2197
+ ...ctx,
2198
+ ns: childNamespace(tag, ns),
2199
+ hydrate: adopted && hydrate ? {
2200
+ cursor: hydrate.cursor,
2201
+ container: adopted
2202
+ } : null
2203
+ }, node, props.children, true);
2204
+ applyTrailingProps(ctx.pass, node, props, true, adopted !== null);
2205
+ attachRef(ctx.pass, node, void 0);
2206
+ return node;
2207
+ }
2208
+ const SKIP_HYDRATE = "data-skip-hydrate";
2209
+ const dormantHosts = /* @__PURE__ */ new WeakMap();
2210
+ function containsPortalWriterAnchor(root) {
2211
+ const pending = Array.from(root.childNodes);
2212
+ while (pending.length) {
2213
+ const node = pending.pop();
2214
+ if (isSSRPortalWriterAnchor(node)) return true;
2215
+ for (const child of node.childNodes) pending.push(child);
2216
+ }
2217
+ return false;
2218
+ }
2219
+ /** The dormant host adopted for `el`, if it has not been activated. */
2220
+ function dormantHostFor(el) {
2221
+ const node = dormantHosts.get(el);
2222
+ return node?.dormant ? node : null;
2223
+ }
2224
+ /**
2225
+ * Hydrate a dormant host in place: claim its server children, then apply
2226
+ * its props, listeners, and ref. The marker is removed when the pass
2227
+ * commits, so a failed activation leaves it for a retry.
2228
+ */
2229
+ function hydrateDormantHost(pass, node) {
2230
+ const dormant = node.dormant;
2231
+ const el = node.el;
2232
+ const ctx = createRenderContext(pass, dormant.owner, dormant.ns, {
2233
+ cursor: new HydrationCursor(),
2234
+ container: el
2235
+ });
2236
+ const props = node.props;
2237
+ applyInitialProps(pass, node, true);
2238
+ if (!isDangerousInnerHTMLPayload(props.dangerouslySetInnerHTML)) {
2239
+ node.children = reconcileChildren({
2240
+ ...ctx,
2241
+ ns: childNamespace(node.tag, dormant.ns)
2242
+ }, node, props.children, true);
2243
+ pass.onDiscard(() => {
2244
+ node.children = [];
2245
+ });
2246
+ }
2247
+ applyTrailingProps(pass, node, props, true, true);
2248
+ attachRef(pass, node, void 0);
2249
+ pass.op(() => {
2250
+ pass.onReversibleCommit(() => {
2251
+ node.dormant = dormant;
2252
+ dormantHosts.set(el, node);
2253
+ });
2254
+ try {
2255
+ removeUnrenderedAttributes(el, props, (attributes) => {
2256
+ pass.onReversibleCommit(() => {
2257
+ for (const attribute of attributes) el.setAttributeNode(attribute);
2258
+ });
2259
+ });
2260
+ } catch (error) {
2261
+ throw new CommitMutationError(error);
2262
+ }
2263
+ node.dormant = null;
2264
+ dormantHosts.delete(el);
2265
+ });
2266
+ }
2267
+ function ownsChildren(props) {
2268
+ return !props.imperativeChildren && !isDangerousInnerHTMLPayload(props.dangerouslySetInnerHTML);
2269
+ }
2270
+ function patchHost(ctx, node, props) {
2271
+ const previous = node.props;
2272
+ if (previous === props) return;
2273
+ if (node.dormant) {
2274
+ ctx.pass.op(() => {
2275
+ const previous = node.props;
2276
+ ctx.pass.onReversibleCommit(() => {
2277
+ node.props = previous;
2278
+ });
2279
+ node.props = props;
2280
+ });
2281
+ return;
2282
+ }
2283
+ const wasManaged = ownsChildren(previous);
2284
+ const isManaged = ownsChildren(props);
2285
+ if (wasManaged && !isManaged) reconcileChildren(ctx, node, null, false);
2286
+ patchProps(ctx.pass, node, previous, props);
2287
+ if (node.tag === "option" && !Object.is(previous.value, props.value)) syncEnclosingSelect(ctx.pass, node.parent);
2288
+ if (!wasManaged && isManaged && node.children.length === 0) ctx.pass.op(() => {
2289
+ const children = Array.from(node.el.childNodes);
2290
+ ctx.pass.onReversibleCommit(() => restoreElementChildren(node.el, children));
2291
+ try {
2292
+ node.el.replaceChildren();
2293
+ } catch (error) {
2294
+ throw new CommitMutationError(error);
2295
+ }
2296
+ });
2297
+ if (isManaged) {
2298
+ reconcileChildren({
2299
+ ...ctx,
2300
+ ns: childNamespace(node.tag, namespaceOf(node.el))
2301
+ }, node, props.children, false);
2302
+ if (props.children === void 0 || props.children === null || props.children === false) ctx.pass.op(() => {
2303
+ if (!node.el.firstChild) return;
2304
+ const previous = Array.from(node.el.childNodes);
2305
+ ctx.pass.onReversibleCommit(() => restoreElementChildren(node.el, previous));
2306
+ try {
2307
+ node.el.replaceChildren();
2308
+ } catch (error) {
2309
+ throw new CommitMutationError(error);
2310
+ }
2311
+ });
2312
+ }
2313
+ ctx.pass.op(() => {
2314
+ const previous = node.props;
2315
+ ctx.pass.onReversibleCommit(() => {
2316
+ node.props = previous;
2317
+ });
2318
+ node.props = props;
2319
+ });
2320
+ if (node.tag === "select") ctx.pass.op(() => {
2321
+ applyTrailingProps(ctx.pass, node, props);
2322
+ markSelectSynced(ctx.pass, node);
2323
+ });
2324
+ if (props.ref !== previous.ref) attachRef(ctx.pass, {
2325
+ ...node,
2326
+ props
2327
+ }, previous.ref);
2328
+ }
2329
+ function namespaceOf(el) {
2330
+ const uri = el.namespaceURI;
2331
+ return uri === SVG_NS || uri === MATHML_NS ? uri : null;
2332
+ }
2333
+ function createComponent(ctx, parent, key, fn, props) {
2334
+ const instance = new ComponentInstance(ctx.owner, fn, props);
2335
+ ctx.pass.own(instance);
2336
+ const node = {
2337
+ kind: 2,
2338
+ parent,
2339
+ key,
2340
+ instance,
2341
+ children: []
2342
+ };
2343
+ instance.view = node;
2344
+ node.children = renderInstance(ctx, node, true);
2345
+ return node;
2346
+ }
2347
+ function propsChanged(previous, next) {
2348
+ if (previous === next) return false;
2349
+ let count = 0;
2350
+ for (const key in previous) {
2351
+ count++;
2352
+ if (!(key in next) || !Object.is(previous[key], next[key])) return true;
2353
+ }
2354
+ for (const key in next) count--;
2355
+ return count !== 0;
2356
+ }
2357
+ function patchComponent(ctx, node, props) {
2358
+ const instance = node.instance;
2359
+ if (!propsChanged(instance.props, props) && !instance.computation.stale && !instance.computation._hasError && instance.seenAncestorContextRevision === ancestorContextRevision(instance)) return;
2360
+ instance.setProps(props);
2361
+ renderInstance(ctx, node, false);
2362
+ }
2363
+ function nearestComponent(instance) {
2364
+ for (let owner = instance.parent; owner; owner = owner.parent) if (owner instanceof ComponentInstance) return owner;
2365
+ return null;
2366
+ }
2367
+ /**
2368
+ * Highest `contextRevision` among `instance`'s component ancestors. Each
2369
+ * instance caches its inherited value for the current context epoch, so
2370
+ * patching every component in a deep chain stays linear. The cache relies on
2371
+ * a live instance's parent chain never changing (owners are not reparented).
2372
+ * The epoch is global, so a `provide()` anywhere invalidates every cache; the
2373
+ * worst case is the uncached O(depth) walk.
2374
+ */
2375
+ function ancestorContextRevision(instance) {
2376
+ const epoch = getContextEpoch();
2377
+ let chain = null;
2378
+ let revision = 0;
2379
+ for (let current = nearestComponent(instance); current;) {
2380
+ if (current.inheritedContextEpoch === epoch) {
2381
+ revision = Math.max(current.inheritedContextRevision, current.contextRevision);
2382
+ break;
2383
+ }
2384
+ (chain ??= []).push(current);
2385
+ current = nearestComponent(current);
2386
+ }
2387
+ if (!chain) return revision;
2388
+ for (let index = chain.length - 1; index >= 0; index--) {
2389
+ const ancestor = chain[index];
2390
+ ancestor.inheritedContextRevision = revision;
2391
+ ancestor.inheritedContextEpoch = epoch;
2392
+ revision = Math.max(revision, ancestor.contextRevision);
2393
+ }
2394
+ return revision;
2395
+ }
2396
+ /**
2397
+ * Render a component and reconcile its output. An error boundary catches a
2398
+ * failure anywhere in its subtree: the work recorded since it started is
2399
+ * rewound and it renders again showing its fallback.
2400
+ */
2401
+ /** The component rendering now while its root hydrates. */
2402
+ let hydratingRender = null;
2403
+ function renderComponent(ctx, node) {
2404
+ const previous = hydratingRender;
2405
+ hydratingRender = ctx.hydrate ? {
2406
+ ctx,
2407
+ node
2408
+ } : null;
2409
+ node.instance.hydrationResourceKeys = ctx.hydrate ? ctx.hydrate.cursor.claimResourceSlots(ctx.hydrate.container) : null;
2410
+ try {
2411
+ return node.instance.render((undo) => ctx.pass.onDiscard(undo));
2412
+ } finally {
2413
+ node.instance.hydrationResourceKeys = null;
2414
+ hydratingRender = previous;
2415
+ }
2416
+ }
2417
+ function isHydratingRender() {
2418
+ return hydratingRender !== null;
2419
+ }
2420
+ function commitSeenAncestorContextRevision(pass, instance, revision) {
2421
+ pass.op(() => {
2422
+ const previous = instance.seenAncestorContextRevision;
2423
+ pass.onReversibleCommit(() => {
2424
+ instance.seenAncestorContextRevision = previous;
2425
+ });
2426
+ instance.seenAncestorContextRevision = revision;
2427
+ });
2428
+ }
2429
+ /**
2430
+ * Render the hydrating component again after the rest of its root, claiming
2431
+ * the server nodes reserved at its position (portal hosts whose content is
2432
+ * written later in the same render).
2433
+ */
2434
+ function deferHydratingRender(instance) {
2435
+ const active = hydratingRender;
2436
+ if (!active || active.node.instance !== instance) return false;
2437
+ const { ctx, node } = active;
2438
+ const hydrate = ctx.hydrate;
2439
+ const cursor = hydrate.cursor.reserve(hydrate.container);
2440
+ hydrate.cursor.deferred.push({ render: () => {
2441
+ if (instance.disposed) return;
2442
+ if (!instance.computation.stale) {
2443
+ if (ctx.pass.hasDormantPortalWriter) {
2444
+ const held = cursor.heldNodes(hydrate.container);
2445
+ node.children = held.map((dom) => ({
2446
+ kind: 7,
2447
+ parent: node,
2448
+ key: void 0,
2449
+ node: dom
2450
+ }));
2451
+ node.deferredHydration = {
2452
+ cursor,
2453
+ container: hydrate.container
2454
+ };
2455
+ }
2456
+ return;
2457
+ }
2458
+ renderInstance({
2459
+ ...ctx,
2460
+ hydrate: {
2461
+ cursor,
2462
+ container: hydrate.container
2463
+ }
2464
+ }, node, true);
2465
+ } });
2466
+ return true;
2467
+ }
2468
+ /** Adopt held server portal output when its deferred writer first runs. */
2469
+ function hydrateDeferredComponent(ctx, node, output) {
2470
+ const deferred = node.deferredHydration;
2471
+ const previous = node.children;
2472
+ const next = reconcileChildren(withOwner({
2473
+ ...ctx,
2474
+ hydrate: deferred
2475
+ }, node.instance), node, output, true);
2476
+ const container = deferred.container;
2477
+ const after = nextDomAfter(node);
2478
+ ctx.pass.op(() => {
2479
+ const previousDom = Array.from(container.childNodes);
2480
+ ctx.pass.onReversibleCommit(() => {
2481
+ try {
2482
+ restoreNodeChildren(container, previousDom);
2483
+ } finally {
2484
+ node.children = previous;
2485
+ node.deferredHydration = deferred;
2486
+ }
2487
+ });
2488
+ const retained = new Set(next.flatMap((child) => collectDom(child)));
2489
+ try {
2490
+ for (const child of previous) for (const dom of collectDom(child)) if (!retained.has(dom)) dom.parentNode?.removeChild(dom);
2491
+ let before = after;
2492
+ for (let index = next.length - 1; index >= 0; index--) {
2493
+ const doms = collectDom(next[index]);
2494
+ for (let item = doms.length - 1; item >= 0; item--) {
2495
+ const dom = doms[item];
2496
+ if (dom.parentNode !== container || dom.nextSibling !== before) container.insertBefore(dom, before);
2497
+ before = dom;
2498
+ }
2499
+ }
2500
+ } catch (error) {
2501
+ throw new CommitMutationError(error);
2502
+ }
2503
+ node.children = next;
2504
+ node.deferredHydration = null;
2505
+ });
2506
+ ctx.pass.markRendered(node.instance);
2507
+ }
2508
+ function renderInstance(ctx, node, fresh) {
2509
+ const instance = node.instance;
2510
+ const inner = withOwner(ctx, instance);
2511
+ const mark = ctx.pass.mark();
2512
+ try {
2513
+ const output = renderComponent(ctx, node);
2514
+ if (instance.mounted) ctx.pass.onDiscard(() => {
2515
+ if (ctx.pass.commitAborted) instance.computation.deferRetry();
2516
+ else instance.computation.invalidate();
2517
+ });
2518
+ const children = reconcileComponentOutput(inner, node, output, fresh);
2519
+ ctx.pass.markRendered(instance);
2520
+ const revision = ancestorContextRevision(instance);
2521
+ commitSeenAncestorContextRevision(ctx.pass, instance, revision);
2522
+ return children;
2523
+ } catch (caught) {
2524
+ const error = clarifyRenderError(caught);
2525
+ const boundary = instance.boundary;
2526
+ if (!boundary) {
2527
+ noteErrorOrigin(instance.parent, error);
2528
+ throw error;
2529
+ }
2530
+ for (const failure of ctx.pass.rewind(mark)) reportUncaughtErrorLater(failure);
2531
+ if (!boundary(error)) throw error;
2532
+ const children = reconcileChildren(inner, node, componentOutput(renderComponent(ctx, node)), fresh);
2533
+ ctx.pass.markRendered(instance);
2534
+ const revision = ancestorContextRevision(instance);
2535
+ commitSeenAncestorContextRevision(ctx.pass, instance, revision);
2536
+ return children;
2537
+ }
2538
+ }
2539
+ /**
2540
+ * A component that returns the same component type directly can form a very
2541
+ * deep transparent chain. Walk the hook-free part of that chain explicitly;
2542
+ * all other output still goes through the ordinary child reconciler.
2543
+ */
2544
+ function reconcileComponentOutput(ctx, node, value, fresh) {
2545
+ let output = componentOutput(value);
2546
+ let current = node;
2547
+ let inner = ctx;
2548
+ let creating = fresh;
2549
+ const rendered = [];
2550
+ for (;;) {
2551
+ const next = current.instance.hooks.length === 0 && !current.instance.boundary ? selfChild(output, current.instance.fn) : null;
2552
+ if (!next) {
2553
+ const children = reconcileChildren(inner, current, output, creating);
2554
+ if (creating) current.children = children;
2555
+ break;
2556
+ }
2557
+ let child;
2558
+ if (creating) {
2559
+ const instance = new ComponentInstance(current.instance, next.fn, next.props);
2560
+ ctx.pass.own(instance);
2561
+ child = {
2562
+ kind: 2,
2563
+ parent: current,
2564
+ key: next.key,
2565
+ instance,
2566
+ children: []
2567
+ };
2568
+ instance.view = child;
2569
+ current.children = [child];
2570
+ } else {
2571
+ const previous = current.children;
2572
+ if (previous.length !== 1 || previous[0].kind !== 2 || previous[0].key !== next.key || previous[0].instance.fn !== next.fn) {
2573
+ reconcileChildren(inner, current, output, false);
2574
+ break;
2575
+ }
2576
+ child = previous[0];
2577
+ const instance = child.instance;
2578
+ if (!propsChanged(instance.props, next.props) && !instance.computation.stale && !instance.computation._hasError && instance.seenAncestorContextRevision === ancestorContextRevision(instance)) break;
2579
+ instance.setProps(next.props);
2580
+ }
2581
+ const instance = child.instance;
2582
+ rendered.push(child);
2583
+ inner = withOwner(inner, instance);
2584
+ current = child;
2585
+ output = componentOutput(renderComponent(inner, child));
2586
+ if (instance.mounted) ctx.pass.onDiscard(() => {
2587
+ if (ctx.pass.commitAborted) instance.computation.deferRetry();
2588
+ else instance.computation.invalidate();
2589
+ });
2590
+ creating = fresh;
2591
+ }
2592
+ for (let i = rendered.length - 1; i >= 0; i--) {
2593
+ const instance = rendered[i].instance;
2594
+ ctx.pass.markRendered(instance);
2595
+ const revision = ancestorContextRevision(instance);
2596
+ commitSeenAncestorContextRevision(ctx.pass, instance, revision);
2597
+ }
2598
+ return node.children;
2599
+ }
2600
+ function restoreNodeChildren(container, previous) {
2601
+ for (let index = 0; index < previous.length; index += 1) {
2602
+ const current = container.childNodes[index] ?? null;
2603
+ if (current !== previous[index]) container.insertBefore(previous[index], current);
2604
+ }
2605
+ while (container.childNodes.length > previous.length) container.removeChild(container.lastChild);
2606
+ }
2607
+ function selfChild(output, fn) {
2608
+ if (!output || typeof output !== "object" || Array.isArray(output) || output.type !== fn) return null;
2609
+ const children = normalizeChildren(output);
2610
+ return children.length === 1 && children[0].kind === 2 ? children[0] : null;
2611
+ }
2612
+ /** A function returned by a component is a value, not a child slot. */
2613
+ function componentOutput(value) {
2614
+ return typeof value === "function" ? null : value;
2615
+ }
2616
+ function createDynamic(ctx, parent, fn) {
2617
+ const node = {
2618
+ kind: DYNAMIC,
2619
+ parent,
2620
+ key: void 0,
2621
+ fn,
2622
+ children: [],
2623
+ depth: (nearestInstance(ctx.owner)?.depth ?? 0) + 1
2624
+ };
2625
+ node.instance = new ComponentInstance(ctx.owner, () => functionChildOutput(readValue(node.fn)), {}, () => scheduleDynamicUpdate(node));
2626
+ node.computation = node.instance.computation;
2627
+ ctx.pass.own(node.instance);
2628
+ node.children = reconcileChildren(withOwner(ctx, node.instance), node, readDynamic(node), true, false);
2629
+ ctx.pass.markRendered(node.instance);
2630
+ return node;
2631
+ }
2632
+ /** Run a function child's read now, tracking what it reads. */
2633
+ function readDynamic(node, pass) {
2634
+ const output = node.instance.render(pass ? (undo) => pass.onDiscard(undo) : void 0);
2635
+ if (pass && node.instance.mounted) pass.onDiscard(() => {
2636
+ if (pass.commitAborted) node.computation.deferRetry();
2637
+ else node.computation.invalidate();
2638
+ });
2639
+ return componentOutput(output);
2640
+ }
2641
+ function patchDynamic(ctx, node, fn) {
2642
+ const previous = node.fn;
2643
+ node.fn = fn;
2644
+ ctx.pass.onDiscard(() => {
2645
+ node.fn = previous;
2646
+ });
2647
+ updateDynamic(ctx, node);
2648
+ }
2649
+ /** Reconcile a function child, remounting only when its own hooks change. */
2650
+ function updateDynamic(ctx, node) {
2651
+ const mark = ctx.pass.mark();
2652
+ try {
2653
+ reconcileChildren(withOwner(ctx, node.instance), node, readDynamic(node, ctx.pass), false, false);
2654
+ ctx.pass.markRendered(node.instance);
2655
+ } catch (error) {
2656
+ if (!(error instanceof HookOrderChangeError) || error.instance !== node.instance) throw error;
2657
+ for (const failure of ctx.pass.rewind(mark)) reportUncaughtErrorLater(failure);
2658
+ const slot = ctx.pass.reserve();
2659
+ const replacement = createDynamic(withOwner(ctx, node.instance.parent), node.parent, node.fn);
2660
+ ctx.pass.fill(slot, () => {
2661
+ const parent = node.parent;
2662
+ const container = containerOf(parent);
2663
+ const next = nextDomAfter(node);
2664
+ const oldDom = collectDom(node);
2665
+ const replacementDom = collectDom(replacement);
2666
+ const index = parent.children.indexOf(node);
2667
+ ctx.pass.onReversibleCommit(() => {
2668
+ for (const dom of replacementDom) dom.parentNode?.removeChild(dom);
2669
+ parent.children[index] = node;
2670
+ }, () => {
2671
+ const errors = [];
2672
+ for (const dom of oldDom) dom.parentNode?.removeChild(dom);
2673
+ release(node, errors);
2674
+ reportTeardown(errors);
2675
+ });
2676
+ parent.children[index] = replacement;
2677
+ try {
2678
+ for (const dom of replacementDom) container.insertBefore(dom, next);
2679
+ } catch (error) {
2680
+ throw new CommitMutationError(error);
2681
+ }
2682
+ });
2683
+ }
2684
+ }
2685
+ function release(node, errors) {
2686
+ const pending = [{
2687
+ node,
2688
+ finish: false
2689
+ }];
2690
+ while (pending.length) {
2691
+ const frame = pending.pop();
2692
+ const current = frame.node;
2693
+ if (frame.finish) {
2694
+ if (current.kind === HOST) {
2695
+ if (current.dormant) dormantHosts.delete(current.el);
2696
+ releaseProps(current, errors);
2697
+ }
2698
+ if (current.kind === 2 || current.kind === DYNAMIC) current.instance.dispose(errors);
2699
+ continue;
2700
+ }
2701
+ if (current.kind === 1 || current.kind === 7) continue;
2702
+ pending.push({
2703
+ node: current,
2704
+ finish: true
2705
+ });
2706
+ for (let i = current.children.length - 1; i >= 0; i--) pending.push({
2707
+ node: current.children[i],
2708
+ finish: false
2709
+ });
2710
+ }
2711
+ }
2712
+ /**
2713
+ * A select's controlled value depends on its options. When a pass changes
2714
+ * them without patching the select (a child component, `For`, or function
2715
+ * child adds, removes, or edits options), re-apply the select's value after
2716
+ * the pass commits, once per select. Each change registers its own sync so
2717
+ * an error boundary that rewinds part of the pass drops only its own.
2718
+ */
2719
+ const scheduledSelectSyncs = /* @__PURE__ */ new WeakMap();
2720
+ const writtenSelectValues = /* @__PURE__ */ new WeakMap();
2721
+ function syncEnclosingSelect(pass, parent) {
2722
+ const select = enclosingSelect(parent);
2723
+ if (!select || select.dormant || !("value" in select.props)) return;
2724
+ let scheduled = scheduledSelectSyncs.get(pass);
2725
+ if (!scheduled) scheduledSelectSyncs.set(pass, scheduled = /* @__PURE__ */ new Set());
2726
+ if (scheduled.has(select)) return;
2727
+ scheduled.add(select);
2728
+ pass.onDiscard(() => scheduled.delete(select));
2729
+ recordSelectUndo(pass, select);
2730
+ pass.beforeJournalSettle(() => {
2731
+ if (writtenSelectValues.get(pass)?.has(select)) return;
2732
+ try {
2733
+ resyncSelect(select);
2734
+ } catch (error) {
2735
+ throw new CommitMutationError(error);
2736
+ }
2737
+ });
2738
+ }
2739
+ /** The select's own patch applied its value after its options. */
2740
+ function markSelectSynced(pass, select) {
2741
+ let written = writtenSelectValues.get(pass);
2742
+ if (!written) writtenSelectValues.set(pass, written = /* @__PURE__ */ new Set());
2743
+ written.add(select);
2744
+ }
2745
+ const domNodes = {
2746
+ listChanged(ctx, parent) {
2747
+ syncEnclosingSelect(ctx.pass, parent);
2748
+ },
2749
+ create(ctx, parent, child) {
2750
+ switch (child.kind) {
2751
+ case 1: {
2752
+ const hydrate = ctx.hydrate;
2753
+ const claimed = hydrate?.cursor.claimText(hydrate.container, child.text);
2754
+ if (claimed && claimed.data !== child.text) {
2755
+ const text = child.text;
2756
+ ctx.pass.op(() => {
2757
+ const previous = claimed.data;
2758
+ ctx.pass.onReversibleCommit(() => {
2759
+ claimed.data = previous;
2760
+ });
2761
+ try {
2762
+ claimed.data = text;
2763
+ } catch (error) {
2764
+ throw new CommitMutationError(error);
2765
+ }
2766
+ });
2767
+ }
2768
+ return {
2769
+ kind: 1,
2770
+ parent,
2771
+ key: void 0,
2772
+ node: claimed ?? document.createTextNode(child.text),
2773
+ text: child.text
2774
+ };
2775
+ }
2776
+ case 0: return createHost(ctx, parent, child.key, child.tag, child.props);
2777
+ case 2: return createComponent(ctx, parent, child.key, child.fn, child.props);
2778
+ case 3: {
2779
+ const node = {
2780
+ kind: 3,
2781
+ parent,
2782
+ key: child.key,
2783
+ children: []
2784
+ };
2785
+ node.children = reconcileChildren(withOwner(ctx, child.owner ?? ctx.owner), node, child.children, true);
2786
+ return node;
2787
+ }
2788
+ case 4: return createDynamic(ctx, parent, child.fn);
2789
+ case 7: return {
2790
+ kind: 7,
2791
+ parent,
2792
+ key: void 0,
2793
+ node: child.node
2794
+ };
2795
+ }
2796
+ },
2797
+ patch(ctx, node, child) {
2798
+ switch (node.kind) {
2799
+ case 1: {
2800
+ const text = child.text;
2801
+ if (node.text !== text) {
2802
+ ctx.pass.op(() => {
2803
+ const previous = node.text;
2804
+ const previousData = node.node.data;
2805
+ ctx.pass.onReversibleCommit(() => {
2806
+ try {
2807
+ node.node.data = previousData;
2808
+ } finally {
2809
+ node.text = previous;
2810
+ }
2811
+ });
2812
+ try {
2813
+ node.node.data = text;
2814
+ node.text = text;
2815
+ } catch (error) {
2816
+ throw new CommitMutationError(error);
2817
+ }
2818
+ });
2819
+ syncEnclosingSelect(ctx.pass, node.parent);
2820
+ }
2821
+ return;
2822
+ }
2823
+ case HOST:
2824
+ patchHost(ctx, node, child.props);
2825
+ return;
2826
+ case 2:
2827
+ patchComponent(ctx, node, child.props);
2828
+ return;
2829
+ case DYNAMIC:
2830
+ patchDynamic(ctx, node, child.fn);
2831
+ return;
2832
+ case 7: return;
2833
+ case 3: {
2834
+ const owner = child.owner;
2835
+ reconcileChildren(withOwner(ctx, owner ?? ctx.owner), node, child.children, false);
2836
+ return;
2837
+ }
2838
+ }
2839
+ },
2840
+ release
2841
+ };
2842
+ //#endregion
2843
+ //#region src/core/dom/updates.ts
2844
+ /**
2845
+ * Standalone updates: a component or function child whose own reads changed
2846
+ * re-renders in its own pass, independent of its parent.
2847
+ *
2848
+ * A failed update discards its pass and is delivered to the nearest error
2849
+ * boundary above it; without one, the error propagates to the scheduler,
2850
+ * which reports it after the flush.
2851
+ */
2852
+ function runPass(owner, render) {
2853
+ const pass = new Pass();
2854
+ try {
2855
+ pass.run(() => render(pass));
2856
+ } catch (error) {
2857
+ for (const failure of pass.discard()) reportUncaughtErrorLater(failure);
2858
+ routeError(owner, clarifyRenderError(error));
2859
+ return;
2860
+ }
2861
+ try {
2862
+ pass.commit();
2863
+ } catch (error) {
2864
+ if (pass.commitAborted) routeError(owner, error);
2865
+ else throw error;
2866
+ }
2867
+ }
2868
+ function rerenderInstance(instance) {
2869
+ const node = instance.view;
2870
+ if (!node || instance.disposed || !instance.mounted) return;
2871
+ if (!instance.computation.stale) return;
2872
+ runPass(instance.parent, (pass) => {
2873
+ const before = instance.renderCount;
2874
+ const computation = instance.computation;
2875
+ const sources = new Set(computation._sources ?? []);
2876
+ pass.onDiscard(() => computation.restoreSources(sources));
2877
+ try {
2878
+ computation.update();
2879
+ if (computation._hasError) throw computation._error;
2880
+ } catch (error) {
2881
+ if (!instance.boundary) throw error;
2882
+ }
2883
+ if (instance.renderCount === before && !computation._hasError) return;
2884
+ renderAfterUpdate(createRenderContext(pass, instance.parent, namespaceAt(node.parent)), node);
2885
+ });
2886
+ }
2887
+ /**
2888
+ * The instance already ran with fresh reads; reconcile that output (or, for
2889
+ * a boundary whose render failed, render its fallback).
2890
+ */
2891
+ function renderAfterUpdate(ctx, node) {
2892
+ const instance = node.instance;
2893
+ const computation = instance.computation;
2894
+ if (computation._hasError) {
2895
+ renderInstance(ctx, node, false);
2896
+ return;
2897
+ }
2898
+ const mark = ctx.pass.mark();
2899
+ try {
2900
+ if (node.deferredHydration) {
2901
+ hydrateDeferredComponent(ctx, node, componentOutput(computation._value));
2902
+ return;
2903
+ }
2904
+ reconcileChildren(withOwner(ctx, instance), node, componentOutput(computation._value), false);
2905
+ ctx.pass.markRendered(instance);
2906
+ } catch (caught) {
2907
+ const error = clarifyRenderError(caught);
2908
+ if (!instance.boundary) throw error;
2909
+ ctx.pass.rewind(mark);
2910
+ if (!instance.boundary(error)) throw error;
2911
+ renderInstance(ctx, node, false);
2912
+ }
2913
+ }
2914
+ const dynamicJobs = /* @__PURE__ */ new WeakMap();
2915
+ function scheduleDynamic(node) {
2916
+ let job = dynamicJobs.get(node);
2917
+ if (!job) {
2918
+ job = {
2919
+ depth: node.depth,
2920
+ run: () => rerenderDynamic(node),
2921
+ cancel: () => node.computation.dropScheduled(),
2922
+ get skip() {
2923
+ return node.computation.disposed || !node.computation.stale;
2924
+ }
2925
+ };
2926
+ dynamicJobs.set(node, job);
2927
+ }
2928
+ schedule(job, "render");
2929
+ }
2930
+ function rerenderDynamic(node) {
2931
+ const computation = node.computation;
2932
+ if (computation.disposed || !computation.stale) return;
2933
+ runPass(node.instance.parent, (pass) => {
2934
+ updateDynamic(createRenderContext(pass, node.instance, namespaceAt(node.parent)), node);
2935
+ });
2936
+ }
2937
+ /** Bind standalone updates when a DOM root is created. */
2938
+ function installRenderUpdates() {
2939
+ setRenderHost({
2940
+ rerender: rerenderInstance,
2941
+ isHydrating: isHydratingRender,
2942
+ deferHydration: deferHydratingRender
2943
+ });
2944
+ setDynamicUpdateScheduler(scheduleDynamic);
2945
+ }
2946
+ //#endregion
2947
+ //#region src/core/dom/root.ts
2948
+ /**
2949
+ * A root renders a value into a container element and owns everything it
2950
+ * renders.
2951
+ *
2952
+ * `prepare()` runs the render phase and returns work that is either
2953
+ * committed or discarded as a unit, so a caller updating several roots (a
2954
+ * navigation) can commit all of them or none. `render()` prepares and
2955
+ * commits in one step.
2956
+ */
2957
+ function createRoot(container, options = {}) {
2958
+ installRenderUpdates();
2959
+ const owner = new Owner(options.owner === void 0 ? getOwner() : options.owner);
2960
+ (owner.context ??= /* @__PURE__ */ new Map()).set(EVENT_ROOT_CONTAINER, container);
2961
+ const node = {
2962
+ kind: 5,
2963
+ el: container,
2964
+ children: [],
2965
+ parent: null,
2966
+ key: void 0,
2967
+ tail: options.before ?? null
2968
+ };
2969
+ let mounted = false;
2970
+ let hydrate = options.hydrate === true;
2971
+ let releaseEvents = null;
2972
+ function prepare(value) {
2973
+ const pass = new Pass();
2974
+ const fresh = !mounted;
2975
+ const tail = node.tail?.parentNode === container ? node.tail : null;
2976
+ const cursor = fresh && hydrate ? new HydrationCursor(tail) : null;
2977
+ const ctx = createRenderContext(pass, owner, namespaceAt(node), cursor ? {
2978
+ cursor,
2979
+ container
2980
+ } : null);
2981
+ try {
2982
+ pass.run(() => runWithOwner(owner, () => {
2983
+ if (!fresh) {
2984
+ reconcileChildren(ctx, node, value, false);
2985
+ return;
2986
+ }
2987
+ const children = reconcileChildren(ctx, node, value, true);
2988
+ for (const deferred of cursor?.deferred ?? []) deferred.render();
2989
+ const adopting = ctx.hydrate !== null;
2990
+ pass.op(() => {
2991
+ const before = node.tail?.parentNode === container ? node.tail : null;
2992
+ const dom = children.flatMap((child) => collectDom(child));
2993
+ if (adopting) {
2994
+ const previous = node.children;
2995
+ pass.onReversibleCommit(() => {
2996
+ node.children = previous;
2997
+ });
2998
+ syncChildren(pass, container, dom, before);
2999
+ node.children = children;
3000
+ return;
3001
+ }
3002
+ const previousChildren = node.children;
3003
+ if (!before) {
3004
+ const replaced = Array.from(container.childNodes);
3005
+ pass.onReversibleCommit(() => {
3006
+ try {
3007
+ restoreRootChildren(container, replaced);
3008
+ } finally {
3009
+ node.children = previousChildren;
3010
+ }
3011
+ });
3012
+ try {
3013
+ container.replaceChildren(...dom);
3014
+ } catch (error) {
3015
+ if (error instanceof CommitMutationError) throw error;
3016
+ throw new CommitMutationError(error);
3017
+ }
3018
+ node.children = children;
3019
+ return;
3020
+ }
3021
+ const previous = Array.from(container.childNodes);
3022
+ pass.onReversibleCommit(() => {
3023
+ try {
3024
+ restoreRootChildren(container, previous);
3025
+ } finally {
3026
+ node.children = previousChildren;
3027
+ }
3028
+ });
3029
+ try {
3030
+ for (const item of dom) container.insertBefore(item, before);
3031
+ } catch (error) {
3032
+ if (error instanceof CommitMutationError) throw error;
3033
+ throw new CommitMutationError(error);
3034
+ }
3035
+ node.children = children;
3036
+ });
3037
+ }));
3038
+ } catch (error) {
3039
+ for (const failure of pass.discard()) reportUncaughtErrorLater(failure);
3040
+ throw clarifyRenderOverflow(error);
3041
+ }
3042
+ let settled = false;
3043
+ let phase = "prepared";
3044
+ let registeredEventsForPass = false;
3045
+ const apply = () => {
3046
+ if (settled || phase !== "prepared") return;
3047
+ try {
3048
+ if (!releaseEvents) {
3049
+ releaseEvents = registerEventRoot(container);
3050
+ registeredEventsForPass = true;
3051
+ }
3052
+ pass.apply();
3053
+ phase = "applied";
3054
+ } catch (error) {
3055
+ phase = "discarded";
3056
+ settled = true;
3057
+ for (const failure of pass.discard()) reportUncaughtErrorLater(failure);
3058
+ if (registeredEventsForPass) {
3059
+ try {
3060
+ releaseEvents?.();
3061
+ } catch (cleanupError) {
3062
+ reportUncaughtErrorLater(cleanupError);
3063
+ }
3064
+ releaseEvents = null;
3065
+ registeredEventsForPass = false;
3066
+ }
3067
+ throw error;
3068
+ }
3069
+ };
3070
+ const publish = () => {
3071
+ if (settled) return;
3072
+ if (phase === "prepared") apply();
3073
+ if (phase !== "applied") throw new Error("[Askr] Cannot publish a render that was not applied.");
3074
+ settled = true;
3075
+ phase = "published";
3076
+ try {
3077
+ pass.publish();
3078
+ } finally {
3079
+ mounted = true;
3080
+ hydrate = false;
3081
+ }
3082
+ };
3083
+ const rollback = () => {
3084
+ if (settled || phase === "discarded") return [];
3085
+ settled = true;
3086
+ phase = "discarded";
3087
+ const errors = pass.rollback();
3088
+ if (registeredEventsForPass) {
3089
+ try {
3090
+ releaseEvents?.();
3091
+ } catch (error) {
3092
+ errors.push(error);
3093
+ }
3094
+ releaseEvents = null;
3095
+ registeredEventsForPass = false;
3096
+ }
3097
+ return errors;
3098
+ };
3099
+ const discard = () => {
3100
+ if (settled || phase === "discarded") return [];
3101
+ if (phase === "applied") return rollback();
3102
+ settled = true;
3103
+ phase = "discarded";
3104
+ return pass.discard();
3105
+ };
3106
+ return {
3107
+ apply,
3108
+ publish,
3109
+ commit() {
3110
+ apply();
3111
+ publish();
3112
+ },
3113
+ rollback,
3114
+ discard,
3115
+ get aborted() {
3116
+ return pass.commitAborted;
3117
+ }
3118
+ };
3119
+ }
3120
+ return {
3121
+ node,
3122
+ owner,
3123
+ prepare,
3124
+ render(value) {
3125
+ prepare(value).commit();
3126
+ },
3127
+ dispose() {
3128
+ const errors = [];
3129
+ for (const child of node.children) {
3130
+ for (const dom of collectDom(child)) dom.parentNode?.removeChild(dom);
3131
+ domNodes.release(child, errors);
3132
+ }
3133
+ node.children = [];
3134
+ mounted = false;
3135
+ owner.dispose(errors);
3136
+ releaseEvents?.();
3137
+ releaseEvents = null;
3138
+ return errors;
3139
+ }
3140
+ };
3141
+ }
3142
+ function restoreRootChildren(container, previous) {
3143
+ for (let index = 0; index < previous.length; index += 1) {
3144
+ const current = container.childNodes[index] ?? null;
3145
+ if (current !== previous[index]) container.insertBefore(previous[index], current);
3146
+ }
3147
+ while (container.childNodes.length > previous.length) container.removeChild(container.lastChild);
3148
+ }
3149
+ //#endregion
3150
+ //#region src/boot/root-element.ts
3151
+ function resolveRootElement(root) {
3152
+ if (typeof root !== "string") return root;
3153
+ const id = root.startsWith("#") ? root.slice(1) : root;
3154
+ return document.getElementById(id);
3155
+ }
3156
+ //#endregion
3157
+ //#region src/boot/root-lifecycle.ts
3158
+ /**
3159
+ * Application roots: one per mounted container element.
3160
+ *
3161
+ * An application root owns a core render root, its application runtime (route
3162
+ * data, data runtime), its default portal, and its cleanup callbacks. The
3163
+ * rendered tree is `RootView`, which provides the application runtime and
3164
+ * renders the current route handler followed by the automatic default portal
3165
+ * host. The root view keeps shared route shells in place across navigation.
3166
+ */
3167
+ var AppRoot = class {
3168
+ constructor(element, component, hydrate) {
3169
+ this.cleanupStrict = false;
3170
+ this.generation = 0;
3171
+ this.routed = false;
3172
+ this.callbacks = /* @__PURE__ */ new Set();
3173
+ this.element = element;
3174
+ this.root = createRoot(element, {
3175
+ owner: null,
3176
+ hydrate
3177
+ });
3178
+ provideDefaultPortal(this.root.owner, element);
3179
+ this.component = component;
3180
+ this.handler = component;
3181
+ }
3182
+ view() {
3183
+ return {
3184
+ $$typeof: ELEMENT_TYPE,
3185
+ type: RootView,
3186
+ props: {
3187
+ handler: this.handler,
3188
+ runtime: this.appRuntime
3189
+ },
3190
+ key: this.routed ? 0 : this.generation
3191
+ };
3192
+ }
3193
+ };
3194
+ function RootView(props, context) {
3195
+ provideAppRuntime(props.runtime);
3196
+ const instance = currentComponent();
3197
+ if (instance) instance.name = props.handler.name || null;
3198
+ return props.handler({}, context);
3199
+ }
3200
+ const appRoots = /* @__PURE__ */ new WeakMap();
3201
+ let routedRootCount = 0;
3202
+ function registerRootCleanupCallback(rootElement, callback) {
3203
+ const app = appRoots.get(rootElement);
3204
+ if (!app) return () => {};
3205
+ app.callbacks.add(callback);
3206
+ return () => app.callbacks.delete(callback);
3207
+ }
3208
+ function disposeAppRoot(app) {
3209
+ if (appRoots.get(app.element) !== app) return;
3210
+ appRoots.delete(app.element);
3211
+ restoreInnerHTML(app.element);
3212
+ const errors = app.root.dispose();
3213
+ for (const callback of Array.from(app.callbacks)) try {
3214
+ callback();
3215
+ } catch (error) {
3216
+ errors.push(error);
3217
+ }
3218
+ app.callbacks.clear();
3219
+ unregisterAppInstance(app);
3220
+ if (app.routed) {
3221
+ app.routed = false;
3222
+ routedRootCount--;
3223
+ if (routedRootCount === 0) clearRouteState();
3224
+ }
3225
+ if (errors.length === 0) return;
3226
+ if (app.cleanupStrict) throw new AggregateError(errors, "cleanup failed for app root");
3227
+ reportUncaughtErrorLater(errors.length === 1 ? errors[0] : new AggregateError(errors, "cleanup failed for app root"));
3228
+ }
3229
+ /**
3230
+ * Clearing a root's markup with `innerHTML = ''` tears the app down first,
3231
+ * so its lifetimes end with the DOM they own.
3232
+ */
3233
+ function interceptInnerHTML(app) {
3234
+ const element = app.element;
3235
+ const descriptor = Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML") ?? Object.getOwnPropertyDescriptor(Object.getPrototypeOf(element), "innerHTML");
3236
+ if (!descriptor?.set || !descriptor.get) return;
3237
+ try {
3238
+ Object.defineProperty(element, "innerHTML", {
3239
+ configurable: true,
3240
+ get() {
3241
+ return descriptor.get.call(this);
3242
+ },
3243
+ set(value) {
3244
+ if (value === "" && appRoots.get(this) === app) {
3245
+ disposeAppRoot(app);
3246
+ if (appRoots.has(this)) return;
3247
+ }
3248
+ descriptor.set.call(this, value);
3249
+ }
3250
+ });
3251
+ } catch {}
3252
+ }
3253
+ function restoreInnerHTML(element) {
3254
+ if (Object.prototype.hasOwnProperty.call(element, "innerHTML")) Reflect.deleteProperty(element, "innerHTML");
3255
+ }
3256
+ /** Mount `component` at `rootElement`, or re-render the app already there. */
3257
+ function mountOrUpdate(rootElement, component, options) {
3258
+ ensureBrowserRuntime();
3259
+ const nonce = validateCspNonce(options?.cspNonce);
3260
+ let app = appRoots.get(rootElement);
3261
+ const replacingIsland = app !== void 0 && app.component !== component && !app.routed;
3262
+ if (!app) {
3263
+ app = new AppRoot(rootElement, component, options?.hydrate === true);
3264
+ appRoots.set(rootElement, app);
3265
+ interceptInnerHTML(app);
3266
+ } else if (app.component !== component) app.generation++;
3267
+ app.component = component;
3268
+ app.handler = wrapRootRouteHandler(component, nonce);
3269
+ app.cspNonce = nonce;
3270
+ app.appRuntime = options?.appRuntime;
3271
+ if (typeof options?.cleanupStrict === "boolean") app.cleanupStrict = options.cleanupStrict;
3272
+ app.root.render(app.view());
3273
+ options?.onCommit?.();
3274
+ flushSync();
3275
+ if (replacingIsland) {
3276
+ const callbacks = Array.from(app.callbacks);
3277
+ app.callbacks.clear();
3278
+ const errors = [];
3279
+ for (const callback of callbacks) try {
3280
+ callback();
3281
+ } catch (error) {
3282
+ errors.push(error);
3283
+ }
3284
+ if (errors.length > 0) {
3285
+ if (app.cleanupStrict) throw new AggregateError(errors, "cleanup failed for app root");
3286
+ reportUncaughtErrorLater(errors.length === 1 ? errors[0] : new AggregateError(errors, "cleanup failed for app root"));
3287
+ }
3288
+ }
3289
+ return app;
3290
+ }
3291
+ async function registerAppNavigation(rootElement, path, source) {
3292
+ ensureBrowserRuntime();
3293
+ const app = appRoots.get(rootElement);
3294
+ if (!app) throw new Error("Internal error: app instance missing");
3295
+ if (!app.routed) {
3296
+ app.routed = true;
3297
+ routedRootCount++;
3298
+ }
3299
+ registerAppInstance(app, path, source);
3300
+ initializeNavigation();
3301
+ }
3302
+ function activateHydrationBoundary(rootElement, boundary) {
3303
+ const app = appRoots.get(rootElement);
3304
+ if (!app || !rootElement.contains(boundary)) return false;
3305
+ const host = dormantHostFor(boundary);
3306
+ if (!host) return false;
3307
+ let parent = host.parent;
3308
+ while (parent && parent.kind !== 5) parent = parent.parent;
3309
+ if (parent !== app.root.node) return false;
3310
+ const pass = new Pass();
3311
+ try {
3312
+ pass.run(() => hydrateDormantHost(pass, host));
3313
+ } catch (error) {
3314
+ for (const failure of pass.discard()) reportUncaughtErrorLater(failure);
3315
+ throw clarifyRenderOverflow(error);
3316
+ }
3317
+ pass.commit();
3318
+ flushSync();
3319
+ return true;
3320
+ }
3321
+ /**
3322
+ * Tear down the app mounted at `root`: its content, lifetimes, and cleanup
3323
+ * callbacks, and its route registration.
3324
+ */
3325
+ function cleanupApp(root) {
3326
+ const rootElement = resolveRootElement(root);
3327
+ if (!rootElement) return;
3328
+ const app = appRoots.get(rootElement);
3329
+ if (app) disposeAppRoot(app);
3330
+ }
3331
+ /** Check whether an app is currently mounted at `root`. */
3332
+ function hasApp(root) {
3333
+ const rootElement = resolveRootElement(root);
3334
+ return rootElement ? appRoots.has(rootElement) : false;
3335
+ }
3336
+ //#endregion
3337
+ //#region src/boot/islands.ts
3338
+ /**
3339
+ * createIsland: Enhances existing DOM (no router, mounts once)
3340
+ */
3341
+ function createIsland(config) {
3342
+ assertExecutionModel("islands");
3343
+ if (!config || typeof config !== "object") throw new Error("createIsland requires a config object");
3344
+ if (typeof config.component !== "function") throw new Error("createIsland: component must be a function");
3345
+ validateCspNonce(config.cspNonce);
3346
+ const rootElement = resolveRootElement(config.root);
3347
+ if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
3348
+ if ("routes" in config) throw new Error("createIsland does not accept routes; use createSPA for routed apps");
3349
+ if (hasRegisteredRoutes()) throw new Error("Routes are not supported with islands. Use createSPA or hydrateSPA with a route registry instead.");
3350
+ mountOrUpdate(rootElement, config.component, {
3351
+ cleanupStrict: config.cleanupStrict,
3352
+ cspNonce: config.cspNonce,
3353
+ appRuntime: createAppRenderRuntime({ dataRuntime: config.dataRuntime ?? getDefaultDataRuntime() })
3354
+ });
3355
+ }
3356
+ /**
3357
+ * createIslands: Enhances one or more existing DOM roots (no router).
3358
+ * The only public islands constructor.
3359
+ */
3360
+ function createIslands(config) {
3361
+ assertExecutionModel("islands");
3362
+ if (!config || typeof config !== "object") throw new Error("createIslands requires a config object");
3363
+ validateCspNonce(config.cspNonce);
3364
+ if (!Array.isArray(config.islands) || config.islands.length === 0) throw new Error("createIslands requires a non-empty islands array");
3365
+ for (const island of config.islands) createIsland({
3366
+ ...island,
3367
+ cspNonce: island.cspNonce ?? config.cspNonce
3368
+ });
3369
+ }
3370
+ //#endregion
3371
+ //#region src/boot/route-startup.ts
3372
+ const MAX_INITIAL_ROUTE_REDIRECTS = 20;
3373
+ function bindResolvedRouteHandler(resolved) {
3374
+ return () => resolved.handler(resolved.params);
3375
+ }
3376
+ function createDeniedStatusNode(status) {
3377
+ return {
3378
+ type: "div",
3379
+ props: { "data-route-denied": String(status) },
3380
+ children: [String(status)]
3381
+ };
3382
+ }
3383
+ function bindDeniedStatus(status) {
3384
+ return () => createDeniedStatusNode(status);
3385
+ }
3386
+ function bindDeniedRouteHandler(status) {
3387
+ return () => createDeniedStatusNode(status);
3388
+ }
3389
+ async function reconcileInitialRouteMetadata(resolved) {
3390
+ if (resolved?.kind !== "render" || !resolved.record) return;
3391
+ if (!resolved.record.options.title && !resolved.record.options.meta && !resolved.record.metaChain?.length) return;
3392
+ const context = getRouteRenderContext(resolved);
3393
+ if (!context) return;
3394
+ reconcileRouteMeta(await resolveRouteMeta(resolved.record, context));
3395
+ }
3396
+ /**
3397
+ * Resolve the route for the current location, following redirects (and
3398
+ * replacing the history entry for each) until a render, deny, or no-match
3399
+ * result is reached. Returns `null` when a redirect leaves the origin: the
3400
+ * browser is loading that document, so there is nothing to mount.
3401
+ */
3402
+ async function resolveInitialRoute(auth, source) {
3403
+ let path = typeof window !== "undefined" ? window.location.pathname : "/";
3404
+ let href = typeof window !== "undefined" ? `${window.location.pathname}${window.location.search}${window.location.hash}` : path;
3405
+ const visited = /* @__PURE__ */ new Set();
3406
+ for (let redirects = 0; redirects <= MAX_INITIAL_ROUTE_REDIRECTS; redirects++) {
3407
+ if (visited.has(href)) throw new Error(`[Askr] Route redirect cycle detected at ${href}.`);
3408
+ visited.add(href);
3409
+ if (!source?.registry) throw new TypeError("resolveInitialRoute requires a route registry.");
3410
+ const resolved = await resolveRouteRequest(href, {
3411
+ registry: source.registry,
3412
+ auth,
3413
+ load: source.load,
3414
+ authContext: source.authContext,
3415
+ dataRuntime: source.dataRuntime
3416
+ });
3417
+ if (!resolved || resolved.kind !== "redirect") return {
3418
+ path,
3419
+ href,
3420
+ resolved
3421
+ };
3422
+ const base = typeof window !== "undefined" ? window.location.href : new URL(href, "http://localhost").href;
3423
+ const redirectTarget = resolveNavigationUrl(resolved.to, base);
3424
+ if (redirectTarget.origin !== new URL(base).origin) {
3425
+ if (typeof window !== "undefined") loadDocument(redirectTarget.href, "replace");
3426
+ return null;
3427
+ }
3428
+ const redirectHref = `${redirectTarget.pathname}${redirectTarget.search}${redirectTarget.hash}`;
3429
+ if (typeof window !== "undefined") window.history.replaceState({
3430
+ path: redirectHref,
3431
+ askrIndex: readHistoryIndex(window.history.state)
3432
+ }, "", redirectTarget.href);
3433
+ path = redirectTarget.pathname;
3434
+ href = redirectHref;
3435
+ }
3436
+ throw new Error(`[Askr] Route redirect limit exceeded (${MAX_INITIAL_ROUTE_REDIRECTS}).`);
3437
+ }
3438
+ //#endregion
3439
+ //#region src/boot/spa.ts
3440
+ /**
3441
+ * App bootstrap and mount
3442
+ */
3443
+ /**
3444
+ * createSPA: Initializes the router and mounts the app with an explicit route registry.
3445
+ * ```ts
3446
+ * import { createRouteRegistry } from '@askrjs/askr/router';
3447
+ * const registry = createRouteRegistry(() => { ... });
3448
+ * await createSPA({ root: '#app', registry });
3449
+ * ```
3450
+ *
3451
+ */
3452
+ async function createSPA(config) {
3453
+ assertExecutionModel("spa");
3454
+ if (!config || typeof config !== "object") throw new Error("createSPA requires a config object");
3455
+ validateCspNonce(config.cspNonce);
3456
+ if (!config.registry) throw new Error("createSPA requires a route registry.");
3457
+ const { manifest } = config.registry;
3458
+ if (manifest.records.length === 0) throw new Error("createSPA requires a route registry with at least one route. If you are enhancing existing HTML, use createIsland instead.");
3459
+ const rootElement = resolveRootElement(config.root);
3460
+ if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
3461
+ configureScrollRestoration(config.scrollRestoration);
3462
+ clearRouteState();
3463
+ _applyManifest(manifest);
3464
+ const routeAuth = config.auth ?? manifest.auth;
3465
+ const dataRuntime = config.dataRuntime ?? getDefaultDataRuntime();
3466
+ const appRouteSource = {
3467
+ registry: config.registry,
3468
+ auth: routeAuth
3469
+ };
3470
+ _setActiveRouteAuthOptions(routeAuth);
3471
+ if (isProductionEnvironment()) lockRouteRegistration();
3472
+ const initialRoute = await resolveInitialRoute(routeAuth, {
3473
+ registry: config.registry,
3474
+ dataRuntime
3475
+ });
3476
+ if (!initialRoute) return;
3477
+ const { path, href: currentUrl, resolved } = initialRoute;
3478
+ setServerLocation(currentUrl);
3479
+ const appRuntime = createAppRenderRuntime({
3480
+ ...resolved?.kind === "render" && hasRouteRenderData(resolved) ? {
3481
+ route: getRouteRenderData(resolved),
3482
+ hasRoute: true
3483
+ } : {},
3484
+ dataRuntime,
3485
+ routeRegistry: config.registry,
3486
+ routeAuth
3487
+ });
3488
+ if (!resolved) {
3489
+ mountOrUpdate(rootElement, () => ({
3490
+ type: "div",
3491
+ children: []
3492
+ }), {
3493
+ cleanupStrict: config.cleanupStrict,
3494
+ appRuntime,
3495
+ cspNonce: config.cspNonce
3496
+ });
3497
+ await registerAppNavigation(rootElement, path, {
3498
+ ...appRouteSource,
3499
+ runtime: appRuntime
3500
+ });
3501
+ return;
3502
+ }
3503
+ await reconcileInitialRouteMetadata(resolved);
3504
+ mountOrUpdate(rootElement, resolved.kind === "deny" ? bindDeniedStatus(resolved.status) : bindResolvedRouteHandler({
3505
+ handler: resolved.handler,
3506
+ params: resolved.params
3507
+ }), {
3508
+ cleanupStrict: config.cleanupStrict,
3509
+ appRuntime,
3510
+ cspNonce: config.cspNonce
3511
+ });
3512
+ await registerAppNavigation(rootElement, path, {
3513
+ ...appRouteSource,
3514
+ runtime: appRuntime
3515
+ });
3516
+ }
3517
+ //#endregion
3518
+ //#region src/boot/hydration.ts
3519
+ const DEFERRED_PAYLOAD = "__askr_deferred__";
3520
+ const SSR_STYLE_REGISTRY_ATTR = "data-askr-style-registry";
3521
+ function applyDeferredStreamPatches(rootElement) {
3522
+ const patches = Array.from(rootElement.querySelectorAll("template[data-askr-deferred-patch]"));
3523
+ for (const template of patches) {
3524
+ const id = template.getAttribute("data-askr-deferred-patch");
3525
+ if (!id) continue;
3526
+ const boundary = Array.from(rootElement.querySelectorAll("askr-resolve[data-askr-deferred]")).find((candidate) => candidate.getAttribute("data-askr-deferred") === id);
3527
+ if (boundary) boundary.replaceWith(template.content.cloneNode(true));
3528
+ template.remove();
3529
+ for (const script of Array.from(rootElement.querySelectorAll("script[data-askr-deferred-apply]"))) if (script.getAttribute("data-askr-deferred-apply") === id) script.remove();
3530
+ }
3531
+ }
3532
+ function hydrationReviver(_key, value) {
3533
+ if (!value || typeof value !== "object") return value;
3534
+ const payload = value;
3535
+ if (payload[DEFERRED_PAYLOAD] === "fulfilled") return reviveDeferredValue("fulfilled", payload.value, void 0);
3536
+ if (payload[DEFERRED_PAYLOAD] === "rejected") return reviveDeferredValue("rejected", void 0, payload.error);
3537
+ return value;
3538
+ }
3539
+ function parseHydrationRenderData(raw) {
3540
+ const value = JSON.parse(raw);
3541
+ if (!isPageRenderEnvelope(value) || !Object.prototype.hasOwnProperty.call(value, "deferredPaths")) return JSON.parse(raw, hydrationReviver);
3542
+ const payload = value;
3543
+ const paths = payload.deferredPaths;
3544
+ if (!Array.isArray(paths) || paths.some((path) => !Array.isArray(path) || path.some((key) => typeof key !== "string"))) throw new TypeError("Invalid deferred hydration locations.");
3545
+ for (let index = paths.length - 1; index >= 0; index--) {
3546
+ const path = paths[index];
3547
+ let parent = value;
3548
+ for (const key of path.slice(0, -1)) {
3549
+ if (!parent || typeof parent !== "object" || !Object.prototype.hasOwnProperty.call(parent, key)) throw new TypeError("Missing deferred hydration location.");
3550
+ parent = parent[key];
3551
+ }
3552
+ const key = path[path.length - 1];
3553
+ if (path.length === 0 || !parent || typeof parent !== "object" || !Object.prototype.hasOwnProperty.call(parent, key)) throw new TypeError("Missing deferred hydration location.");
3554
+ const record = parent;
3555
+ Object.defineProperty(record, key, { value: hydrationReviver(key, record[key]) });
3556
+ }
3557
+ delete payload.deferredPaths;
3558
+ return payload;
3559
+ }
3560
+ /**
3561
+ * Relocate the SSR style registry carrier out of the hydration root.
3562
+ *
3563
+ * `@askrjs/server` prepends the collected styles to the page body, so they land
3564
+ * as a direct child of the mount root. That extra element makes the root's
3565
+ * child list disagree with the rendered tree, which costs the app in-place
3566
+ * adoption and discards the carried CSS during reconciliation. The carrier is
3567
+ * transport rather than application markup, so move it into `<head>`, where it
3568
+ * still applies and no longer participates in reconciliation.
3569
+ *
3570
+ * Deferred style patches resolve the carrier with a document-wide query, so
3571
+ * they keep working from its new position.
3572
+ */
3573
+ function adoptSsrStyleCarriers(rootElement) {
3574
+ const head = document.head;
3575
+ if (!head) return;
3576
+ for (const child of Array.from(rootElement.children)) if (child instanceof HTMLStyleElement && child.hasAttribute(SSR_STYLE_REGISTRY_ATTR)) head.append(child);
3577
+ }
3578
+ function takeHydrationRenderData(rootElement) {
3579
+ for (const child of Array.from(rootElement.children)) if (child instanceof HTMLScriptElement && child.getAttribute("data-askr-render-data") === "true") {
3580
+ const raw = child.textContent ?? "";
3581
+ child.remove();
3582
+ if (!raw) return pageRenderEnvelope(void 0);
3583
+ try {
3584
+ return pageRenderEnvelope(parseHydrationRenderData(raw));
3585
+ } catch (err) {
3586
+ const error = /* @__PURE__ */ new Error("[Askr] Failed to parse embedded SSR render data during hydration.");
3587
+ error.cause = err;
3588
+ throw error;
3589
+ }
3590
+ }
3591
+ return null;
3592
+ }
3593
+ function markSkippedElements(root, skipSelectors) {
3594
+ if (skipSelectors.length === 0) return;
3595
+ const selectorList = Array.from(new Set(skipSelectors)).join(", ");
3596
+ root.querySelectorAll(selectorList).forEach((el) => el.setAttribute("data-skip-hydrate", "true"));
3597
+ }
3598
+ function collectDeferredBelowFoldBoundaries(root, foldY) {
3599
+ const boundaries = [];
3600
+ const stack = [];
3601
+ for (let index = root.children.length - 1; index >= 0; index -= 1) stack.push(root.children[index]);
3602
+ while (stack.length > 0) {
3603
+ const element = stack.pop();
3604
+ if (element.hasAttribute("data-skip-hydrate")) continue;
3605
+ if (element.getBoundingClientRect().top >= foldY) {
3606
+ element.setAttribute("data-skip-hydrate", "true");
3607
+ boundaries.push(element);
3608
+ continue;
3609
+ }
3610
+ for (let index = element.children.length - 1; index >= 0; index -= 1) stack.push(element.children[index]);
3611
+ }
3612
+ return boundaries;
3613
+ }
3614
+ function activateVisibleDeferredBoundaries(root, boundaries, foldY, activate, reportedDepthErrors) {
3615
+ let activated = false;
3616
+ let remaining = 0;
3617
+ for (const element of boundaries) {
3618
+ if (!element.hasAttribute("data-skip-hydrate")) continue;
3619
+ if (element.getBoundingClientRect().top < foldY) try {
3620
+ if (activate(root, element)) {
3621
+ activated = true;
3622
+ reportedDepthErrors.delete(element);
3623
+ }
3624
+ } catch (error) {
3625
+ const clarified = clarifyRenderOverflow(error);
3626
+ if (clarified instanceof RenderDepthError && !reportedDepthErrors.has(element)) {
3627
+ reportedDepthErrors.add(element);
3628
+ reportUncaughtErrorLater(clarified);
3629
+ }
3630
+ }
3631
+ if (element.hasAttribute("data-skip-hydrate")) remaining += 1;
3632
+ }
3633
+ return {
3634
+ activated,
3635
+ remaining
3636
+ };
3637
+ }
3638
+ function queueIdleWork(work) {
3639
+ return new Promise((resolve, reject) => {
3640
+ const run = () => {
3641
+ try {
3642
+ work();
3643
+ resolve();
3644
+ } catch (error) {
3645
+ reject(error);
3646
+ }
3647
+ };
3648
+ if (typeof requestIdleCallback !== "undefined") {
3649
+ requestIdleCallback(run, { timeout: 2e3 });
3650
+ return;
3651
+ }
3652
+ setTimeout(run, 0);
3653
+ });
3654
+ }
3655
+ function shouldVerifyHydrationMarkup(config) {
3656
+ const explicit = config.hydrate?.verifyMarkup;
3657
+ if (typeof explicit === "boolean") return explicit;
3658
+ return !isProductionEnvironment();
3659
+ }
3660
+ async function applySelectiveHydration(rootElement, resolved, path, cleanupStrict, hydrateOptions, source, hooks, interactionReplay) {
3661
+ const hasBelowFoldDeferral = !!hydrateOptions.deferBelowFold;
3662
+ let releaseSelectiveHydrationResources = () => {};
3663
+ let registerSelectiveHydrationCleanup = () => {};
3664
+ if (hydrateOptions.skipSelectors?.length) markSkippedElements(rootElement, hydrateOptions.skipSelectors);
3665
+ let deferredBoundaries = [];
3666
+ const reportedDepthErrors = /* @__PURE__ */ new WeakSet();
3667
+ if (hydrateOptions.deferBelowFold) {
3668
+ const foldY = hydrateOptions.foldThreshold ?? window.innerHeight;
3669
+ deferredBoundaries = collectDeferredBelowFoldBoundaries(rootElement, foldY);
3670
+ interactionReplay.registerDeferredBoundaries(deferredBoundaries);
3671
+ let selectiveHydrationResourcesReleased = false;
3672
+ let unregisterRootCleanupCallback = () => {};
3673
+ function handleScroll() {
3674
+ const { remaining } = activateVisibleDeferredBoundaries(rootElement, deferredBoundaries, foldY, hooks.activateHydrationBoundary, reportedDepthErrors);
3675
+ if (remaining === 0) releaseSelectiveHydrationResources();
3676
+ }
3677
+ releaseSelectiveHydrationResources = () => {
3678
+ if (selectiveHydrationResourcesReleased) return;
3679
+ selectiveHydrationResourcesReleased = true;
3680
+ unregisterRootCleanupCallback();
3681
+ window.removeEventListener("scroll", handleScroll);
3682
+ interactionReplay.clearDeferredBoundaries();
3683
+ };
3684
+ interactionReplay.setOnDeferredBoundariesDrained(releaseSelectiveHydrationResources);
3685
+ registerSelectiveHydrationCleanup = () => {
3686
+ unregisterRootCleanupCallback = hooks.registerRootCleanupCallback(rootElement, releaseSelectiveHydrationResources);
3687
+ };
3688
+ window.addEventListener("scroll", handleScroll, { passive: true });
3689
+ }
3690
+ if (hydrateOptions.deferUntilIdle && !hasBelowFoldDeferral) {
3691
+ await queueIdleWork(() => {
3692
+ hooks.mountOrUpdate(rootElement, (() => resolved.handler(resolved.params)), {
3693
+ cleanupStrict,
3694
+ appRuntime: source?.runtime
3695
+ });
3696
+ });
3697
+ await hooks.registerAppNavigation(rootElement, path, source);
3698
+ return;
3699
+ }
3700
+ try {
3701
+ hooks.mountOrUpdate(rootElement, (() => resolved.handler(resolved.params)), {
3702
+ cleanupStrict,
3703
+ appRuntime: source?.runtime
3704
+ });
3705
+ registerSelectiveHydrationCleanup();
3706
+ await hooks.registerAppNavigation(rootElement, path, source);
3707
+ } catch (error) {
3708
+ releaseSelectiveHydrationResources();
3709
+ throw error;
3710
+ }
3711
+ if (hydrateOptions.deferUntilIdle && deferredBoundaries.length > 0) await queueIdleWork(() => {
3712
+ try {
3713
+ const { remaining } = activateVisibleDeferredBoundaries(rootElement, deferredBoundaries, Number.POSITIVE_INFINITY, hooks.activateHydrationBoundary, reportedDepthErrors);
3714
+ if (remaining === 0) releaseSelectiveHydrationResources();
3715
+ } catch (error) {
3716
+ releaseSelectiveHydrationResources();
3717
+ throw error;
3718
+ }
3719
+ });
3720
+ if (deferredBoundaries.length === 0) releaseSelectiveHydrationResources();
3721
+ }
3722
+ //#endregion
3723
+ //#region src/boot/hydration-interaction-replay.ts
3724
+ const HYDRATION_INTERACTION_TYPES = [
3725
+ "click",
3726
+ "dblclick",
3727
+ "pointerdown",
3728
+ "pointerup",
3729
+ "keydown",
3730
+ "keyup",
3731
+ "input",
3732
+ "change",
3733
+ "submit"
3734
+ ];
3735
+ function cloneInteractionEvent(event) {
3736
+ const EventConstructor = event.constructor;
3737
+ const eventWithDetails = event;
3738
+ const init = {
3739
+ bubbles: event.bubbles,
3740
+ cancelable: event.cancelable,
3741
+ composed: event.composed,
3742
+ detail: eventWithDetails.detail,
3743
+ view: eventWithDetails.view,
3744
+ key: eventWithDetails.key,
3745
+ code: eventWithDetails.code,
3746
+ location: eventWithDetails.location,
3747
+ repeat: eventWithDetails.repeat,
3748
+ isComposing: eventWithDetails.isComposing,
3749
+ ctrlKey: eventWithDetails.ctrlKey,
3750
+ shiftKey: eventWithDetails.shiftKey,
3751
+ altKey: eventWithDetails.altKey,
3752
+ metaKey: eventWithDetails.metaKey,
3753
+ button: eventWithDetails.button,
3754
+ buttons: eventWithDetails.buttons,
3755
+ clientX: eventWithDetails.clientX,
3756
+ clientY: eventWithDetails.clientY,
3757
+ screenX: eventWithDetails.screenX,
3758
+ screenY: eventWithDetails.screenY,
3759
+ pointerId: eventWithDetails.pointerId,
3760
+ pointerType: eventWithDetails.pointerType,
3761
+ isPrimary: eventWithDetails.isPrimary,
3762
+ data: eventWithDetails.data,
3763
+ inputType: eventWithDetails.inputType,
3764
+ submitter: eventWithDetails.submitter
3765
+ };
3766
+ try {
3767
+ return new EventConstructor(event.type, init);
3768
+ } catch {
3769
+ return new Event(event.type, {
3770
+ bubbles: event.bubbles,
3771
+ cancelable: event.cancelable,
3772
+ composed: event.composed
3773
+ });
3774
+ }
3775
+ }
3776
+ /**
3777
+ * Capture discrete interactions before hydrated handlers exist, then replay
3778
+ * them after the listener transaction commits. Deferred boundaries remain
3779
+ * interaction-activatable until they are revealed or the root is cleaned up.
3780
+ */
3781
+ function beginHydrationInteractionReplay(root, activateBoundary, permanentSkipSelectors = []) {
3782
+ const queued = [];
3783
+ const deferredBoundaries = /* @__PURE__ */ new Set();
3784
+ let active = true;
3785
+ let hydratingRoot = true;
3786
+ let replaying = false;
3787
+ let hadDeferredBoundaries = false;
3788
+ let onDeferredBoundariesDrained;
3789
+ function isPermanentlySkipped(target) {
3790
+ const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
3791
+ if (!element) return false;
3792
+ return permanentSkipSelectors.some((selector) => {
3793
+ try {
3794
+ return element.closest(selector) !== null;
3795
+ } catch {
3796
+ return false;
3797
+ }
3798
+ });
3799
+ }
3800
+ function findDeferredBoundary(target) {
3801
+ if (!(target instanceof Node)) return;
3802
+ for (const boundary of deferredBoundaries) if (boundary.hasAttribute("data-skip-hydrate") && boundary.contains(target)) return boundary;
3803
+ }
3804
+ function removeCaptureListeners() {
3805
+ if (!active) return;
3806
+ active = false;
3807
+ for (const type of HYDRATION_INTERACTION_TYPES) root.removeEventListener(type, captureInteraction, true);
3808
+ }
3809
+ function releaseIfFinished() {
3810
+ if (!hydratingRoot && deferredBoundaries.size === 0) removeCaptureListeners();
3811
+ }
3812
+ function notifyIfDeferredBoundariesDrained() {
3813
+ for (const boundary of deferredBoundaries) if (!boundary.hasAttribute("data-skip-hydrate") || !root.contains(boundary)) deferredBoundaries.delete(boundary);
3814
+ if (hadDeferredBoundaries && deferredBoundaries.size === 0) onDeferredBoundariesDrained?.();
3815
+ }
3816
+ function replayInteraction(interaction) {
3817
+ if (!(interaction.target instanceof Node) || !root.contains(interaction.target)) return;
3818
+ const boundary = findDeferredBoundary(interaction.target);
3819
+ if (boundary) {
3820
+ try {
3821
+ if (!activateBoundary(boundary)) return;
3822
+ } catch {
3823
+ return;
3824
+ }
3825
+ deferredBoundaries.delete(boundary);
3826
+ notifyIfDeferredBoundariesDrained();
3827
+ }
3828
+ replaying = true;
3829
+ try {
3830
+ interaction.target.dispatchEvent(interaction.event);
3831
+ } finally {
3832
+ replaying = false;
3833
+ }
3834
+ releaseIfFinished();
3835
+ }
3836
+ function captureInteraction(event) {
3837
+ if (!active || replaying) return;
3838
+ const target = event.target;
3839
+ if (!target || isPermanentlySkipped(target)) return;
3840
+ const boundary = findDeferredBoundary(target);
3841
+ if (!hydratingRoot && !boundary) return;
3842
+ event.preventDefault();
3843
+ event.stopImmediatePropagation();
3844
+ const interaction = {
3845
+ target,
3846
+ event: cloneInteractionEvent(event)
3847
+ };
3848
+ if (hydratingRoot) {
3849
+ queued.push(interaction);
3850
+ return;
3851
+ }
3852
+ replayInteraction(interaction);
3853
+ }
3854
+ const installedTypes = [];
3855
+ try {
3856
+ for (const type of HYDRATION_INTERACTION_TYPES) {
3857
+ root.addEventListener(type, captureInteraction, true);
3858
+ installedTypes.push(type);
3859
+ }
3860
+ } catch (error) {
3861
+ active = false;
3862
+ for (const type of installedTypes) root.removeEventListener(type, captureInteraction, true);
3863
+ throw error;
3864
+ }
3865
+ return {
3866
+ registerDeferredBoundaries(boundaries) {
3867
+ for (const boundary of boundaries) deferredBoundaries.add(boundary);
3868
+ hadDeferredBoundaries ||= boundaries.length > 0;
3869
+ },
3870
+ setOnDeferredBoundariesDrained(callback) {
3871
+ onDeferredBoundariesDrained = callback;
3872
+ },
3873
+ clearDeferredBoundaries() {
3874
+ deferredBoundaries.clear();
3875
+ releaseIfFinished();
3876
+ },
3877
+ complete() {
3878
+ if (!active || !hydratingRoot) return;
3879
+ hydratingRoot = false;
3880
+ const pending = queued.splice(0);
3881
+ for (const interaction of pending) replayInteraction(interaction);
3882
+ releaseIfFinished();
3883
+ },
3884
+ abort() {
3885
+ queued.length = 0;
3886
+ deferredBoundaries.clear();
3887
+ onDeferredBoundariesDrained = void 0;
3888
+ hydratingRoot = false;
3889
+ removeCaptureListeners();
3890
+ }
3891
+ };
3892
+ }
3893
+ //#endregion
3894
+ //#region src/boot/hydrate-spa.ts
3895
+ /**
3896
+ * App bootstrap and mount
3897
+ */
3898
+ /**
3899
+ * hydrateSPA: Hydrate server-rendered HTML with an explicit route registry.
3900
+ */
3901
+ async function hydrateSPA(config) {
3902
+ assertExecutionModel("spa");
3903
+ if (!config || typeof config !== "object") throw new Error("hydrateSPA requires a config object");
3904
+ validateCspNonce(config.cspNonce);
3905
+ if (!config.registry) throw new Error("hydrateSPA requires a route registry.");
3906
+ const { manifest } = config.registry;
3907
+ if (manifest.records.length === 0) throw new Error("hydrateSPA requires a route registry with at least one route. If you are enhancing existing HTML, use createIsland instead.");
3908
+ const rootElement = resolveRootElement(config.root);
3909
+ if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
3910
+ const interactionReplay = beginHydrationInteractionReplay(rootElement, (boundary) => activateHydrationBoundary(rootElement, boundary), config.hydrate?.skipSelectors);
3911
+ try {
3912
+ applyDeferredStreamPatches(rootElement);
3913
+ adoptSsrStyleCarriers(rootElement);
3914
+ const hydrationRenderData = takeHydrationRenderData(rootElement);
3915
+ const hydrationQueryCache = hydrationRenderData?.queries;
3916
+ const dataRuntime = config.dataRuntime ?? getDefaultDataRuntime();
3917
+ if (hydrationQueryCache) hydrateDataRuntime(dataRuntime, hydrationQueryCache);
3918
+ const hydrationRenderDataForApp = withoutHydratedAuth(hydrationRenderData);
3919
+ configureScrollRestoration(config.scrollRestoration);
3920
+ clearRouteState();
3921
+ _applyManifest(manifest);
3922
+ const hydratedAuth = readHydratedAuth(hydrationRenderData);
3923
+ const routeAuth = config.auth ?? manifest.auth;
3924
+ const appRouteSource = {
3925
+ registry: config.registry,
3926
+ auth: routeAuth,
3927
+ runtime: createAppRenderRuntime({
3928
+ hydrationResources: hydrationRenderDataForApp?.resources,
3929
+ framework: hydrationRenderDataForApp?.framework,
3930
+ route: hydrationRenderData?.route,
3931
+ hasRoute: hydrationRenderData !== null,
3932
+ dataRuntime,
3933
+ routeRegistry: config.registry,
3934
+ routeAuth
3935
+ })
3936
+ };
3937
+ _setActiveRouteAuthOptions(routeAuth);
3938
+ const initialRoute = await resolveInitialRoute(routeAuth, {
3939
+ registry: config.registry,
3940
+ load: false,
3941
+ authContext: hydratedAuth,
3942
+ dataRuntime
3943
+ });
3944
+ if (!initialRoute) {
3945
+ interactionReplay.abort();
3946
+ return;
3947
+ }
3948
+ const { path, href: currentUrl, resolved } = initialRoute;
3949
+ setServerLocation(currentUrl);
3950
+ if (isProductionEnvironment()) lockRouteRegistration();
3951
+ if (!resolved) throw new Error(`hydrateSPA: no route found for current path (${path}).`);
3952
+ await reconcileInitialRouteMetadata(resolved);
3953
+ const hydrationResolved = resolved.kind === "deny" ? {
3954
+ handler: bindDeniedRouteHandler(resolved.status),
3955
+ params: {}
3956
+ } : resolved;
3957
+ let verifyClientMarkup;
3958
+ let clientMarkup = null;
3959
+ const captureClientMarkup = () => {
3960
+ if (verifyClientMarkup) clientMarkup = rootElement.innerHTML;
3961
+ };
3962
+ const mountHydratedRoot = (...args) => mountOrUpdate(args[0], args[1], {
3963
+ ...args[2],
3964
+ cspNonce: config.cspNonce,
3965
+ hydrate: true,
3966
+ onCommit: captureClientMarkup
3967
+ });
3968
+ if (shouldVerifyHydrationMarkup(config)) {
3969
+ const { captureServerHydrationMarkup, verifyClientHydrationMarkup, verifyHydrationSyncForUrl } = await import("./verify-hydration-D_cyn1OX.js");
3970
+ if (!verifyHydrationSyncForUrl({
3971
+ root: rootElement,
3972
+ url: currentUrl,
3973
+ registry: config.registry,
3974
+ resolved: hydrationResolved,
3975
+ options: {
3976
+ data: hydrationRenderDataForApp?.resources,
3977
+ dataRuntime,
3978
+ envelope: hydrationRenderDataForApp ?? void 0,
3979
+ cspNonce: config.cspNonce
3980
+ }
3981
+ })) throw new Error("[Askr] Hydration mismatch detected. Server HTML does not match expected server-render output.");
3982
+ const serverMarkup = captureServerHydrationMarkup(rootElement, currentUrl, hydrationRenderDataForApp ?? void 0);
3983
+ if (serverMarkup !== null) verifyClientMarkup = async () => {
3984
+ await Promise.resolve();
3985
+ if (!(clientMarkup !== null && verifyClientHydrationMarkup(clientMarkup, serverMarkup)) && !verifyClientHydrationMarkup(rootElement.innerHTML, serverMarkup)) throw new Error("[Askr] Hydration mismatch detected between server and client markup.");
3986
+ };
3987
+ }
3988
+ const hydrateOptions = config.hydrate;
3989
+ if (hydrateOptions) {
3990
+ if (hydrateOptions.deferUntilIdle || hydrateOptions.deferBelowFold) {
3991
+ if (hydrationRenderDataForApp) startHydrationRenderPhase(hydrationRenderDataForApp);
3992
+ try {
3993
+ await applySelectiveHydration(rootElement, hydrationResolved, path, config.cleanupStrict, hydrateOptions, appRouteSource, {
3994
+ mountOrUpdate: mountHydratedRoot,
3995
+ registerAppNavigation,
3996
+ registerRootCleanupCallback,
3997
+ activateHydrationBoundary
3998
+ }, interactionReplay);
3999
+ } finally {
4000
+ if (hydrationRenderDataForApp) stopHydrationRenderPhase();
4001
+ }
4002
+ flushSync();
4003
+ if (!rootElement.querySelector("[data-skip-hydrate]")) await verifyClientMarkup?.();
4004
+ interactionReplay.complete();
4005
+ return;
4006
+ }
4007
+ if (hydrateOptions.skipSelectors?.length) markSkippedElements(rootElement, hydrateOptions.skipSelectors);
4008
+ }
4009
+ if (hydrationRenderDataForApp) startHydrationRenderPhase(hydrationRenderDataForApp);
4010
+ try {
4011
+ mountHydratedRoot(rootElement, resolved.kind === "deny" ? bindDeniedStatus(resolved.status) : bindResolvedRouteHandler(hydrationResolved), {
4012
+ cleanupStrict: config.cleanupStrict,
4013
+ appRuntime: appRouteSource.runtime
4014
+ });
4015
+ flushSync();
4016
+ } finally {
4017
+ if (hydrationRenderDataForApp) stopHydrationRenderPhase();
4018
+ }
4019
+ await verifyClientMarkup?.();
4020
+ interactionReplay.complete();
4021
+ await registerAppNavigation(rootElement, path, { ...appRouteSource });
4022
+ } catch (error) {
4023
+ interactionReplay.abort();
4024
+ throw error;
4025
+ }
4026
+ }
4027
+ //#endregion
4028
+ //#region src/boot/index.ts
4029
+ /** Stable bootstrap exports; each mounting mode owns its orchestration. */
4030
+ composeBrowserRuntime();
4031
+ //#endregion
4032
+ export { cleanupApp as a, createIslands as i, createSPA as n, hasApp as o, createIsland as r, hydrateSPA as t };