@askrjs/askr 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +874 -0
- package/README.md +61 -29
- package/capabilities.json +19 -9
- package/dist/actions/index.js +56 -12
- package/dist/benchmark.js +10 -15
- package/dist/boot/index.d.ts +3 -2
- package/dist/boot/index.js +1 -1
- package/dist/boot-DaoTYoCw.js +3989 -0
- package/dist/browser-activity-CB8e-V4f.js +220 -0
- package/dist/components/index.d.ts +8 -1
- package/dist/components/index.js +1 -43
- package/dist/{aria-hKjThHcp.js → compose-ref-DWzdbxsD.js} +41 -4
- package/dist/control/index.d.ts +2 -0
- package/dist/control/index.js +1 -1
- package/dist/control-nfpT2fbK.js +209 -0
- package/dist/core.d.ts +5 -9
- package/dist/csp-nonce-DwGw1A6Q.js +35 -0
- package/dist/data/index.js +3 -3
- package/dist/{data-2edW0jhF.js → data-BCwClJiF.js} +283 -94
- package/dist/data-runtime-C1bFAlba.js +242 -0
- package/dist/{deferred-CbgNhf67.js → deferred-bEEHHvic.js} +42 -20
- package/dist/domains/actions.d.ts +8 -3
- package/dist/domains/component.d.ts +1 -107
- package/dist/domains/context.d.ts +0 -15
- package/dist/domains/control.d.ts +36 -249
- package/dist/domains/data.d.ts +31 -13
- package/dist/domains/diagnostics.d.ts +1 -2
- package/dist/domains/lifecycle.d.ts +2 -5
- package/dist/domains/refs.d.ts +0 -2
- package/dist/domains/routing.d.ts +43 -8
- package/dist/domains/scheduler.d.ts +1 -76
- package/dist/domains/server.d.ts +5 -2
- package/dist/domains/state.d.ts +2 -5
- package/dist/domains/telemetry.d.ts +0 -2
- package/dist/elements.d.ts +214 -51
- package/dist/error-boundary-Bpwofwps.js +293 -0
- package/dist/foundations/icon/index.d.ts +0 -58
- package/dist/foundations/icon/index.js +2 -2
- package/dist/foundations/index.js +2 -1
- package/dist/foundations/interactions/index.js +1 -1
- package/dist/foundations/state/index.d.ts +3 -3
- package/dist/foundations/state/index.js +5 -3
- package/dist/foundations/structures/index.js +2 -1
- package/dist/foundations/utilities/index.d.ts +2 -0
- package/dist/foundations/utilities/index.js +1 -2
- package/dist/fx/index.d.ts +31 -58
- package/dist/fx/index.js +313 -195
- package/dist/hooks-BimKStr4.js +202 -0
- package/dist/index.d.ts +11 -74
- package/dist/index.js +8 -447
- package/dist/instance-BAnfmXOR.js +988 -0
- package/dist/{interactions-CTeACdT_.js → interactions-D2BoREb-.js} +3 -2
- package/dist/jsx-dev-runtime.d.ts +41 -18
- package/dist/jsx-dev-runtime.js +1 -6
- package/dist/{jsx-runtime-CPJXAOBF.js → jsx-runtime-C4idqHY0.js} +18 -7
- package/dist/jsx-runtime.js +1 -1
- package/dist/jsx.d.ts +61 -37
- package/dist/{manifest-D1P3UNWV.js → manifest-CW9gyNBY.js} +3 -3
- package/dist/{navigate-prC5V8XV.js → navigate-CP_0Zy5U.js} +454 -87
- package/dist/navigation.d.ts +12 -3
- package/dist/peer-types.d.ts +8 -0
- package/dist/portal-DuOKfnLa.js +349 -0
- package/dist/promise-Dz8iIHAm.js +16 -0
- package/dist/query-registry-WMCN0BJL.js +320 -0
- package/dist/readable-DFWOsTac.js +21 -0
- package/dist/{render-context-B7ohlh6t.js → render-context-DGHnEveC.js} +27 -11
- package/dist/render-sync-IC5lMFpq.js +1240 -0
- package/dist/resolution-Cx82-xjW.js +1244 -0
- package/dist/resource-CLjB7RBQ.js +377 -0
- package/dist/resources/index.d.ts +21 -11
- package/dist/resources/index.js +45 -244
- package/dist/router/index.d.ts +14 -5
- package/dist/router/index.js +22 -53
- package/dist/scope-CwpVSHVf.js +55 -0
- package/dist/{shared-kU8yvryp.js → shared-O2ZnmYAf.js} +1 -7
- package/dist/{snapshot-source-An4iKajG.js → snapshot-Cjx4N988.js} +6 -1
- package/dist/ssg/index.d.ts +1 -2
- package/dist/ssg/index.js +5 -13
- package/dist/ssr/index.d.ts +26 -28
- package/dist/ssr/index.js +4 -4
- package/dist/{ssr-CiNm6Vzl.js → ssr-BJ8ug7zH.js} +133 -122
- package/dist/ssr-DoV-vJ-5.js +229 -0
- package/dist/state-DWO1KzkX.js +232 -0
- package/dist/structures-D4dDMqHX.js +231 -0
- package/dist/testing/index.d.ts +8 -4
- package/dist/testing/index.js +29 -34
- package/dist/url-CnbfpX0K.js +185 -0
- package/dist/utilities.d.ts +48 -2
- package/dist/verify-hydration-DpVaDnqz.js +152 -0
- package/package.json +19 -9
- package/dist/access-CEA2pOry.js +0 -1046
- package/dist/activity-oy2YFTFF.js +0 -201
- package/dist/boot-BMBMWUGI.js +0 -1084
- package/dist/capabilities-Bbf0mlp9.js +0 -42
- package/dist/compose-ref-v0qGlHoN.js +0 -26
- package/dist/control-BR_mrX__.js +0 -173
- package/dist/control-C-ApnFs1.js +0 -20
- package/dist/csp-nonce-BfFWcd0w.js +0 -173
- package/dist/data-runtime-CxoY98lX.js +0 -149
- package/dist/default-portal-runtime-DvMvWJVp.js +0 -20
- package/dist/dom-renderer.d.ts +0 -77
- package/dist/domains/renderer.d.ts +0 -127
- package/dist/eager-control.d.ts +0 -8
- package/dist/effect-C3Zsc1k6.js +0 -292
- package/dist/for-state-BDykeOJL.js +0 -1753
- package/dist/instance-VAFYVJOa.js +0 -1027
- package/dist/notify-DxxuDPeY.js +0 -266
- package/dist/portal-w3oQ6oQU.js +0 -29
- package/dist/query-registry-LrYpQsDJ.js +0 -83
- package/dist/render-resolved-BC0W7_5b.js +0 -1123
- package/dist/renderer-CT0HeY58.js +0 -8623
- package/dist/resolution-3DskUkjG.js +0 -577
- package/dist/resource-18-sFRl9.js +0 -344
- package/dist/route-matching-CUzZ31gT.js +0 -551
- package/dist/router-internal-7nuNEHfG.js +0 -36
- package/dist/scope-Cq_iYbTR.js +0 -403
- package/dist/selector-BSPVkjKP.js +0 -311
- package/dist/ssr-EPjwJH5R.js +0 -29
- package/dist/state-BXJfudq1.js +0 -91
- package/dist/structures-CjEzfj4h.js +0 -654
- package/dist/timer-C7uqnJVt.js +0 -85
- package/dist/verify-hydration-ikaxotf7.js +0 -27
- package/dist/vnode-D24p6IW2.js +0 -7
package/dist/boot-BMBMWUGI.js
DELETED
|
@@ -1,1084 +0,0 @@
|
|
|
1
|
-
import { T as isProductionEnvironment, i as getRuntimeCleanup, l as installRuntimeRenderer, r as flushRuntimeScheduler, v as logger, w as isDevelopmentEnvironment } from "./access-CEA2pOry.js";
|
|
2
|
-
import { D as createComponentOwnership, E as cleanupComponentGeneration, F as captureGenerationExecution, N as assertSyncComponentResult, T as cleanupComponent, k as resetComponentWork, n as createComponentInstance, r as executeComponent } from "./instance-VAFYVJOa.js";
|
|
3
|
-
import { a as registerDeferredHydrationBoundary, c as beginHydrationDirectListenerMode, d as commitHydrationListenerTransaction, f as discardHydrationListenerTransaction, g as teardownNodeSubtree, h as removeAllListeners, i as clearDeferredHydrationBoundaries, l as endHydrationDirectListenerMode, n as activateHydrationBoundary$1, o as setStaticChildSlotsCacheEnabled, s as withIntrinsicHydrationAdoption, t as createRendererCapabilities, u as beginHydrationListenerTransaction } from "./renderer-CT0HeY58.js";
|
|
4
|
-
import { b as attachOwnership } from "./scope-Cq_iYbTR.js";
|
|
5
|
-
import { m as cleanupReadableSubscriptionSources } from "./notify-DxxuDPeY.js";
|
|
6
|
-
import { n as Fragment, t as ELEMENT_TYPE } from "./jsx-DRS-qjiN.js";
|
|
7
|
-
import { r as assertExecutionModel, t as _applyManifest } from "./manifest-D1P3UNWV.js";
|
|
8
|
-
import { d as stopHydrationRenderPhase, l as startHydrationRenderPhase, y as pageRenderEnvelope } from "./render-context-B7ohlh6t.js";
|
|
9
|
-
import { r as validateCspNonce, t as CspNonceScope } from "./csp-nonce-BfFWcd0w.js";
|
|
10
|
-
import { i as getDefaultDataRuntime } from "./data-runtime-CxoY98lX.js";
|
|
11
|
-
import { a as hydrateDataRuntime } from "./query-registry-LrYpQsDJ.js";
|
|
12
|
-
import { _ as configureRootUpdateHost, f as registerAppInstance, g as unregisterAppInstance, i as resolveRouteMeta, o as configureScrollRestoration, r as reconcileRouteMeta, t as initializeNavigation } from "./navigate-prC5V8XV.js";
|
|
13
|
-
import { n as disposeRegisteredDefaultPortalScope, r as getDefaultPortalHost, t as clearRegisteredDefaultPortalForInstance } from "./default-portal-runtime-DvMvWJVp.js";
|
|
14
|
-
import { a as clearStagedAppRenderRouteLocation, o as createAppRenderRuntime, r as setServerLocation, s as stageAppRenderRouteLocation } from "./activity-oy2YFTFF.js";
|
|
15
|
-
import { C as reviveDeferredValue, a as resolveRouteRequest, i as hasRouteRenderData, n as getRouteRenderContext, r as getRouteRenderData, y as withoutRouteHydrationMetadata } from "./resolution-3DskUkjG.js";
|
|
16
|
-
import { F as hasRegisteredRoutes, L as lockRouteRegistration, _ as clearRouteState, m as _setActiveRouteAuthOptions } from "./route-matching-CUzZ31gT.js";
|
|
17
|
-
import "./ssr-EPjwJH5R.js";
|
|
18
|
-
//#region src/runtime/component/generation.ts
|
|
19
|
-
/** Reuse an execution record after boot has retired its previous lifetime. */
|
|
20
|
-
function restartComponentGeneration(instance, fn, preserveState) {
|
|
21
|
-
instance.owner = createComponentOwnership(instance);
|
|
22
|
-
instance.fn = fn;
|
|
23
|
-
instance.evaluationGeneration++;
|
|
24
|
-
instance.expectedStateIndices = [];
|
|
25
|
-
instance.firstRenderComplete = false;
|
|
26
|
-
if (!preserveState) {
|
|
27
|
-
resetComponentWork(instance);
|
|
28
|
-
instance.stateValues = [];
|
|
29
|
-
instance.stateIndexCheck = -1;
|
|
30
|
-
instance._currentRenderToken = void 0;
|
|
31
|
-
instance.lastRenderToken = 0;
|
|
32
|
-
instance.errorBoundaryState = void 0;
|
|
33
|
-
instance._portalErrorParent = void 0;
|
|
34
|
-
instance._portalErrorParentGeneration = void 0;
|
|
35
|
-
instance.devWarningsEmitted = void 0;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
/** Runtime-owned preparation and retirement. A host may restore its own state
|
|
39
|
-
* between disposing provisional work and restoring the previous execution. */
|
|
40
|
-
function captureComponentGeneration(instance) {
|
|
41
|
-
const restoreHostIndex = getRuntimeCleanup().captureComponentHost(instance);
|
|
42
|
-
const snapshot = captureGenerationExecution(instance);
|
|
43
|
-
const reads = instance.owner.reads;
|
|
44
|
-
const readerEntries = Array.from(reads ?? [], (source) => ({
|
|
45
|
-
source,
|
|
46
|
-
reader: source._readers?.get(instance)
|
|
47
|
-
}));
|
|
48
|
-
let settled = false;
|
|
49
|
-
return {
|
|
50
|
-
prepare(fn, props) {
|
|
51
|
-
if (settled || instance.owner !== snapshot.owner) throw new Error("[Askr] component generation is no longer available for preparation");
|
|
52
|
-
const owner = createComponentOwnership(instance);
|
|
53
|
-
owner.mounted = snapshot.owner.mounted;
|
|
54
|
-
instance.owner = owner;
|
|
55
|
-
attachOwnership(owner, snapshot.owner.parent);
|
|
56
|
-
instance.fn = fn;
|
|
57
|
-
instance.props = props;
|
|
58
|
-
resetComponentWork(instance);
|
|
59
|
-
instance.stateValues = [];
|
|
60
|
-
instance.expectedStateIndices = [];
|
|
61
|
-
instance.firstRenderComplete = false;
|
|
62
|
-
instance.stateIndexCheck = -1;
|
|
63
|
-
instance.evaluationGeneration++;
|
|
64
|
-
instance.lifecycleGeneration++;
|
|
65
|
-
instance.mountOperations = [];
|
|
66
|
-
instance.commitOperations = [];
|
|
67
|
-
instance.lifecycleSlots = [];
|
|
68
|
-
instance._currentRenderToken = void 0;
|
|
69
|
-
instance.lastRenderToken = 0;
|
|
70
|
-
},
|
|
71
|
-
rollback(restoreHost) {
|
|
72
|
-
if (settled) return [];
|
|
73
|
-
settled = true;
|
|
74
|
-
const errors = [];
|
|
75
|
-
const provisional = instance.owner;
|
|
76
|
-
if (provisional !== snapshot.owner) {
|
|
77
|
-
try {
|
|
78
|
-
cleanupReadableSubscriptionSources(instance, provisional.reads, provisional.identity);
|
|
79
|
-
} catch (error) {
|
|
80
|
-
errors.push(error);
|
|
81
|
-
}
|
|
82
|
-
provisional.reads = void 0;
|
|
83
|
-
try {
|
|
84
|
-
cleanupComponent(instance);
|
|
85
|
-
} catch (error) {
|
|
86
|
-
errors.push(error);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
try {
|
|
90
|
-
errors.push(...restoreHost());
|
|
91
|
-
} catch (error) {
|
|
92
|
-
errors.push(error);
|
|
93
|
-
}
|
|
94
|
-
Object.assign(instance, snapshot);
|
|
95
|
-
try {
|
|
96
|
-
restoreHostIndex?.();
|
|
97
|
-
} catch (error) {
|
|
98
|
-
errors.push(error);
|
|
99
|
-
}
|
|
100
|
-
snapshot.owner.reads = reads;
|
|
101
|
-
for (const { source, reader } of readerEntries) {
|
|
102
|
-
if (!reader) {
|
|
103
|
-
if (source._readers?.delete(instance) && false);
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
const readers = source._readers ??= /* @__PURE__ */ new Map();
|
|
107
|
-
readers.has(instance);
|
|
108
|
-
readers.set(instance, reader);
|
|
109
|
-
}
|
|
110
|
-
return errors;
|
|
111
|
-
},
|
|
112
|
-
retire() {
|
|
113
|
-
if (settled) return;
|
|
114
|
-
settled = true;
|
|
115
|
-
cleanupComponentGeneration(instance, snapshot.owner);
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
//#endregion
|
|
120
|
-
//#region src/renderer/ownership/root-snapshot.ts
|
|
121
|
-
function captureRootTree(root) {
|
|
122
|
-
if (!root) return null;
|
|
123
|
-
const nodes = [];
|
|
124
|
-
const visit = (node) => {
|
|
125
|
-
nodes.push({
|
|
126
|
-
node,
|
|
127
|
-
children: Array.from(node.childNodes),
|
|
128
|
-
attributes: node instanceof Element ? Array.from(node.attributes).map((attribute) => [attribute.name, attribute.value]) : null,
|
|
129
|
-
nodeValue: node.nodeValue
|
|
130
|
-
});
|
|
131
|
-
for (const child of Array.from(node.childNodes)) visit(child);
|
|
132
|
-
};
|
|
133
|
-
visit(root);
|
|
134
|
-
return {
|
|
135
|
-
root,
|
|
136
|
-
nodes
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
function collectProvisionalRootNodes(snapshot) {
|
|
140
|
-
const originalNodes = new Set(snapshot.nodes.map((entry) => entry.node));
|
|
141
|
-
const provisional = [];
|
|
142
|
-
const visit = (node) => {
|
|
143
|
-
if (!originalNodes.has(node)) {
|
|
144
|
-
provisional.push(node);
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
for (const child of Array.from(node.childNodes)) visit(child);
|
|
148
|
-
};
|
|
149
|
-
for (const child of Array.from(snapshot.root.childNodes)) visit(child);
|
|
150
|
-
return provisional;
|
|
151
|
-
}
|
|
152
|
-
function restoreRootTree(snapshot) {
|
|
153
|
-
if (!snapshot) return [];
|
|
154
|
-
const errors = [];
|
|
155
|
-
for (const node of collectProvisionalRootNodes(snapshot)) try {
|
|
156
|
-
teardownNodeSubtree(node, { strict: true });
|
|
157
|
-
} catch (error) {
|
|
158
|
-
errors.push(error);
|
|
159
|
-
}
|
|
160
|
-
for (const entry of snapshot.nodes) {
|
|
161
|
-
const { node, attributes } = entry;
|
|
162
|
-
try {
|
|
163
|
-
if (attributes && node instanceof Element) {
|
|
164
|
-
const expected = new Map(attributes);
|
|
165
|
-
for (const attribute of Array.from(node.attributes)) if (!expected.has(attribute.name)) node.removeAttribute(attribute.name);
|
|
166
|
-
for (const [name, value] of attributes) if (node.getAttribute(name) !== value) node.setAttribute(name, value);
|
|
167
|
-
} else if (!(node instanceof Element)) node.nodeValue = entry.nodeValue;
|
|
168
|
-
if (node instanceof Element || node instanceof DocumentFragment) node.replaceChildren(...entry.children);
|
|
169
|
-
} catch (error) {
|
|
170
|
-
errors.push(error);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return errors;
|
|
174
|
-
}
|
|
175
|
-
function captureRootHost(root) {
|
|
176
|
-
const snapshot = captureRootTree(root);
|
|
177
|
-
return { restore: () => restoreRootTree(snapshot) };
|
|
178
|
-
}
|
|
179
|
-
//#endregion
|
|
180
|
-
//#region src/boot/root-handler.ts
|
|
181
|
-
function wrapRootRouteHandler(componentFn, cspNonce) {
|
|
182
|
-
const wrappedFn = (props, ctx) => {
|
|
183
|
-
const out = componentFn(props, ctx);
|
|
184
|
-
assertSyncComponentResult(out);
|
|
185
|
-
const portalVNode = {
|
|
186
|
-
$$typeof: ELEMENT_TYPE,
|
|
187
|
-
type: getDefaultPortalHost(),
|
|
188
|
-
props: { __askrAutoDefaultPortal: true },
|
|
189
|
-
key: "__default_portal"
|
|
190
|
-
};
|
|
191
|
-
const root = {
|
|
192
|
-
$$typeof: ELEMENT_TYPE,
|
|
193
|
-
type: Fragment,
|
|
194
|
-
props: { children: out === void 0 || out === null ? [portalVNode] : [out, portalVNode] }
|
|
195
|
-
};
|
|
196
|
-
return cspNonce === void 0 ? root : CspNonceScope({
|
|
197
|
-
value: cspNonce,
|
|
198
|
-
children: root
|
|
199
|
-
});
|
|
200
|
-
};
|
|
201
|
-
Object.defineProperty(wrappedFn, "name", { value: componentFn.name || "Component" });
|
|
202
|
-
return wrappedFn;
|
|
203
|
-
}
|
|
204
|
-
//#endregion
|
|
205
|
-
//#region src/boot/root-update.ts
|
|
206
|
-
function prepare(root, input) {
|
|
207
|
-
const instance = root;
|
|
208
|
-
const generation = captureComponentGeneration(instance);
|
|
209
|
-
const host = captureRootHost(instance.target);
|
|
210
|
-
const current = instance._appRenderRuntime;
|
|
211
|
-
const runtime = createAppRenderRuntime({
|
|
212
|
-
framework: withoutRouteHydrationMetadata(current?.framework),
|
|
213
|
-
dataRuntime: current?.dataRuntime,
|
|
214
|
-
routeRegistry: current?.routeRegistry,
|
|
215
|
-
routeAuth: current?.routeAuth,
|
|
216
|
-
route: input.routeData,
|
|
217
|
-
hasRoute: input.hasRouteData
|
|
218
|
-
});
|
|
219
|
-
let applied = false;
|
|
220
|
-
let settled = false;
|
|
221
|
-
return {
|
|
222
|
-
apply() {
|
|
223
|
-
if (applied) return;
|
|
224
|
-
applied = true;
|
|
225
|
-
stageAppRenderRouteLocation(runtime, input.href);
|
|
226
|
-
instance._appRenderRuntime = runtime;
|
|
227
|
-
if (input.replaceLifetime) {
|
|
228
|
-
clearRegisteredDefaultPortalForInstance(instance);
|
|
229
|
-
generation.prepare(wrapRootRouteHandler(input.handler, instance._cspNonce), {});
|
|
230
|
-
executeComponent(instance);
|
|
231
|
-
} else instance._enqueueRun?.();
|
|
232
|
-
},
|
|
233
|
-
publish() {
|
|
234
|
-
clearStagedAppRenderRouteLocation(runtime);
|
|
235
|
-
},
|
|
236
|
-
rollback() {
|
|
237
|
-
if (settled) return [];
|
|
238
|
-
settled = true;
|
|
239
|
-
clearStagedAppRenderRouteLocation(runtime);
|
|
240
|
-
return applied ? generation.rollback(() => host.restore()) : [];
|
|
241
|
-
},
|
|
242
|
-
retire() {
|
|
243
|
-
if (settled) return [];
|
|
244
|
-
settled = true;
|
|
245
|
-
const errors = [];
|
|
246
|
-
if (applied && input.replaceLifetime) try {
|
|
247
|
-
generation.retire();
|
|
248
|
-
} catch (error) {
|
|
249
|
-
errors.push(error);
|
|
250
|
-
}
|
|
251
|
-
return errors;
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
function installRootUpdateHost() {
|
|
256
|
-
configureRootUpdateHost({ prepare });
|
|
257
|
-
}
|
|
258
|
-
//#endregion
|
|
259
|
-
//#region src/boot/runtime-wiring.ts
|
|
260
|
-
let bridged = false;
|
|
261
|
-
/**
|
|
262
|
-
* Browser composition owns the connection between execution and rendering.
|
|
263
|
-
*
|
|
264
|
-
* Installs unconditionally: importing the boot entry means native boot owns the
|
|
265
|
-
* renderer, replacing whatever a consumer had configured before that point.
|
|
266
|
-
*/
|
|
267
|
-
function installRendererBridge() {
|
|
268
|
-
bridged = true;
|
|
269
|
-
installRuntimeRenderer(createRendererCapabilities());
|
|
270
|
-
return true;
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Connect execution to the DOM renderer only if nothing has yet.
|
|
274
|
-
*
|
|
275
|
-
* Unlike the install above this leaves an already-installed host alone, so a
|
|
276
|
-
* renderer a consumer configured after composing boot survives. Kept in this
|
|
277
|
-
* module rather than the composition root so a caller that only needs
|
|
278
|
-
* rendering — driving the runtime directly, as the jsdom test environment does
|
|
279
|
-
* — does not pull root-lifetime wiring into its module graph.
|
|
280
|
-
*/
|
|
281
|
-
function ensureRendererBridge() {
|
|
282
|
-
if (bridged) return;
|
|
283
|
-
installRendererBridge();
|
|
284
|
-
}
|
|
285
|
-
//#endregion
|
|
286
|
-
//#region src/boot/composition.ts
|
|
287
|
-
let rootUpdateHosted = false;
|
|
288
|
-
function ensureRootUpdateHost() {
|
|
289
|
-
if (rootUpdateHosted) return;
|
|
290
|
-
rootUpdateHosted = true;
|
|
291
|
-
installRootUpdateHost();
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Browser composition root, owned by the boot entry.
|
|
295
|
-
*
|
|
296
|
-
* Wiring a host is composition, not an import side effect of an implementation
|
|
297
|
-
* module: when these calls lived at the top of root-lifecycle.ts, the import
|
|
298
|
-
* order of unrelated modules decided whether a host existed. An entry barrel is
|
|
299
|
-
* where composition belongs, so importing `@askrjs/askr/boot` installs the
|
|
300
|
-
* native renderer — that is the contract consumers rely on.
|
|
301
|
-
*/
|
|
302
|
-
function composeBrowserRuntime() {
|
|
303
|
-
installRendererBridge();
|
|
304
|
-
ensureRootUpdateHost();
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Compose whatever is still missing, for a mount reached without the boot entry.
|
|
308
|
-
*
|
|
309
|
-
* Non-clobbering: a renderer the consumer configured after composing boot stays
|
|
310
|
-
* installed across mounts.
|
|
311
|
-
*/
|
|
312
|
-
function ensureBrowserRuntime() {
|
|
313
|
-
ensureRendererBridge();
|
|
314
|
-
ensureRootUpdateHost();
|
|
315
|
-
}
|
|
316
|
-
//#endregion
|
|
317
|
-
//#region src/boot/root-element.ts
|
|
318
|
-
function resolveRootElement(root) {
|
|
319
|
-
if (typeof root !== "string") return root;
|
|
320
|
-
const id = root.startsWith("#") ? root.slice(1) : root;
|
|
321
|
-
return document.getElementById(id);
|
|
322
|
-
}
|
|
323
|
-
//#endregion
|
|
324
|
-
//#region src/boot/root-lifecycle.ts
|
|
325
|
-
let componentIdCounter = 0;
|
|
326
|
-
const instancesByRoot = /* @__PURE__ */ new WeakMap();
|
|
327
|
-
const routedRoots = /* @__PURE__ */ new Set();
|
|
328
|
-
const CLEANUP_SYMBOL = Symbol.for("__askrCleanup__");
|
|
329
|
-
const ROOT_CLEANUP_CALLBACKS_SYMBOL = Symbol.for("__askrRootCleanupCallbacks__");
|
|
330
|
-
function clearRootCleanupCallbacks(rootElement) {
|
|
331
|
-
if (!Reflect.deleteProperty(rootElement, ROOT_CLEANUP_CALLBACKS_SYMBOL)) rootElement[ROOT_CLEANUP_CALLBACKS_SYMBOL]?.clear();
|
|
332
|
-
}
|
|
333
|
-
function registerRootCleanupCallback(rootElement, callback) {
|
|
334
|
-
const elementWithCleanup = rootElement;
|
|
335
|
-
const callbacks = elementWithCleanup[ROOT_CLEANUP_CALLBACKS_SYMBOL] ?? /* @__PURE__ */ new Set();
|
|
336
|
-
callbacks.add(callback);
|
|
337
|
-
elementWithCleanup[ROOT_CLEANUP_CALLBACKS_SYMBOL] = callbacks;
|
|
338
|
-
return () => {
|
|
339
|
-
callbacks.delete(callback);
|
|
340
|
-
if (callbacks.size === 0 && elementWithCleanup[ROOT_CLEANUP_CALLBACKS_SYMBOL] === callbacks) clearRootCleanupCallbacks(rootElement);
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
function cleanupRootInstance(rootElement, instance, options) {
|
|
344
|
-
if (instancesByRoot.get(rootElement) !== instance) return;
|
|
345
|
-
const element = rootElement;
|
|
346
|
-
const cleanup = element[CLEANUP_SYMBOL];
|
|
347
|
-
const callbacks = element[ROOT_CLEANUP_CALLBACKS_SYMBOL];
|
|
348
|
-
const wasRoutedRoot = routedRoots.delete(rootElement);
|
|
349
|
-
instancesByRoot.delete(rootElement);
|
|
350
|
-
clearRootCleanupCallbacks(rootElement);
|
|
351
|
-
Reflect.deleteProperty(element, CLEANUP_SYMBOL);
|
|
352
|
-
clearDeferredHydrationBoundaries(rootElement);
|
|
353
|
-
const errors = [];
|
|
354
|
-
for (const phase of [[() => teardownNodeSubtree(rootElement), () => cleanupComponent(instance)], callbacks ?? []]) for (const callback of phase) try {
|
|
355
|
-
callback();
|
|
356
|
-
} catch (e) {
|
|
357
|
-
errors.push(e);
|
|
358
|
-
}
|
|
359
|
-
if (options?.preserveInstance && !instancesByRoot.has(rootElement)) {
|
|
360
|
-
instancesByRoot.set(rootElement, instance);
|
|
361
|
-
element[CLEANUP_SYMBOL] = cleanup;
|
|
362
|
-
if (wasRoutedRoot) routedRoots.add(rootElement);
|
|
363
|
-
} else unregisterAppInstance(instance);
|
|
364
|
-
if (wasRoutedRoot && routedRoots.size === 0) clearRouteState();
|
|
365
|
-
if (errors.length > 0) {
|
|
366
|
-
if (instance.cleanupStrict) throw new AggregateError(errors, `cleanup failed for app root`);
|
|
367
|
-
else if (isDevelopmentEnvironment()) for (const err of errors) logger.warn("[Askr] cleanup error:", err);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
function attachCleanupForRoot(rootElement, instance) {
|
|
371
|
-
rootElement[CLEANUP_SYMBOL] = (options) => {
|
|
372
|
-
cleanupRootInstance(rootElement, instance, options);
|
|
373
|
-
};
|
|
374
|
-
registerRootCleanupCallback(rootElement, () => {
|
|
375
|
-
disposeRegisteredDefaultPortalScope(instance.portalScope ?? instance);
|
|
376
|
-
});
|
|
377
|
-
try {
|
|
378
|
-
const descriptor = Object.getOwnPropertyDescriptor(rootElement, "innerHTML") || Object.getOwnPropertyDescriptor(Object.getPrototypeOf(rootElement), "innerHTML") || Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML");
|
|
379
|
-
if (descriptor && (descriptor.get || descriptor.set)) Object.defineProperty(rootElement, "innerHTML", {
|
|
380
|
-
get: function() {
|
|
381
|
-
return descriptor.get?.call(this);
|
|
382
|
-
},
|
|
383
|
-
set: function(value) {
|
|
384
|
-
if (value === "" && instancesByRoot.get(this) === instance) {
|
|
385
|
-
cleanupRootInstance(rootElement, instance);
|
|
386
|
-
if (instancesByRoot.has(this)) return;
|
|
387
|
-
}
|
|
388
|
-
return descriptor.set?.call(this, value);
|
|
389
|
-
},
|
|
390
|
-
configurable: true
|
|
391
|
-
});
|
|
392
|
-
} catch {}
|
|
393
|
-
}
|
|
394
|
-
function mountOrUpdate(rootElement, componentFn, options) {
|
|
395
|
-
ensureBrowserRuntime();
|
|
396
|
-
const nonce = validateCspNonce(options?.cspNonce);
|
|
397
|
-
const wrappedFn = wrapRootRouteHandler(componentFn, nonce);
|
|
398
|
-
const existingCleanup = rootElement[CLEANUP_SYMBOL];
|
|
399
|
-
const reusedExistingInstance = typeof existingCleanup === "function";
|
|
400
|
-
if (reusedExistingInstance) {
|
|
401
|
-
existingCleanup({ preserveInstance: true });
|
|
402
|
-
if (rootElement[CLEANUP_SYMBOL] !== existingCleanup) return;
|
|
403
|
-
}
|
|
404
|
-
let instance = instancesByRoot.get(rootElement);
|
|
405
|
-
if (instance) {
|
|
406
|
-
const shouldResetHookState = instance._rootComponentFn !== componentFn;
|
|
407
|
-
if (!reusedExistingInstance) {
|
|
408
|
-
removeAllListeners(rootElement);
|
|
409
|
-
try {
|
|
410
|
-
cleanupComponent(instance);
|
|
411
|
-
} catch (e) {
|
|
412
|
-
if (isDevelopmentEnvironment()) logger.warn("[Askr] prior cleanup threw:", e);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
restartComponentGeneration(instance, wrappedFn, !shouldResetHookState);
|
|
416
|
-
} else {
|
|
417
|
-
const componentId = String(++componentIdCounter);
|
|
418
|
-
instance = createComponentInstance(componentId, wrappedFn, {}, rootElement, null);
|
|
419
|
-
instancesByRoot.set(rootElement, instance);
|
|
420
|
-
}
|
|
421
|
-
instance.isRoot = true;
|
|
422
|
-
instance._rootComponentFn = componentFn;
|
|
423
|
-
instance.portalScope = instance;
|
|
424
|
-
instance._appRenderRuntime = options?.appRuntime;
|
|
425
|
-
instance._cspNonce = nonce;
|
|
426
|
-
if (options && typeof options.cleanupStrict === "boolean") instance.cleanupStrict = options.cleanupStrict;
|
|
427
|
-
attachCleanupForRoot(rootElement, instance);
|
|
428
|
-
executeComponent(instance);
|
|
429
|
-
flushRuntimeScheduler();
|
|
430
|
-
}
|
|
431
|
-
/** @internal Activate one deferred boundary without rerunning its root. */
|
|
432
|
-
function activateHydrationBoundary(rootElement, boundary) {
|
|
433
|
-
if (!instancesByRoot.has(rootElement)) return false;
|
|
434
|
-
const activated = activateHydrationBoundary$1(boundary);
|
|
435
|
-
if (activated) flushRuntimeScheduler();
|
|
436
|
-
return activated;
|
|
437
|
-
}
|
|
438
|
-
async function registerAppNavigation(rootElement, path, source) {
|
|
439
|
-
ensureBrowserRuntime();
|
|
440
|
-
const instance = instancesByRoot.get(rootElement);
|
|
441
|
-
if (!instance) throw new Error("Internal error: app instance missing");
|
|
442
|
-
routedRoots.add(rootElement);
|
|
443
|
-
registerAppInstance(instance, path, source);
|
|
444
|
-
initializeNavigation();
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Tear down the app mounted at `root`: runs its cleanup callbacks, clears
|
|
448
|
-
* route state if it was the last routed root, and removes bookkeeping for
|
|
449
|
-
* the root element.
|
|
450
|
-
*/
|
|
451
|
-
function cleanupApp(root) {
|
|
452
|
-
const rootElement = resolveRootElement(root);
|
|
453
|
-
if (!rootElement) return;
|
|
454
|
-
const cleanupFn = rootElement[CLEANUP_SYMBOL];
|
|
455
|
-
if (typeof cleanupFn === "function") cleanupFn();
|
|
456
|
-
else clearRootCleanupCallbacks(rootElement);
|
|
457
|
-
}
|
|
458
|
-
/** Check whether an app instance is currently mounted at `root`. */
|
|
459
|
-
function hasApp(root) {
|
|
460
|
-
const rootElement = resolveRootElement(root);
|
|
461
|
-
if (!rootElement) return false;
|
|
462
|
-
return instancesByRoot.has(rootElement);
|
|
463
|
-
}
|
|
464
|
-
//#endregion
|
|
465
|
-
//#region src/boot/islands.ts
|
|
466
|
-
/**
|
|
467
|
-
* createIsland: Enhances existing DOM (no router, mounts once)
|
|
468
|
-
*/
|
|
469
|
-
function createIsland(config) {
|
|
470
|
-
assertExecutionModel("islands");
|
|
471
|
-
if (!config || typeof config !== "object") throw new Error("createIsland requires a config object");
|
|
472
|
-
if (typeof config.component !== "function") throw new Error("createIsland: component must be a function");
|
|
473
|
-
validateCspNonce(config.cspNonce);
|
|
474
|
-
const rootElement = resolveRootElement(config.root);
|
|
475
|
-
if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
|
|
476
|
-
if ("routes" in config) throw new Error("createIsland does not accept routes; use createSPA for routed apps");
|
|
477
|
-
if (hasRegisteredRoutes()) throw new Error("Routes are not supported with islands. Use createSPA (client) or createSSR (server) instead.");
|
|
478
|
-
mountOrUpdate(rootElement, config.component, {
|
|
479
|
-
cleanupStrict: config.cleanupStrict,
|
|
480
|
-
cspNonce: config.cspNonce,
|
|
481
|
-
appRuntime: createAppRenderRuntime({ dataRuntime: config.dataRuntime ?? getDefaultDataRuntime() })
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* createIslands: Enhances one or more existing DOM roots (no router).
|
|
486
|
-
* The only public islands constructor.
|
|
487
|
-
*/
|
|
488
|
-
function createIslands(config) {
|
|
489
|
-
assertExecutionModel("islands");
|
|
490
|
-
if (!config || typeof config !== "object") throw new Error("createIslands requires a config object");
|
|
491
|
-
validateCspNonce(config.cspNonce);
|
|
492
|
-
if (!Array.isArray(config.islands) || config.islands.length === 0) throw new Error("createIslands requires a non-empty islands array");
|
|
493
|
-
for (const island of config.islands) createIsland({
|
|
494
|
-
...island,
|
|
495
|
-
cspNonce: island.cspNonce ?? config.cspNonce
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
//#endregion
|
|
499
|
-
//#region src/boot/route-startup.ts
|
|
500
|
-
const MAX_INITIAL_ROUTE_REDIRECTS = 20;
|
|
501
|
-
function bindResolvedRouteHandler(resolved) {
|
|
502
|
-
return () => resolved.handler(resolved.params);
|
|
503
|
-
}
|
|
504
|
-
function createDeniedStatusNode(status) {
|
|
505
|
-
return {
|
|
506
|
-
type: "div",
|
|
507
|
-
props: { "data-route-denied": String(status) },
|
|
508
|
-
children: [String(status)]
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
function bindDeniedStatus(status) {
|
|
512
|
-
return () => createDeniedStatusNode(status);
|
|
513
|
-
}
|
|
514
|
-
function bindDeniedRouteHandler(status) {
|
|
515
|
-
return () => createDeniedStatusNode(status);
|
|
516
|
-
}
|
|
517
|
-
async function reconcileInitialRouteMetadata(resolved) {
|
|
518
|
-
if (resolved?.kind !== "render" || !resolved.record) return;
|
|
519
|
-
if (!resolved.record.options.title && !resolved.record.options.meta && !resolved.record.metaChain?.length) return;
|
|
520
|
-
const context = getRouteRenderContext(resolved);
|
|
521
|
-
if (!context) return;
|
|
522
|
-
reconcileRouteMeta(await resolveRouteMeta(resolved.record, context));
|
|
523
|
-
}
|
|
524
|
-
async function resolveInitialRoute(auth, source) {
|
|
525
|
-
let path = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
526
|
-
let href = typeof window !== "undefined" ? `${window.location.pathname}${window.location.search}${window.location.hash}` : path;
|
|
527
|
-
const visited = /* @__PURE__ */ new Set();
|
|
528
|
-
for (let redirects = 0; redirects <= MAX_INITIAL_ROUTE_REDIRECTS; redirects++) {
|
|
529
|
-
if (visited.has(href)) throw new Error(`[Askr] Route redirect cycle detected at ${href}.`);
|
|
530
|
-
visited.add(href);
|
|
531
|
-
if (!source?.registry) throw new TypeError("resolveInitialRoute requires a route registry.");
|
|
532
|
-
const resolved = await resolveRouteRequest(href, {
|
|
533
|
-
registry: source.registry,
|
|
534
|
-
auth,
|
|
535
|
-
load: source.load
|
|
536
|
-
});
|
|
537
|
-
if (typeof window === "undefined" || !resolved || resolved.kind !== "redirect") return {
|
|
538
|
-
path,
|
|
539
|
-
href,
|
|
540
|
-
resolved
|
|
541
|
-
};
|
|
542
|
-
const redirectTarget = new URL(resolved.to, window.location.href);
|
|
543
|
-
const redirectHref = `${redirectTarget.pathname}${redirectTarget.search}${redirectTarget.hash}`;
|
|
544
|
-
window.history.replaceState({ path: redirectHref }, "", redirectHref);
|
|
545
|
-
path = redirectTarget.pathname;
|
|
546
|
-
href = redirectHref;
|
|
547
|
-
}
|
|
548
|
-
throw new Error(`[Askr] Route redirect limit exceeded (${MAX_INITIAL_ROUTE_REDIRECTS}).`);
|
|
549
|
-
}
|
|
550
|
-
//#endregion
|
|
551
|
-
//#region src/boot/spa.ts
|
|
552
|
-
/**
|
|
553
|
-
* App bootstrap and mount
|
|
554
|
-
*/
|
|
555
|
-
/**
|
|
556
|
-
* createSPA: Initializes the router and mounts the app with an explicit route registry.
|
|
557
|
-
* ```ts
|
|
558
|
-
* import { createRouteRegistry } from '@askrjs/askr/router';
|
|
559
|
-
* const registry = createRouteRegistry(() => { ... });
|
|
560
|
-
* await createSPA({ root: '#app', registry });
|
|
561
|
-
* ```
|
|
562
|
-
*
|
|
563
|
-
*/
|
|
564
|
-
async function createSPA(config) {
|
|
565
|
-
assertExecutionModel("spa");
|
|
566
|
-
if (!config || typeof config !== "object") throw new Error("createSPA requires a config object");
|
|
567
|
-
validateCspNonce(config.cspNonce);
|
|
568
|
-
if (!config.registry) throw new Error("createSPA requires a route registry.");
|
|
569
|
-
const { manifest } = config.registry;
|
|
570
|
-
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.");
|
|
571
|
-
const rootElement = resolveRootElement(config.root);
|
|
572
|
-
if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
|
|
573
|
-
configureScrollRestoration(config.scrollRestoration);
|
|
574
|
-
clearRouteState();
|
|
575
|
-
_applyManifest(manifest);
|
|
576
|
-
const routeAuth = config.auth ?? manifest.auth;
|
|
577
|
-
const appRouteSource = {
|
|
578
|
-
registry: config.registry,
|
|
579
|
-
auth: routeAuth
|
|
580
|
-
};
|
|
581
|
-
_setActiveRouteAuthOptions(routeAuth);
|
|
582
|
-
if (isProductionEnvironment()) lockRouteRegistration();
|
|
583
|
-
const { path, href: currentUrl, resolved } = await resolveInitialRoute(routeAuth, { registry: config.registry });
|
|
584
|
-
setServerLocation(currentUrl);
|
|
585
|
-
const appRuntime = createAppRenderRuntime({
|
|
586
|
-
...resolved?.kind === "render" && hasRouteRenderData(resolved) ? {
|
|
587
|
-
route: getRouteRenderData(resolved),
|
|
588
|
-
hasRoute: true
|
|
589
|
-
} : {},
|
|
590
|
-
dataRuntime: config.dataRuntime ?? getDefaultDataRuntime(),
|
|
591
|
-
routeRegistry: config.registry,
|
|
592
|
-
routeAuth
|
|
593
|
-
});
|
|
594
|
-
if (!resolved) {
|
|
595
|
-
mountOrUpdate(rootElement, () => ({
|
|
596
|
-
type: "div",
|
|
597
|
-
children: []
|
|
598
|
-
}), {
|
|
599
|
-
cleanupStrict: config.cleanupStrict,
|
|
600
|
-
appRuntime,
|
|
601
|
-
cspNonce: config.cspNonce
|
|
602
|
-
});
|
|
603
|
-
await registerAppNavigation(rootElement, path, {
|
|
604
|
-
...appRouteSource,
|
|
605
|
-
runtime: appRuntime
|
|
606
|
-
});
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
if (resolved.kind === "redirect") {
|
|
610
|
-
mountOrUpdate(rootElement, () => ({
|
|
611
|
-
type: "div",
|
|
612
|
-
children: []
|
|
613
|
-
}), {
|
|
614
|
-
cleanupStrict: config.cleanupStrict,
|
|
615
|
-
appRuntime,
|
|
616
|
-
cspNonce: config.cspNonce
|
|
617
|
-
});
|
|
618
|
-
await registerAppNavigation(rootElement, path, {
|
|
619
|
-
...appRouteSource,
|
|
620
|
-
runtime: appRuntime
|
|
621
|
-
});
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
await reconcileInitialRouteMetadata(resolved);
|
|
625
|
-
mountOrUpdate(rootElement, resolved.kind === "deny" ? bindDeniedStatus(resolved.status) : bindResolvedRouteHandler({
|
|
626
|
-
handler: resolved.handler,
|
|
627
|
-
params: resolved.params
|
|
628
|
-
}), {
|
|
629
|
-
cleanupStrict: config.cleanupStrict,
|
|
630
|
-
appRuntime,
|
|
631
|
-
cspNonce: config.cspNonce
|
|
632
|
-
});
|
|
633
|
-
await registerAppNavigation(rootElement, path, {
|
|
634
|
-
...appRouteSource,
|
|
635
|
-
runtime: appRuntime
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
|
-
//#endregion
|
|
639
|
-
//#region src/boot/hydration.ts
|
|
640
|
-
const DEFERRED_PAYLOAD = "__askr_deferred__";
|
|
641
|
-
function applyDeferredStreamPatches(rootElement) {
|
|
642
|
-
const patches = Array.from(rootElement.querySelectorAll("template[data-askr-deferred-patch]"));
|
|
643
|
-
for (const template of patches) {
|
|
644
|
-
const id = template.getAttribute("data-askr-deferred-patch");
|
|
645
|
-
if (!id) continue;
|
|
646
|
-
const boundary = Array.from(rootElement.querySelectorAll("askr-resolve[data-askr-deferred]")).find((candidate) => candidate.getAttribute("data-askr-deferred") === id);
|
|
647
|
-
if (boundary) boundary.replaceWith(template.content.cloneNode(true));
|
|
648
|
-
template.remove();
|
|
649
|
-
for (const script of Array.from(rootElement.querySelectorAll("script[data-askr-deferred-apply]"))) if (script.getAttribute("data-askr-deferred-apply") === id) script.remove();
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
function hydrationReviver(_key, value) {
|
|
653
|
-
if (!value || typeof value !== "object") return value;
|
|
654
|
-
const payload = value;
|
|
655
|
-
if (payload[DEFERRED_PAYLOAD] === "fulfilled") return reviveDeferredValue("fulfilled", payload.value, void 0);
|
|
656
|
-
if (payload[DEFERRED_PAYLOAD] === "rejected") return reviveDeferredValue("rejected", void 0, payload.error);
|
|
657
|
-
return value;
|
|
658
|
-
}
|
|
659
|
-
function takeHydrationRenderData(rootElement) {
|
|
660
|
-
for (const child of Array.from(rootElement.children)) if (child instanceof HTMLScriptElement && child.getAttribute("data-askr-render-data") === "true") {
|
|
661
|
-
const raw = child.textContent ?? "";
|
|
662
|
-
child.remove();
|
|
663
|
-
if (!raw) return pageRenderEnvelope(void 0);
|
|
664
|
-
try {
|
|
665
|
-
return pageRenderEnvelope(JSON.parse(raw, hydrationReviver));
|
|
666
|
-
} catch (err) {
|
|
667
|
-
const error = /* @__PURE__ */ new Error("[Askr] Failed to parse embedded SSR render data during hydration.");
|
|
668
|
-
error.cause = err;
|
|
669
|
-
throw error;
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
return null;
|
|
673
|
-
}
|
|
674
|
-
function markSkippedElements(root, skipSelectors) {
|
|
675
|
-
if (skipSelectors.length === 0) return;
|
|
676
|
-
const selectorList = Array.from(new Set(skipSelectors)).join(", ");
|
|
677
|
-
root.querySelectorAll(selectorList).forEach((el) => el.setAttribute("data-skip-hydrate", "true"));
|
|
678
|
-
}
|
|
679
|
-
function collectDeferredBelowFoldBoundaries(root, foldY) {
|
|
680
|
-
const boundaries = [];
|
|
681
|
-
const stack = [];
|
|
682
|
-
for (let index = root.children.length - 1; index >= 0; index -= 1) stack.push(root.children[index]);
|
|
683
|
-
while (stack.length > 0) {
|
|
684
|
-
const element = stack.pop();
|
|
685
|
-
if (element.hasAttribute("data-skip-hydrate")) continue;
|
|
686
|
-
if (element.getBoundingClientRect().top >= foldY) {
|
|
687
|
-
element.setAttribute("data-skip-hydrate", "true");
|
|
688
|
-
registerDeferredHydrationBoundary(root, element);
|
|
689
|
-
boundaries.push(element);
|
|
690
|
-
continue;
|
|
691
|
-
}
|
|
692
|
-
for (let index = element.children.length - 1; index >= 0; index -= 1) stack.push(element.children[index]);
|
|
693
|
-
}
|
|
694
|
-
return boundaries;
|
|
695
|
-
}
|
|
696
|
-
function activateVisibleDeferredBoundaries(root, boundaries, foldY, activate) {
|
|
697
|
-
let activated = false;
|
|
698
|
-
let remaining = 0;
|
|
699
|
-
for (const element of boundaries) {
|
|
700
|
-
if (!element.hasAttribute("data-skip-hydrate")) continue;
|
|
701
|
-
if (element.getBoundingClientRect().top < foldY) try {
|
|
702
|
-
if (activate(root, element)) activated = true;
|
|
703
|
-
} catch {}
|
|
704
|
-
if (element.hasAttribute("data-skip-hydrate")) remaining += 1;
|
|
705
|
-
}
|
|
706
|
-
return {
|
|
707
|
-
activated,
|
|
708
|
-
remaining
|
|
709
|
-
};
|
|
710
|
-
}
|
|
711
|
-
function queueIdleWork(work) {
|
|
712
|
-
return new Promise((resolve) => {
|
|
713
|
-
if (typeof requestIdleCallback !== "undefined") {
|
|
714
|
-
requestIdleCallback(() => {
|
|
715
|
-
work();
|
|
716
|
-
resolve();
|
|
717
|
-
}, { timeout: 2e3 });
|
|
718
|
-
return;
|
|
719
|
-
}
|
|
720
|
-
setTimeout(() => {
|
|
721
|
-
work();
|
|
722
|
-
resolve();
|
|
723
|
-
}, 0);
|
|
724
|
-
});
|
|
725
|
-
}
|
|
726
|
-
function shouldVerifyHydrationMarkup(config) {
|
|
727
|
-
const explicit = config.hydrate?.verifyMarkup;
|
|
728
|
-
if (typeof explicit === "boolean") return explicit;
|
|
729
|
-
return !isProductionEnvironment();
|
|
730
|
-
}
|
|
731
|
-
async function applySelectiveHydration(rootElement, resolved, path, cleanupStrict, hydrateOptions, source, hooks, interactionReplay) {
|
|
732
|
-
const hasBelowFoldDeferral = !!hydrateOptions.deferBelowFold;
|
|
733
|
-
let staticChildSlotsCacheSuspended = false;
|
|
734
|
-
let releaseSelectiveHydrationResources = () => {};
|
|
735
|
-
const restoreStaticChildSlotsCache = () => {
|
|
736
|
-
if (!staticChildSlotsCacheSuspended) return;
|
|
737
|
-
setStaticChildSlotsCacheEnabled(true);
|
|
738
|
-
staticChildSlotsCacheSuspended = false;
|
|
739
|
-
};
|
|
740
|
-
if (hydrateOptions.skipSelectors?.length) markSkippedElements(rootElement, hydrateOptions.skipSelectors);
|
|
741
|
-
let deferredBoundaries = [];
|
|
742
|
-
if (hydrateOptions.deferBelowFold) {
|
|
743
|
-
setStaticChildSlotsCacheEnabled(false);
|
|
744
|
-
staticChildSlotsCacheSuspended = true;
|
|
745
|
-
const foldY = hydrateOptions.foldThreshold ?? window.innerHeight;
|
|
746
|
-
deferredBoundaries = collectDeferredBelowFoldBoundaries(rootElement, foldY);
|
|
747
|
-
interactionReplay.registerDeferredBoundaries(deferredBoundaries);
|
|
748
|
-
let selectiveHydrationResourcesReleased = false;
|
|
749
|
-
let unregisterRootCleanupCallback = () => {};
|
|
750
|
-
function handleScroll() {
|
|
751
|
-
const { activated, remaining } = activateVisibleDeferredBoundaries(rootElement, deferredBoundaries, foldY, hooks.activateHydrationBoundary);
|
|
752
|
-
if (!activated) return;
|
|
753
|
-
if (remaining === 0) releaseSelectiveHydrationResources();
|
|
754
|
-
}
|
|
755
|
-
releaseSelectiveHydrationResources = () => {
|
|
756
|
-
if (selectiveHydrationResourcesReleased) return;
|
|
757
|
-
selectiveHydrationResourcesReleased = true;
|
|
758
|
-
unregisterRootCleanupCallback();
|
|
759
|
-
window.removeEventListener("scroll", handleScroll);
|
|
760
|
-
restoreStaticChildSlotsCache();
|
|
761
|
-
interactionReplay.clearDeferredBoundaries();
|
|
762
|
-
};
|
|
763
|
-
interactionReplay.setOnDeferredBoundariesDrained(releaseSelectiveHydrationResources);
|
|
764
|
-
unregisterRootCleanupCallback = hooks.registerRootCleanupCallback(rootElement, releaseSelectiveHydrationResources);
|
|
765
|
-
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
766
|
-
}
|
|
767
|
-
if (hydrateOptions.deferUntilIdle && !hasBelowFoldDeferral) {
|
|
768
|
-
await queueIdleWork(() => {
|
|
769
|
-
hooks.mountOrUpdate(rootElement, (() => resolved.handler(resolved.params)), {
|
|
770
|
-
cleanupStrict,
|
|
771
|
-
appRuntime: source?.runtime
|
|
772
|
-
});
|
|
773
|
-
});
|
|
774
|
-
await hooks.registerAppNavigation(rootElement, path, source);
|
|
775
|
-
return;
|
|
776
|
-
}
|
|
777
|
-
try {
|
|
778
|
-
hooks.mountOrUpdate(rootElement, (() => resolved.handler(resolved.params)), {
|
|
779
|
-
cleanupStrict,
|
|
780
|
-
appRuntime: source?.runtime
|
|
781
|
-
});
|
|
782
|
-
await hooks.registerAppNavigation(rootElement, path, source);
|
|
783
|
-
} catch (error) {
|
|
784
|
-
releaseSelectiveHydrationResources();
|
|
785
|
-
throw error;
|
|
786
|
-
}
|
|
787
|
-
if (hydrateOptions.deferUntilIdle && deferredBoundaries.length > 0) await queueIdleWork(() => {
|
|
788
|
-
try {
|
|
789
|
-
activateVisibleDeferredBoundaries(rootElement, deferredBoundaries, Number.POSITIVE_INFINITY, hooks.activateHydrationBoundary);
|
|
790
|
-
} finally {
|
|
791
|
-
releaseSelectiveHydrationResources();
|
|
792
|
-
}
|
|
793
|
-
});
|
|
794
|
-
if (deferredBoundaries.length === 0) releaseSelectiveHydrationResources();
|
|
795
|
-
}
|
|
796
|
-
//#endregion
|
|
797
|
-
//#region src/boot/hydration-interaction-replay.ts
|
|
798
|
-
const HYDRATION_INTERACTION_TYPES = [
|
|
799
|
-
"click",
|
|
800
|
-
"dblclick",
|
|
801
|
-
"pointerdown",
|
|
802
|
-
"pointerup",
|
|
803
|
-
"keydown",
|
|
804
|
-
"keyup",
|
|
805
|
-
"input",
|
|
806
|
-
"change",
|
|
807
|
-
"submit"
|
|
808
|
-
];
|
|
809
|
-
function cloneInteractionEvent(event) {
|
|
810
|
-
const EventConstructor = event.constructor;
|
|
811
|
-
const eventWithDetails = event;
|
|
812
|
-
const init = {
|
|
813
|
-
bubbles: event.bubbles,
|
|
814
|
-
cancelable: event.cancelable,
|
|
815
|
-
composed: event.composed,
|
|
816
|
-
detail: eventWithDetails.detail,
|
|
817
|
-
view: eventWithDetails.view,
|
|
818
|
-
key: eventWithDetails.key,
|
|
819
|
-
code: eventWithDetails.code,
|
|
820
|
-
location: eventWithDetails.location,
|
|
821
|
-
repeat: eventWithDetails.repeat,
|
|
822
|
-
isComposing: eventWithDetails.isComposing,
|
|
823
|
-
ctrlKey: eventWithDetails.ctrlKey,
|
|
824
|
-
shiftKey: eventWithDetails.shiftKey,
|
|
825
|
-
altKey: eventWithDetails.altKey,
|
|
826
|
-
metaKey: eventWithDetails.metaKey,
|
|
827
|
-
button: eventWithDetails.button,
|
|
828
|
-
buttons: eventWithDetails.buttons,
|
|
829
|
-
clientX: eventWithDetails.clientX,
|
|
830
|
-
clientY: eventWithDetails.clientY,
|
|
831
|
-
screenX: eventWithDetails.screenX,
|
|
832
|
-
screenY: eventWithDetails.screenY,
|
|
833
|
-
pointerId: eventWithDetails.pointerId,
|
|
834
|
-
pointerType: eventWithDetails.pointerType,
|
|
835
|
-
isPrimary: eventWithDetails.isPrimary,
|
|
836
|
-
data: eventWithDetails.data,
|
|
837
|
-
inputType: eventWithDetails.inputType
|
|
838
|
-
};
|
|
839
|
-
try {
|
|
840
|
-
return new EventConstructor(event.type, init);
|
|
841
|
-
} catch {
|
|
842
|
-
return new Event(event.type, {
|
|
843
|
-
bubbles: event.bubbles,
|
|
844
|
-
cancelable: event.cancelable,
|
|
845
|
-
composed: event.composed
|
|
846
|
-
});
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
/**
|
|
850
|
-
* Capture discrete interactions before hydrated handlers exist, then replay
|
|
851
|
-
* them after the listener transaction commits. Deferred boundaries remain
|
|
852
|
-
* interaction-activatable until they are revealed or the root is cleaned up.
|
|
853
|
-
*/
|
|
854
|
-
function beginHydrationInteractionReplay(root, activateBoundary, permanentSkipSelectors = []) {
|
|
855
|
-
const queued = [];
|
|
856
|
-
const deferredBoundaries = /* @__PURE__ */ new Set();
|
|
857
|
-
let active = true;
|
|
858
|
-
let hydratingRoot = true;
|
|
859
|
-
let replaying = false;
|
|
860
|
-
let hadDeferredBoundaries = false;
|
|
861
|
-
let onDeferredBoundariesDrained;
|
|
862
|
-
function isPermanentlySkipped(target) {
|
|
863
|
-
const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
|
|
864
|
-
if (!element) return false;
|
|
865
|
-
return permanentSkipSelectors.some((selector) => {
|
|
866
|
-
try {
|
|
867
|
-
return element.closest(selector) !== null;
|
|
868
|
-
} catch {
|
|
869
|
-
return false;
|
|
870
|
-
}
|
|
871
|
-
});
|
|
872
|
-
}
|
|
873
|
-
function findDeferredBoundary(target) {
|
|
874
|
-
if (!(target instanceof Node)) return;
|
|
875
|
-
for (const boundary of deferredBoundaries) if (boundary.hasAttribute("data-skip-hydrate") && boundary.contains(target)) return boundary;
|
|
876
|
-
}
|
|
877
|
-
function removeCaptureListeners() {
|
|
878
|
-
if (!active) return;
|
|
879
|
-
active = false;
|
|
880
|
-
for (const type of HYDRATION_INTERACTION_TYPES) root.removeEventListener(type, captureInteraction, true);
|
|
881
|
-
}
|
|
882
|
-
function releaseIfFinished() {
|
|
883
|
-
if (!hydratingRoot && deferredBoundaries.size === 0) removeCaptureListeners();
|
|
884
|
-
}
|
|
885
|
-
function notifyIfDeferredBoundariesDrained() {
|
|
886
|
-
if (hadDeferredBoundaries && deferredBoundaries.size === 0) onDeferredBoundariesDrained?.();
|
|
887
|
-
}
|
|
888
|
-
function replayInteraction(interaction) {
|
|
889
|
-
if (!(interaction.target instanceof Node) || !root.contains(interaction.target)) return;
|
|
890
|
-
const boundary = findDeferredBoundary(interaction.target);
|
|
891
|
-
if (boundary) {
|
|
892
|
-
try {
|
|
893
|
-
if (!activateBoundary(boundary)) return;
|
|
894
|
-
} catch {
|
|
895
|
-
return;
|
|
896
|
-
}
|
|
897
|
-
deferredBoundaries.delete(boundary);
|
|
898
|
-
notifyIfDeferredBoundariesDrained();
|
|
899
|
-
}
|
|
900
|
-
replaying = true;
|
|
901
|
-
try {
|
|
902
|
-
interaction.target.dispatchEvent(interaction.event);
|
|
903
|
-
} finally {
|
|
904
|
-
replaying = false;
|
|
905
|
-
}
|
|
906
|
-
releaseIfFinished();
|
|
907
|
-
}
|
|
908
|
-
function captureInteraction(event) {
|
|
909
|
-
if (!active || replaying) return;
|
|
910
|
-
const target = event.target;
|
|
911
|
-
if (!target || isPermanentlySkipped(target)) return;
|
|
912
|
-
const boundary = findDeferredBoundary(target);
|
|
913
|
-
if (!hydratingRoot && !boundary) return;
|
|
914
|
-
event.preventDefault();
|
|
915
|
-
event.stopImmediatePropagation();
|
|
916
|
-
const interaction = {
|
|
917
|
-
target,
|
|
918
|
-
event: cloneInteractionEvent(event)
|
|
919
|
-
};
|
|
920
|
-
if (hydratingRoot) {
|
|
921
|
-
queued.push(interaction);
|
|
922
|
-
return;
|
|
923
|
-
}
|
|
924
|
-
replayInteraction(interaction);
|
|
925
|
-
}
|
|
926
|
-
const installedTypes = [];
|
|
927
|
-
try {
|
|
928
|
-
for (const type of HYDRATION_INTERACTION_TYPES) {
|
|
929
|
-
root.addEventListener(type, captureInteraction, true);
|
|
930
|
-
installedTypes.push(type);
|
|
931
|
-
}
|
|
932
|
-
} catch (error) {
|
|
933
|
-
active = false;
|
|
934
|
-
for (const type of installedTypes) root.removeEventListener(type, captureInteraction, true);
|
|
935
|
-
throw error;
|
|
936
|
-
}
|
|
937
|
-
return {
|
|
938
|
-
registerDeferredBoundaries(boundaries) {
|
|
939
|
-
for (const boundary of boundaries) deferredBoundaries.add(boundary);
|
|
940
|
-
hadDeferredBoundaries ||= boundaries.length > 0;
|
|
941
|
-
},
|
|
942
|
-
setOnDeferredBoundariesDrained(callback) {
|
|
943
|
-
onDeferredBoundariesDrained = callback;
|
|
944
|
-
},
|
|
945
|
-
clearDeferredBoundaries() {
|
|
946
|
-
deferredBoundaries.clear();
|
|
947
|
-
releaseIfFinished();
|
|
948
|
-
},
|
|
949
|
-
complete() {
|
|
950
|
-
if (!active || !hydratingRoot) return;
|
|
951
|
-
hydratingRoot = false;
|
|
952
|
-
const pending = queued.splice(0);
|
|
953
|
-
for (const interaction of pending) replayInteraction(interaction);
|
|
954
|
-
releaseIfFinished();
|
|
955
|
-
},
|
|
956
|
-
abort() {
|
|
957
|
-
queued.length = 0;
|
|
958
|
-
deferredBoundaries.clear();
|
|
959
|
-
onDeferredBoundariesDrained = void 0;
|
|
960
|
-
hydratingRoot = false;
|
|
961
|
-
removeCaptureListeners();
|
|
962
|
-
}
|
|
963
|
-
};
|
|
964
|
-
}
|
|
965
|
-
//#endregion
|
|
966
|
-
//#region src/boot/hydrate-spa.ts
|
|
967
|
-
/**
|
|
968
|
-
* App bootstrap and mount
|
|
969
|
-
*/
|
|
970
|
-
/**
|
|
971
|
-
* hydrateSPA: Hydrate server-rendered HTML with an explicit route registry.
|
|
972
|
-
*/
|
|
973
|
-
async function hydrateSPA(config) {
|
|
974
|
-
assertExecutionModel("spa");
|
|
975
|
-
if (!config || typeof config !== "object") throw new Error("hydrateSPA requires a config object");
|
|
976
|
-
validateCspNonce(config.cspNonce);
|
|
977
|
-
if (!config.registry) throw new Error("hydrateSPA requires a route registry.");
|
|
978
|
-
const { manifest } = config.registry;
|
|
979
|
-
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.");
|
|
980
|
-
const rootElement = resolveRootElement(config.root);
|
|
981
|
-
if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
|
|
982
|
-
const interactionReplay = beginHydrationInteractionReplay(rootElement, (boundary) => activateHydrationBoundary(rootElement, boundary), config.hydrate?.skipSelectors);
|
|
983
|
-
try {
|
|
984
|
-
applyDeferredStreamPatches(rootElement);
|
|
985
|
-
const hydrationRenderData = takeHydrationRenderData(rootElement);
|
|
986
|
-
const hydrationQueryCache = hydrationRenderData?.resources;
|
|
987
|
-
if (hydrationQueryCache) hydrateDataRuntime(config.dataRuntime ?? getDefaultDataRuntime(), hydrationQueryCache);
|
|
988
|
-
const hydrationRenderDataForApp = hydrationRenderData;
|
|
989
|
-
configureScrollRestoration(config.scrollRestoration);
|
|
990
|
-
clearRouteState();
|
|
991
|
-
_applyManifest(manifest);
|
|
992
|
-
const routeAuth = config.auth ?? manifest.auth;
|
|
993
|
-
const appRouteSource = {
|
|
994
|
-
registry: config.registry,
|
|
995
|
-
auth: routeAuth,
|
|
996
|
-
runtime: createAppRenderRuntime({
|
|
997
|
-
framework: hydrationRenderData?.framework,
|
|
998
|
-
route: hydrationRenderData?.route,
|
|
999
|
-
hasRoute: hydrationRenderData !== null,
|
|
1000
|
-
routeRegistry: config.registry,
|
|
1001
|
-
routeAuth
|
|
1002
|
-
})
|
|
1003
|
-
};
|
|
1004
|
-
_setActiveRouteAuthOptions(routeAuth);
|
|
1005
|
-
const { path, href: currentUrl, resolved } = await resolveInitialRoute(routeAuth, {
|
|
1006
|
-
registry: config.registry,
|
|
1007
|
-
load: false
|
|
1008
|
-
});
|
|
1009
|
-
setServerLocation(currentUrl);
|
|
1010
|
-
if (isProductionEnvironment()) lockRouteRegistration();
|
|
1011
|
-
if (!resolved) throw new Error(`hydrateSPA: no route found for current path (${path}).`);
|
|
1012
|
-
if (resolved.kind === "redirect") throw new Error(`hydrateSPA: unresolved redirect for current path (${path}).`);
|
|
1013
|
-
await reconcileInitialRouteMetadata(resolved);
|
|
1014
|
-
const hydrationResolved = resolved.kind === "deny" ? {
|
|
1015
|
-
handler: bindDeniedRouteHandler(resolved.status),
|
|
1016
|
-
params: {}
|
|
1017
|
-
} : resolved;
|
|
1018
|
-
const mountHydratedRoot = (...args) => withIntrinsicHydrationAdoption(() => mountOrUpdate(args[0], args[1], {
|
|
1019
|
-
...args[2],
|
|
1020
|
-
cspNonce: config.cspNonce
|
|
1021
|
-
}));
|
|
1022
|
-
if (shouldVerifyHydrationMarkup(config)) {
|
|
1023
|
-
const { verifyHydrationSyncForUrl } = await import("./verify-hydration-ikaxotf7.js");
|
|
1024
|
-
if (!verifyHydrationSyncForUrl({
|
|
1025
|
-
root: rootElement,
|
|
1026
|
-
url: currentUrl,
|
|
1027
|
-
registry: config.registry,
|
|
1028
|
-
resolved: hydrationResolved,
|
|
1029
|
-
options: {
|
|
1030
|
-
data: hydrationRenderDataForApp?.resources,
|
|
1031
|
-
dataRuntime: config.dataRuntime ?? getDefaultDataRuntime(),
|
|
1032
|
-
envelope: hydrationRenderDataForApp ?? void 0,
|
|
1033
|
-
cspNonce: config.cspNonce
|
|
1034
|
-
}
|
|
1035
|
-
})) throw new Error("[Askr] Hydration mismatch detected. Server HTML does not match expected server-render output.");
|
|
1036
|
-
}
|
|
1037
|
-
const hydrateOptions = config.hydrate;
|
|
1038
|
-
if (hydrateOptions) {
|
|
1039
|
-
if (hydrateOptions.deferUntilIdle || hydrateOptions.deferBelowFold) {
|
|
1040
|
-
if (hydrationRenderDataForApp) startHydrationRenderPhase(hydrationRenderDataForApp);
|
|
1041
|
-
try {
|
|
1042
|
-
await applySelectiveHydration(rootElement, hydrationResolved, path, config.cleanupStrict, hydrateOptions, appRouteSource, {
|
|
1043
|
-
mountOrUpdate: mountHydratedRoot,
|
|
1044
|
-
registerAppNavigation,
|
|
1045
|
-
registerRootCleanupCallback,
|
|
1046
|
-
activateHydrationBoundary
|
|
1047
|
-
}, interactionReplay);
|
|
1048
|
-
} finally {
|
|
1049
|
-
if (hydrationRenderDataForApp) stopHydrationRenderPhase();
|
|
1050
|
-
}
|
|
1051
|
-
interactionReplay.complete();
|
|
1052
|
-
return;
|
|
1053
|
-
}
|
|
1054
|
-
if (hydrateOptions.skipSelectors?.length) markSkippedElements(rootElement, hydrateOptions.skipSelectors);
|
|
1055
|
-
}
|
|
1056
|
-
if (hydrationRenderDataForApp) startHydrationRenderPhase(hydrationRenderDataForApp);
|
|
1057
|
-
const listenerTransaction = beginHydrationListenerTransaction();
|
|
1058
|
-
beginHydrationDirectListenerMode();
|
|
1059
|
-
try {
|
|
1060
|
-
mountHydratedRoot(rootElement, resolved.kind === "deny" ? bindDeniedStatus(resolved.status) : bindResolvedRouteHandler(hydrationResolved), {
|
|
1061
|
-
cleanupStrict: config.cleanupStrict,
|
|
1062
|
-
appRuntime: appRouteSource.runtime
|
|
1063
|
-
});
|
|
1064
|
-
commitHydrationListenerTransaction(listenerTransaction);
|
|
1065
|
-
interactionReplay.complete();
|
|
1066
|
-
} catch (error) {
|
|
1067
|
-
discardHydrationListenerTransaction(listenerTransaction);
|
|
1068
|
-
throw error;
|
|
1069
|
-
} finally {
|
|
1070
|
-
endHydrationDirectListenerMode();
|
|
1071
|
-
if (hydrationRenderDataForApp) stopHydrationRenderPhase();
|
|
1072
|
-
}
|
|
1073
|
-
await registerAppNavigation(rootElement, path, { ...appRouteSource });
|
|
1074
|
-
} catch (error) {
|
|
1075
|
-
interactionReplay.abort();
|
|
1076
|
-
throw error;
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
//#endregion
|
|
1080
|
-
//#region src/boot/index.ts
|
|
1081
|
-
/** Stable bootstrap exports; each mounting mode owns its orchestration. */
|
|
1082
|
-
composeBrowserRuntime();
|
|
1083
|
-
//#endregion
|
|
1084
|
-
export { cleanupApp as a, createIslands as i, createSPA as n, hasApp as o, createIsland as r, installRendererBridge as s, hydrateSPA as t };
|