@askrjs/askr 0.2.4 → 0.3.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 +11 -0
- package/dist/access-CEA2pOry.js +1046 -0
- package/dist/actions/index.d.ts +16 -2
- package/dist/actions/index.js +5 -5
- package/dist/{activity-FirwPXA_.js → activity-oy2YFTFF.js} +10 -16
- package/dist/benchmark.js +8 -9
- package/dist/boot/index.d.ts +19 -9
- package/dist/boot/index.js +1 -1
- package/dist/{boot-tWyF6121.js → boot-BMBMWUGI.js} +680 -441
- package/dist/capabilities-Bbf0mlp9.js +42 -0
- package/dist/components/index.d.ts +11 -8
- package/dist/components/index.js +1 -1
- package/dist/{compose-ref-Bfaf4kUC.js → compose-ref-v0qGlHoN.js} +7 -1
- package/dist/control/index.d.ts +20 -2
- package/dist/control/index.js +1 -1
- package/dist/{control-C-wSaq9V.js → control-BR_mrX__.js} +4 -4
- package/dist/core.d.ts +163 -0
- package/dist/csp-nonce-BfFWcd0w.js +173 -0
- package/dist/data/index.d.ts +74 -2
- package/dist/data/index.js +3 -3
- package/dist/{data-ghKopAbp.js → data-2edW0jhF.js} +108 -122
- package/dist/{data-runtime-7t_Y0iv3.js → data-runtime-CxoY98lX.js} +22 -15
- package/dist/{deferred-CxjWCkIn.js → deferred-CbgNhf67.js} +5 -5
- package/dist/dom-renderer.d.ts +77 -0
- package/dist/domains/actions.d.ts +70 -0
- package/dist/domains/component.d.ts +119 -0
- package/dist/domains/context.d.ts +73 -0
- package/dist/domains/control.d.ts +328 -0
- package/dist/domains/data.d.ts +461 -0
- package/dist/domains/diagnostics.d.ts +21 -0
- package/dist/domains/lifecycle.d.ts +112 -0
- package/dist/domains/refs.d.ts +18 -0
- package/dist/domains/renderer.d.ts +127 -0
- package/dist/domains/routing.d.ts +501 -0
- package/dist/domains/scheduler.d.ts +79 -0
- package/dist/domains/server.d.ts +112 -0
- package/dist/domains/state.d.ts +129 -0
- package/dist/domains/telemetry.d.ts +29 -0
- package/dist/eager-control.d.ts +8 -0
- package/dist/{effect-CNwstBBD.js → effect-C3Zsc1k6.js} +14 -41
- package/dist/{types-DWVrKn9N.d.ts → elements.d.ts} +79 -42
- package/dist/{for-internal-DyBHfKJA.js → for-state-BDykeOJL.js} +598 -494
- package/dist/foundations/icon/index.d.ts +67 -16
- package/dist/foundations/icon/index.js +1 -1
- package/dist/foundations/index.d.ts +26 -3
- package/dist/foundations/index.js +1 -1
- package/dist/foundations/interactions/index.d.ts +73 -34
- package/dist/foundations/interactions/index.js +1 -1
- package/dist/foundations/state/index.d.ts +12 -5
- package/dist/foundations/state/index.js +1 -1
- package/dist/foundations/structures/index.d.ts +44 -3
- package/dist/foundations/structures/index.js +1 -1
- package/dist/foundations/utilities/index.d.ts +34 -3
- package/dist/foundations/utilities/index.js +1 -1
- package/dist/fx/index.d.ts +72 -25
- package/dist/fx/index.js +8 -8
- package/dist/index.d.ts +121 -4
- package/dist/index.js +316 -47
- package/dist/{component-internal-C4hKKQBF.js → instance-VAFYVJOa.js} +495 -283
- package/dist/{interactions-D102JxBR.js → interactions-CTeACdT_.js} +1 -1
- package/dist/jsx-dev-runtime.d.ts +51 -11
- package/dist/jsx-globals.d.ts +2 -0
- package/dist/jsx-runtime-CPJXAOBF.js +43 -0
- package/dist/jsx-runtime.d.ts +17 -3
- package/dist/jsx-runtime.js +2 -42
- package/dist/jsx.d.ts +108 -0
- package/dist/{manifest-D6rfj_jC.js → manifest-D1P3UNWV.js} +4 -9
- package/dist/{navigate-ZZu1IV0B.js → navigate-prC5V8XV.js} +125 -472
- package/dist/{navigate-F4sHgWbw.d.ts → navigation.d.ts} +8 -8
- package/dist/{readable-Di1fzSwm.js → notify-DxxuDPeY.js} +96 -34
- package/dist/{query-registry-Bua3cDyb.js → query-registry-LrYpQsDJ.js} +1 -1
- package/dist/{compose-ref-DOJHoW5X.d.ts → refs.d.ts} +11 -7
- package/dist/{ssr-Doog-AfJ.js → render-resolved-BC0W7_5b.js} +165 -558
- package/dist/{renderer-Cl23O0hV.js → renderer-CT0HeY58.js} +6825 -5692
- package/dist/{resolution-ClD-K7Lz.js → resolution-3DskUkjG.js} +189 -180
- package/dist/{resource-operation-Vz1S5h2k.js → resource-18-sFRl9.js} +98 -58
- package/dist/resources/index.d.ts +64 -12
- package/dist/resources/index.js +214 -8
- package/dist/route-activity.d.ts +21 -0
- package/dist/{route-matching-Bo6UqG-5.js → route-matching-CUzZ31gT.js} +68 -82
- package/dist/router/index.d.ts +378 -79
- package/dist/router/index.js +9 -9
- package/dist/router-internal-7nuNEHfG.js +36 -0
- package/dist/scope-Cq_iYbTR.js +403 -0
- package/dist/{selector-CsKuomw0.js → selector-BSPVkjKP.js} +26 -27
- package/dist/{shared-CYBH3F8I.js → shared-kU8yvryp.js} +3 -6
- package/dist/{snapshot-source-BB42xgwb.js → snapshot-source-An4iKajG.js} +1 -1
- package/dist/ssg/index.d.ts +51 -15
- package/dist/ssg/index.js +269 -209
- package/dist/ssr/index.d.ts +121 -68
- package/dist/ssr/index.js +2 -1
- package/dist/ssr-CiNm6Vzl.js +393 -0
- package/dist/{ssr-DSHd_Q9K.js → ssr-EPjwJH5R.js} +5 -1
- package/dist/{state-BGJgYSyQ.js → state-BXJfudq1.js} +10 -15
- package/dist/{structures-DFyK5AdQ.js → structures-CjEzfj4h.js} +37 -57
- package/dist/{index-CTpMmzgN.d.ts → structures.d.ts} +57 -36
- package/dist/testing/index.d.ts +123 -24
- package/dist/testing/index.js +24 -61
- package/dist/timer-C7uqnJVt.js +85 -0
- package/dist/{index-B1gu0_m0.d.ts → utilities.d.ts} +24 -13
- package/dist/{verify-hydration-BZPJ_kYa.js → verify-hydration-ikaxotf7.js} +8 -5
- package/package.json +12 -14
- package/dist/access-BucjYZcX.js +0 -509
- package/dist/activity-DooIuxQO.d.ts +0 -14
- package/dist/benchmark.d.ts +0 -26
- package/dist/cleanup-TXSsApEl.js +0 -535
- package/dist/component-cleanup-jdbgjkT1.js +0 -143
- package/dist/component-scope-4I3yEMkb.js +0 -195
- package/dist/control-Cxag1yqV.d.ts +0 -8
- package/dist/csp-nonce-9NFUb7hJ.js +0 -72
- package/dist/css-BuPaTOfe.js +0 -70
- package/dist/fastlane-BKZduiBQ.js +0 -166
- package/dist/index-Dgjbo2gU.d.ts +0 -1456
- package/dist/jsx-runtime-Cg7GqY1S.d.ts +0 -2
- package/dist/jsx-runtime-gVIaTPw0.d.ts +0 -38
- package/dist/lifecycle-batch-63aXFf2S.js +0 -253
- package/dist/lifecycle-operations-Q0k3EFnG.js +0 -287
|
@@ -1,22 +1,20 @@
|
|
|
1
|
+
import { v as logger } from "./access-CEA2pOry.js";
|
|
2
|
+
import { P as isPromiseLike, S as markVNodeTreeWithContextFrame, T as cleanupComponent, g as getExecutionContextFrame, n as createComponentInstance, p as callWithContext, x as getVNodeContextFrame } from "./instance-VAFYVJOa.js";
|
|
3
|
+
import { s as endComponentScope, u as enterRenderScopedComponent } from "./scope-Cq_iYbTR.js";
|
|
1
4
|
import { t as __CONTROL_BOUNDARY__ } from "./control-C-ApnFs1.js";
|
|
2
5
|
import { t as __ERROR_BOUNDARY__ } from "./vnode-D24p6IW2.js";
|
|
3
|
-
import { y as logger } from "./access-BucjYZcX.js";
|
|
4
|
-
import { f as getCurrentComponentInstance, w as setCurrentComponentInstance } from "./component-scope-4I3yEMkb.js";
|
|
5
6
|
import { i as isFragmentType, n as Fragment, t as ELEMENT_TYPE } from "./jsx-DRS-qjiN.js";
|
|
6
|
-
import { _ as
|
|
7
|
-
import { t as cleanupComponent } from "./component-cleanup-jdbgjkT1.js";
|
|
8
|
-
import { p as isPromiseLike } from "./lifecycle-batch-63aXFf2S.js";
|
|
9
|
-
import { _ as setControlBoundaryContextFrame, g as evaluateShowState, h as evaluateCaseState, i as evaluateForState, o as rollbackForStateTransaction, r as commitForStateTransaction } from "./for-internal-DyBHfKJA.js";
|
|
7
|
+
import { _ as setControlBoundaryContextFrame, g as evaluateShowState, h as evaluateCaseState, i as evaluateForState, o as rollbackForStateTransaction, r as commitForStateTransaction } from "./for-state-BDykeOJL.js";
|
|
10
8
|
import { C as SSRDataMissingError, _ as isEmptyPageRenderEnvelope, f as stopRenderPhase$1, h as createPageRenderEnvelope, t as configureRenderContextProvider, u as startRenderPhase$1, v as isPageRenderEnvelope, x as withHydrationRenderUrl } from "./render-context-B7ohlh6t.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
9
|
+
import { a as booleanAttributeValue, c as normalizeStylePropertyName, i as sanitizeCssValue, l as getPublicAttributeName, o as isAriaAttribute, r as validateCspNonce, s as isSkippedProp, t as CspNonceScope } from "./csp-nonce-BfFWcd0w.js";
|
|
10
|
+
import { r as isUnsafeUrlAttribute } from "./router-internal-7nuNEHfG.js";
|
|
13
11
|
import { n as SSR_PORTAL_HOST, t as SSR_PORTAL_ANCHOR } from "./portal-w3oQ6oQU.js";
|
|
14
|
-
import { t as createDataRuntime } from "./data-runtime-
|
|
15
|
-
import {
|
|
16
|
-
import { a as resolveRouteFromRoutes, c as removeRouteBasePath, s as normalizeRouteBasePath, t as _resolveRouteMatchFromRoutes } from "./route-matching-Bo6UqG-5.js";
|
|
17
|
-
import { a as resolveRouteRequest, d as DEFERRED_BOUNDARY, l as validateRouteHydrationData, n as getRouteRenderContext, o as getRouteHydrationMetadata, p as isDeferred } from "./resolution-ClD-K7Lz.js";
|
|
12
|
+
import { t as createDataRuntime } from "./data-runtime-CxoY98lX.js";
|
|
13
|
+
import { i as dehydrateDataRuntime } from "./query-registry-LrYpQsDJ.js";
|
|
18
14
|
import { n as disposeRegisteredDefaultPortalScope, r as getDefaultPortalHost } from "./default-portal-runtime-DvMvWJVp.js";
|
|
19
|
-
import {
|
|
15
|
+
import { S as isDeferred, b as DEFERRED_BOUNDARY, h as getRouteHydrationMetadata, v as validateRouteHydrationData } from "./resolution-3DskUkjG.js";
|
|
16
|
+
import { a as resolveRouteFromRoutes, c as removeRouteBasePath, s as normalizeRouteBasePath } from "./route-matching-CUzZ31gT.js";
|
|
17
|
+
import { t as SSR_RENDER_DATA_ATTR } from "./ssr-EPjwJH5R.js";
|
|
20
18
|
//#region src/ssr/escape.ts
|
|
21
19
|
/**
|
|
22
20
|
* HTML escaping utilities for SSR
|
|
@@ -47,7 +45,7 @@ const MAX_STYLE_PROP_CACHE_SIZE = 512;
|
|
|
47
45
|
function toKebabCached(prop) {
|
|
48
46
|
const cached = STYLE_PROP_CACHE.get(prop);
|
|
49
47
|
if (cached !== void 0) return cached;
|
|
50
|
-
const kebab = prop
|
|
48
|
+
const kebab = normalizeStylePropertyName(prop);
|
|
51
49
|
if (STYLE_PROP_CACHE.size < MAX_STYLE_PROP_CACHE_SIZE) STYLE_PROP_CACHE.set(prop, kebab);
|
|
52
50
|
return kebab;
|
|
53
51
|
}
|
|
@@ -224,6 +222,7 @@ function createRenderContext(seed = 12345, opts = {}) {
|
|
|
224
222
|
* Concurrency-safe in Node.js via AsyncLocalStorage.
|
|
225
223
|
*/
|
|
226
224
|
function withRenderContext(ctx, fn) {
|
|
225
|
+
ensureRenderContextProvider();
|
|
227
226
|
ensureRenderContextAccessor();
|
|
228
227
|
if (renderContextAccessor) return renderContextAccessor.run(ctx, fn);
|
|
229
228
|
const prev = fallbackStack;
|
|
@@ -261,6 +260,7 @@ async function getAsyncRenderContextAccessor() {
|
|
|
261
260
|
}
|
|
262
261
|
/** Run `fn` with `ctx` as the active SSR render context, using async-local storage. */
|
|
263
262
|
async function withRenderContextAsync(ctx, fn) {
|
|
263
|
+
ensureRenderContextProvider();
|
|
264
264
|
const accessor = await getAsyncRenderContextAccessor();
|
|
265
265
|
if (accessor) return accessor.run(ctx, () => Promise.resolve(fn()));
|
|
266
266
|
throw new Error(FALLBACK_ASYNC_CONTEXT_ERROR);
|
|
@@ -274,7 +274,21 @@ function getRenderContext() {
|
|
|
274
274
|
if (renderContextAccessor) return renderContextAccessor.getStore() ?? null;
|
|
275
275
|
return fallbackStack;
|
|
276
276
|
}
|
|
277
|
-
|
|
277
|
+
let renderContextProvided = false;
|
|
278
|
+
/**
|
|
279
|
+
* Publish this module's render-context accessor to `common/render-context`.
|
|
280
|
+
*
|
|
281
|
+
* Runtime code (resources, portals, deferred routes) asks that module for the
|
|
282
|
+
* active SSR context, and it cannot import SSR. Installing the accessor at
|
|
283
|
+
* module scope meant the answer depended on whether anything had imported this
|
|
284
|
+
* module; entering a render context composes it instead. Consumers only ever
|
|
285
|
+
* observe a context from inside one, so this covers every reader.
|
|
286
|
+
*/
|
|
287
|
+
function ensureRenderContextProvider() {
|
|
288
|
+
if (renderContextProvided) return;
|
|
289
|
+
renderContextProvided = true;
|
|
290
|
+
configureRenderContextProvider({ getRenderContext });
|
|
291
|
+
}
|
|
278
292
|
/**
|
|
279
293
|
* Centralized SSR enforcement helper — throws a consistent error when async
|
|
280
294
|
* data is encountered during synchronous SSR.
|
|
@@ -283,75 +297,6 @@ function throwSSRDataMissing() {
|
|
|
283
297
|
throw new SSRDataMissingError();
|
|
284
298
|
}
|
|
285
299
|
//#endregion
|
|
286
|
-
//#region src/ssr/route-policy-resolution.ts
|
|
287
|
-
const MAX_SSR_REDIRECTS = 20;
|
|
288
|
-
function getRouteRequestResultSync(result) {
|
|
289
|
-
if (isPromiseLike(result)) throwSSRDataMissing();
|
|
290
|
-
return result;
|
|
291
|
-
}
|
|
292
|
-
function buildDeniedRoute(route, status) {
|
|
293
|
-
return {
|
|
294
|
-
...route,
|
|
295
|
-
handler: () => ({
|
|
296
|
-
type: "div",
|
|
297
|
-
props: { "data-route-denied": String(status) },
|
|
298
|
-
children: [String(status)]
|
|
299
|
-
})
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
function routeHasSSRLoader(manifest, route) {
|
|
303
|
-
const routeFallbackPrefix = route.fallbackPrefix;
|
|
304
|
-
return manifest.records.some((record) => {
|
|
305
|
-
const recordFallbackPrefix = record.fallbackPrefix;
|
|
306
|
-
return record.path === route.path && recordFallbackPrefix === routeFallbackPrefix && typeof record.options?.loader === "function";
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
function resolvePolicyAwareSSRRoute(opts, routeTable) {
|
|
310
|
-
const manifest = opts.registry.manifest;
|
|
311
|
-
let href = opts.url;
|
|
312
|
-
const visited = /* @__PURE__ */ new Set();
|
|
313
|
-
for (let redirects = 0; redirects <= MAX_SSR_REDIRECTS; redirects += 1) {
|
|
314
|
-
const logicalHref = removeRouteBasePath(href, normalizeRouteBasePath(manifest.basePath));
|
|
315
|
-
if (logicalHref === void 0) throw new Error(`SSR: no route found for url: ${href}`);
|
|
316
|
-
const logicalUrl = new URL(logicalHref, "http://localhost");
|
|
317
|
-
const matched = _resolveRouteMatchFromRoutes(logicalUrl.pathname, routeTable);
|
|
318
|
-
if (!matched) throw new Error(`SSR: no route found for url: ${href}`);
|
|
319
|
-
const resolved = getRouteRequestResultSync(resolveRouteRequest(href, {
|
|
320
|
-
registry: opts.registry,
|
|
321
|
-
mode: "ssr",
|
|
322
|
-
auth: opts.auth,
|
|
323
|
-
authContext: opts.authContext,
|
|
324
|
-
request: opts.request,
|
|
325
|
-
signal: opts.signal
|
|
326
|
-
}));
|
|
327
|
-
if (resolved === null) throw new Error(`SSR: no route found for url: ${href}`);
|
|
328
|
-
if (resolved.kind === "redirect") {
|
|
329
|
-
const redirectTarget = new URL(resolved.to, "http://localhost");
|
|
330
|
-
const redirectHref = `${redirectTarget.pathname}${redirectTarget.search}${redirectTarget.hash}`;
|
|
331
|
-
if (redirectHref === href || visited.has(redirectHref)) throw new Error(`[Askr] SSR redirect cycle detected at ${redirectHref}.`);
|
|
332
|
-
if (redirects >= MAX_SSR_REDIRECTS) throw new Error(`[Askr] SSR redirect limit exceeded (${MAX_SSR_REDIRECTS}).`);
|
|
333
|
-
visited.add(redirectHref);
|
|
334
|
-
href = redirectHref;
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
if (resolved.kind === "deny") return {
|
|
338
|
-
url: href,
|
|
339
|
-
route: buildDeniedRoute(matched.route, resolved.status),
|
|
340
|
-
params: matched.params
|
|
341
|
-
};
|
|
342
|
-
return {
|
|
343
|
-
url: href,
|
|
344
|
-
route: {
|
|
345
|
-
...matched.route,
|
|
346
|
-
handler: routeHasSSRLoader(manifest, matched.route) ? resolved.handler : matched.route.handler
|
|
347
|
-
},
|
|
348
|
-
params: matched.params,
|
|
349
|
-
authContext: getRouteRenderContext(resolved)?.auth
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
throw new Error(`[Askr] SSR redirect limit exceeded (${MAX_SSR_REDIRECTS}).`);
|
|
353
|
-
}
|
|
354
|
-
//#endregion
|
|
355
300
|
//#region src/ssr/sink.ts
|
|
356
301
|
var StringSink = class StringSink {
|
|
357
302
|
constructor() {
|
|
@@ -435,129 +380,10 @@ var StreamSink = class {
|
|
|
435
380
|
}
|
|
436
381
|
};
|
|
437
382
|
//#endregion
|
|
438
|
-
//#region src/ssr/route-render.ts
|
|
439
|
-
/** Resolve a URL against a route registry for SSR, applying auth/policies before render. */
|
|
440
|
-
async function resolveRequest(opts) {
|
|
441
|
-
if (!opts?.registry) throw new TypeError("resolveRequest requires opts.registry.");
|
|
442
|
-
const { url, auth, authContext, request, signal } = opts;
|
|
443
|
-
return await resolveRouteRequest(url, {
|
|
444
|
-
registry: opts.registry,
|
|
445
|
-
mode: "ssr",
|
|
446
|
-
auth,
|
|
447
|
-
authContext,
|
|
448
|
-
request,
|
|
449
|
-
signal
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
function resolveSSRRouteSource(source) {
|
|
453
|
-
if (source.registry.routes.length === 0) throw new Error("SSR requires a non-empty route registry.");
|
|
454
|
-
return source.registry.routes.map((route) => ({
|
|
455
|
-
...route,
|
|
456
|
-
path: route.path,
|
|
457
|
-
handler: route.handler,
|
|
458
|
-
namespace: route.namespace
|
|
459
|
-
}));
|
|
460
|
-
}
|
|
461
|
-
function resolveSSRRouteRender(opts) {
|
|
462
|
-
const { seed = 12345, data, document, styleRegistrationValidation = "warn" } = opts;
|
|
463
|
-
const cspNonce = validateCspNonce(opts.cspNonce);
|
|
464
|
-
const routeTable = resolveSSRRouteSource(opts);
|
|
465
|
-
const resolvedRoute = resolvePolicyAwareSSRRoute(opts, routeTable);
|
|
466
|
-
const requestUrl = new URL(resolvedRoute.url, "http://localhost");
|
|
467
|
-
const ctx = createRenderContext(seed, {
|
|
468
|
-
url: resolvedRoute.url,
|
|
469
|
-
basePath: opts.registry.manifest.basePath,
|
|
470
|
-
data,
|
|
471
|
-
params: resolvedRoute.params,
|
|
472
|
-
routes: routeTable,
|
|
473
|
-
routeAuth: opts.auth ?? opts.registry.manifest.auth,
|
|
474
|
-
signal: opts.signal ?? opts.request?.signal,
|
|
475
|
-
dataRuntime: opts.dataRuntime,
|
|
476
|
-
queryPrefetch: opts.queryPrefetch,
|
|
477
|
-
authContext: resolvedRoute.authContext,
|
|
478
|
-
envelope: opts.envelope,
|
|
479
|
-
cspNonce
|
|
480
|
-
});
|
|
481
|
-
return {
|
|
482
|
-
url: resolvedRoute.url,
|
|
483
|
-
requestUrl,
|
|
484
|
-
route: resolvedRoute.route,
|
|
485
|
-
params: resolvedRoute.params,
|
|
486
|
-
seed,
|
|
487
|
-
data,
|
|
488
|
-
ctx,
|
|
489
|
-
document,
|
|
490
|
-
cspNonce,
|
|
491
|
-
styleRegistrationValidation
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
function buildDocumentRenderArgs(resolved, appHtml) {
|
|
495
|
-
return {
|
|
496
|
-
appHtml,
|
|
497
|
-
context: {
|
|
498
|
-
mode: "ssr",
|
|
499
|
-
url: resolved.url,
|
|
500
|
-
pathname: resolved.requestUrl.pathname,
|
|
501
|
-
search: resolved.requestUrl.search,
|
|
502
|
-
hash: resolved.requestUrl.hash,
|
|
503
|
-
params: resolved.params,
|
|
504
|
-
data: resolved.data,
|
|
505
|
-
seed: resolved.seed,
|
|
506
|
-
route: {
|
|
507
|
-
path: resolved.route.path,
|
|
508
|
-
namespace: resolved.route.namespace
|
|
509
|
-
},
|
|
510
|
-
cspNonce: resolved.cspNonce,
|
|
511
|
-
styles: Array.from(resolved.ctx.ssrStyles.values())
|
|
512
|
-
}
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
function renderResolvedRouteAppToSink(resolved, sink, host) {
|
|
516
|
-
host.renderAppToSink({
|
|
517
|
-
route: resolved.route,
|
|
518
|
-
params: resolved.params,
|
|
519
|
-
data: resolved.data,
|
|
520
|
-
ctx: resolved.ctx,
|
|
521
|
-
sink
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
function renderToSinkInternal(opts, host) {
|
|
525
|
-
const { sink, ...renderOptions } = opts;
|
|
526
|
-
return withTelemetry(renderOptions.telemetry?.ssrRender, {}, () => {
|
|
527
|
-
const resolved = resolveSSRRouteRender(renderOptions);
|
|
528
|
-
if (!resolved.document) {
|
|
529
|
-
renderResolvedRouteAppToSink(resolved, sink, host);
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
const appSink = new StringSink();
|
|
533
|
-
renderResolvedRouteAppToSink(resolved, appSink, host);
|
|
534
|
-
appSink.end();
|
|
535
|
-
sink.write(renderDocument(resolved.document, buildDocumentRenderArgs(resolved, appSink.toString()), "renderToString()/renderToStream()", resolved.styleRegistrationValidation));
|
|
536
|
-
});
|
|
537
|
-
}
|
|
538
|
-
function renderRouteToString(opts, host) {
|
|
539
|
-
const sink = new StringSink();
|
|
540
|
-
renderToSinkInternal({
|
|
541
|
-
...opts,
|
|
542
|
-
sink
|
|
543
|
-
}, host);
|
|
544
|
-
sink.end();
|
|
545
|
-
return sink.toString();
|
|
546
|
-
}
|
|
547
|
-
function renderRouteToStream(opts, host) {
|
|
548
|
-
const sink = new StreamSink(opts.onChunk, opts.onComplete);
|
|
549
|
-
renderToSinkInternal({
|
|
550
|
-
...opts,
|
|
551
|
-
sink
|
|
552
|
-
}, host);
|
|
553
|
-
sink.end();
|
|
554
|
-
}
|
|
555
|
-
//#endregion
|
|
556
383
|
//#region src/ssr/component-runtime.ts
|
|
557
384
|
function executeComponentSync(component, props, ctx, ownerFrame = null) {
|
|
558
385
|
try {
|
|
559
|
-
const
|
|
560
|
-
const temp = createComponentInstance("ssr-temp", component, props || {}, null);
|
|
386
|
+
const temp = createComponentInstance("ssr-temp", component, props || {}, null, null);
|
|
561
387
|
temp.ssr = true;
|
|
562
388
|
temp.ownerFrame = ownerFrame;
|
|
563
389
|
temp.portalScope = temp;
|
|
@@ -576,7 +402,7 @@ function executeComponentSync(component, props, ctx, ownerFrame = null) {
|
|
|
576
402
|
}
|
|
577
403
|
if (cleanupError) throw cleanupError;
|
|
578
404
|
});
|
|
579
|
-
|
|
405
|
+
const previous = enterRenderScopedComponent(temp, 0);
|
|
580
406
|
try {
|
|
581
407
|
const executionFrame = getExecutionContextFrame(ownerFrame);
|
|
582
408
|
const result = callWithContext(executionFrame, component, props || {}, { ssr: ctx });
|
|
@@ -591,7 +417,7 @@ function executeComponentSync(component, props, ctx, ownerFrame = null) {
|
|
|
591
417
|
}
|
|
592
418
|
return markVNodeTreeWithContextFrame(result, ownerFrame);
|
|
593
419
|
} finally {
|
|
594
|
-
|
|
420
|
+
endComponentScope(previous);
|
|
595
421
|
}
|
|
596
422
|
} finally {}
|
|
597
423
|
}
|
|
@@ -757,9 +583,6 @@ const escapedAttrValueCache = /* @__PURE__ */ new Map();
|
|
|
757
583
|
function isEventHandler(key) {
|
|
758
584
|
return key.length >= 2 && key.slice(0, 2).toLowerCase() === "on";
|
|
759
585
|
}
|
|
760
|
-
function isAriaAttribute(key) {
|
|
761
|
-
return key.length > 5 && key.slice(0, 5).toLowerCase() === "aria-";
|
|
762
|
-
}
|
|
763
586
|
function assertAttributeName(name) {
|
|
764
587
|
if (!/^[A-Za-z_:][A-Za-z0-9_.:-]*$/.test(name)) throw new TypeError(`Invalid SSR attribute name: ${JSON.stringify(name)}`);
|
|
765
588
|
}
|
|
@@ -782,7 +605,7 @@ function renderAttrsDirect(props, sink) {
|
|
|
782
605
|
const propsObj = props;
|
|
783
606
|
for (const key in propsObj) {
|
|
784
607
|
const value = propsObj[key];
|
|
785
|
-
if (key
|
|
608
|
+
if (isSkippedProp(key) || key === "dangerouslySetInnerHTML") continue;
|
|
786
609
|
if (isEventHandler(key)) continue;
|
|
787
610
|
if (key.charCodeAt(0) === 95) continue;
|
|
788
611
|
const attrName = getPublicAttributeName(key);
|
|
@@ -799,6 +622,12 @@ function renderAttrsDirect(props, sink) {
|
|
|
799
622
|
if (value === true) {
|
|
800
623
|
sink.write(" ");
|
|
801
624
|
sink.write(attrName);
|
|
625
|
+
const booleanValue = booleanAttributeValue(attrName);
|
|
626
|
+
if (booleanValue) {
|
|
627
|
+
sink.write("=\"");
|
|
628
|
+
sink.write(booleanValue);
|
|
629
|
+
sink.write("\"");
|
|
630
|
+
}
|
|
802
631
|
continue;
|
|
803
632
|
}
|
|
804
633
|
if (value === null || value === void 0 || value === false && !isAriaAttribute(attrName)) continue;
|
|
@@ -811,44 +640,6 @@ function renderAttrsDirect(props, sink) {
|
|
|
811
640
|
sink.write("\"");
|
|
812
641
|
}
|
|
813
642
|
}
|
|
814
|
-
function renderAttrs(props, opts) {
|
|
815
|
-
if (!props || typeof props !== "object") return opts?.returnDangerousHtml ? { attrs: "" } : "";
|
|
816
|
-
const attrParts = [];
|
|
817
|
-
let dangerousHtml;
|
|
818
|
-
const propsObj = props;
|
|
819
|
-
for (const key in propsObj) {
|
|
820
|
-
const value = propsObj[key];
|
|
821
|
-
if (key === "children" || key === "imperativeChildren") continue;
|
|
822
|
-
if (key === "key" || key === "ref") continue;
|
|
823
|
-
if (key === "dangerouslySetInnerHTML") {
|
|
824
|
-
if (value && typeof value === "object" && "__html" in value) dangerousHtml = String(value.__html);
|
|
825
|
-
continue;
|
|
826
|
-
}
|
|
827
|
-
if (isEventHandler(key)) continue;
|
|
828
|
-
if (key.charCodeAt(0) === 95) continue;
|
|
829
|
-
const attrName = key === "class" ? "class" : getPublicAttributeName(key);
|
|
830
|
-
assertAttributeName(attrName);
|
|
831
|
-
if (attrName === "style") {
|
|
832
|
-
const css = typeof value === "string" ? value : styleObjToCss(value);
|
|
833
|
-
if (css === null || css === "") continue;
|
|
834
|
-
attrParts.push(` style="${getEscapedAttrValue(css)}"`);
|
|
835
|
-
continue;
|
|
836
|
-
}
|
|
837
|
-
if (value === true) attrParts.push(` ${attrName}`);
|
|
838
|
-
else if (value === null || value === void 0 || value === false && !isAriaAttribute(attrName)) continue;
|
|
839
|
-
else {
|
|
840
|
-
const strValue = String(value);
|
|
841
|
-
if (isUnsafeUrlAttribute(attrName, strValue)) continue;
|
|
842
|
-
attrParts.push(` ${attrName}="${getEscapedAttrValue(strValue)}"`);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
const result = attrParts.join("");
|
|
846
|
-
if (opts?.returnDangerousHtml) return {
|
|
847
|
-
attrs: result,
|
|
848
|
-
dangerousHtml
|
|
849
|
-
};
|
|
850
|
-
return result;
|
|
851
|
-
}
|
|
852
643
|
//#endregion
|
|
853
644
|
//#region src/ssr/hydration-data.ts
|
|
854
645
|
const DEFERRED_PAYLOAD = "__askr_deferred__";
|
|
@@ -898,33 +689,11 @@ function stopRenderPhase() {
|
|
|
898
689
|
//#region src/ssr/render-sync.ts
|
|
899
690
|
const RANGE_START = "<!--askr-range-start-->";
|
|
900
691
|
const RANGE_END = "<!--askr-range-end-->";
|
|
901
|
-
function renderDeferredBoundarySync(node, ctx) {
|
|
902
|
-
const id = String(node.props?.["id"] ?? "");
|
|
903
|
-
const pending = node.props?.["pending"];
|
|
904
|
-
return `<askr-resolve data-askr-deferred="${id}">${renderRenderableSync(pending, ctx)}</askr-resolve>`;
|
|
905
|
-
}
|
|
906
692
|
function isMultiRangeChild(child) {
|
|
907
693
|
if (Array.isArray(child)) return true;
|
|
908
694
|
if (!child || typeof child !== "object" || !("type" in child)) return false;
|
|
909
695
|
return isFragmentType(child.type);
|
|
910
696
|
}
|
|
911
|
-
function renderForRangeChildSync(child, ctx) {
|
|
912
|
-
if (child && typeof child === "object" && "type" in child && typeof child.type === "function") {
|
|
913
|
-
const vnode = child;
|
|
914
|
-
return renderForRangeChildSync(inheritRenderableKey(vnode, executeComponentSync(vnode.type, vnode.props, ctx, getVNodeContextFrame(vnode) ?? null)), ctx);
|
|
915
|
-
}
|
|
916
|
-
const rendered = renderChildSync(child, ctx);
|
|
917
|
-
return isMultiRangeChild(child) ? `${RANGE_START}${rendered}${RANGE_END}` : rendered;
|
|
918
|
-
}
|
|
919
|
-
function renderControlChildrenSync(node, ctx) {
|
|
920
|
-
const controlState = getControlBoundaryState(node);
|
|
921
|
-
return withControlBoundaryChildren(node, (children) => {
|
|
922
|
-
const values = children ?? [];
|
|
923
|
-
if (controlState?.kind === "for") return values.map((child) => renderForRangeChildSync(child, ctx)).join("");
|
|
924
|
-
const rendered = renderChildrenSync(values, ctx);
|
|
925
|
-
return values.length === 1 && isMultiRangeChild(values[0]) ? `${RANGE_START}${rendered}${RANGE_END}` : values.length !== 1 ? `${RANGE_START}${rendered}${RANGE_END}` : rendered;
|
|
926
|
-
});
|
|
927
|
-
}
|
|
928
697
|
function inheritRenderableKey(source, result) {
|
|
929
698
|
const inheritedKey = source.key;
|
|
930
699
|
if (inheritedKey === void 0 || inheritedKey === null) return result;
|
|
@@ -938,13 +707,18 @@ function inheritRenderableKey(source, result) {
|
|
|
938
707
|
}
|
|
939
708
|
return result;
|
|
940
709
|
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
710
|
+
/**
|
|
711
|
+
* Render a value to a string through the streaming renderer.
|
|
712
|
+
*
|
|
713
|
+
* Portal resolution splices content into the finished document by token, so it
|
|
714
|
+
* genuinely needs a string — but it gets one from the same renderer everything
|
|
715
|
+
* else uses rather than from a second implementation.
|
|
716
|
+
*/
|
|
717
|
+
function renderRenderableToString(value, ctx) {
|
|
718
|
+
const sink = new StringSink();
|
|
719
|
+
renderRenderableSyncToSink(value, sink, ctx);
|
|
720
|
+
sink.end();
|
|
721
|
+
return sink.toString();
|
|
948
722
|
}
|
|
949
723
|
function resolveSSRPortals(html, ctx) {
|
|
950
724
|
let resolved = html;
|
|
@@ -957,7 +731,7 @@ function resolveSSRPortals(html, ctx) {
|
|
|
957
731
|
for (const host of slot.hosts) {
|
|
958
732
|
if (renderedHosts.has(host.token)) continue;
|
|
959
733
|
foundHost = true;
|
|
960
|
-
const content = activeHosts.has(host.token) && slot.hasValue ?
|
|
734
|
+
const content = activeHosts.has(host.token) && slot.hasValue ? renderRenderableToString(slot.value, ctx) : "";
|
|
961
735
|
resolved = resolved.replace(host.token, () => host.automatic && content === "" && slot.hasValue && explicitHosts.length === 0 ? host.token : content);
|
|
962
736
|
renderedHosts.add(host.token);
|
|
963
737
|
}
|
|
@@ -987,9 +761,39 @@ var SSRPortalSink = class {
|
|
|
987
761
|
this.sink.write(resolveSSRPortals(this.bufferedChunks.join(""), ctx));
|
|
988
762
|
}
|
|
989
763
|
};
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
764
|
+
/**
|
|
765
|
+
* Collects writes so they can be published to a real sink, or dropped.
|
|
766
|
+
*
|
|
767
|
+
* An ErrorBoundary is transactional: markup its subtree produced before a
|
|
768
|
+
* descendant threw must never reach the response, or the fallback lands inside
|
|
769
|
+
* a half-written element. A sink cannot take output back, so the protected
|
|
770
|
+
* subtree renders into one of these first.
|
|
771
|
+
*
|
|
772
|
+
* Portal host writes are recorded rather than flattened, because reaching the
|
|
773
|
+
* real sink through `writePortalHost` is what puts it into portal-resolving
|
|
774
|
+
* mode; replaying them preserves both that signal and the original order.
|
|
775
|
+
*/
|
|
776
|
+
var BufferedSink = class {
|
|
777
|
+
constructor() {
|
|
778
|
+
this.operations = [];
|
|
779
|
+
}
|
|
780
|
+
write(html) {
|
|
781
|
+
if (html) this.operations.push({
|
|
782
|
+
portalHost: false,
|
|
783
|
+
text: html
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
writePortalHost(token) {
|
|
787
|
+
this.operations.push({
|
|
788
|
+
portalHost: true,
|
|
789
|
+
text: token
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
publishTo(sink) {
|
|
793
|
+
for (const operation of this.operations) if (operation.portalHost && sink.writePortalHost) sink.writePortalHost(operation.text);
|
|
794
|
+
else sink.write(operation.text);
|
|
795
|
+
}
|
|
796
|
+
};
|
|
993
797
|
function renderRenderableSyncToSink(value, sink, ctx) {
|
|
994
798
|
if (value === null || value === void 0 || value === false) return;
|
|
995
799
|
if (typeof value === "string") {
|
|
@@ -1009,22 +813,58 @@ function renderRenderableSyncToSink(value, sink, ctx) {
|
|
|
1009
813
|
function renderChildSyncToSink(child, sink, ctx) {
|
|
1010
814
|
renderRenderableSyncToSink(child, sink, ctx);
|
|
1011
815
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
816
|
+
/**
|
|
817
|
+
* Write a deferred boundary's pending content inside its resolve wrapper.
|
|
818
|
+
*
|
|
819
|
+
* The wrapper is known from the node's props, so the children stream into the
|
|
820
|
+
* sink between the two markers rather than being rendered to a string first.
|
|
821
|
+
*/
|
|
822
|
+
function renderDeferredBoundaryToSink(node, sink, ctx) {
|
|
823
|
+
const id = String(node.props?.["id"] ?? "");
|
|
824
|
+
sink.write(`<askr-resolve data-askr-deferred="${id}">`);
|
|
825
|
+
renderRenderableSyncToSink(node.props?.["pending"], sink, ctx);
|
|
826
|
+
sink.write("</askr-resolve>");
|
|
827
|
+
}
|
|
828
|
+
/** Write one `For` row, bracketing it with range markers when it spans several nodes. */
|
|
829
|
+
function renderForRangeChildToSink(child, sink, ctx) {
|
|
830
|
+
if (child && typeof child === "object" && "type" in child && typeof child.type === "function") {
|
|
831
|
+
const vnode = child;
|
|
832
|
+
renderForRangeChildToSink(inheritRenderableKey(vnode, executeComponentSync(vnode.type, vnode.props, ctx, getVNodeContextFrame(vnode) ?? null)), sink, ctx);
|
|
833
|
+
return;
|
|
1019
834
|
}
|
|
1020
|
-
|
|
835
|
+
const spansRange = isMultiRangeChild(child);
|
|
836
|
+
if (spansRange) sink.write(RANGE_START);
|
|
837
|
+
renderChildSyncToSink(child, sink, ctx);
|
|
838
|
+
if (spansRange) sink.write(RANGE_END);
|
|
1021
839
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
840
|
+
/**
|
|
841
|
+
* Write a control boundary's children.
|
|
842
|
+
*
|
|
843
|
+
* Whether the boundary needs range markers is decided from the children
|
|
844
|
+
* themselves, before any of them render, so the decision costs nothing and the
|
|
845
|
+
* subtree can stream out as it is produced.
|
|
846
|
+
*/
|
|
847
|
+
function renderControlChildrenToSink(node, sink, ctx) {
|
|
848
|
+
const controlState = getControlBoundaryState(node);
|
|
849
|
+
withControlBoundaryChildren(node, (children) => {
|
|
850
|
+
const values = children ?? [];
|
|
851
|
+
if (controlState?.kind === "for") {
|
|
852
|
+
for (let index = 0; index < values.length; index += 1) renderForRangeChildToSink(values[index], sink, ctx);
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
const spansRange = values.length !== 1 || isMultiRangeChild(values[0]);
|
|
856
|
+
if (spansRange) sink.write(RANGE_START);
|
|
857
|
+
renderChildrenSyncToSink(values, sink, ctx);
|
|
858
|
+
if (spansRange) sink.write(RANGE_END);
|
|
859
|
+
});
|
|
1025
860
|
}
|
|
1026
861
|
function renderErrorBoundaryFallbackValueToSink(fallback, error, reset, sink, ctx) {
|
|
1027
|
-
|
|
862
|
+
const nextValue = resolveErrorBoundaryFallbackNode(fallback, error, reset);
|
|
863
|
+
if (Array.isArray(nextValue)) {
|
|
864
|
+
renderChildrenSyncToSink(normalizeRenderableChildren(nextValue), sink, ctx);
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
renderChildSyncToSink(nextValue, sink, ctx);
|
|
1028
868
|
}
|
|
1029
869
|
function renderChildrenSyncToSink(children, sink, ctx) {
|
|
1030
870
|
if (!children || !Array.isArray(children) || children.length === 0) return;
|
|
@@ -1051,44 +891,6 @@ function sinkWrite3(sink, a, b, c) {
|
|
|
1051
891
|
sink.write(b);
|
|
1052
892
|
sink.write(c);
|
|
1053
893
|
}
|
|
1054
|
-
function renderNodeSync(node, ctx) {
|
|
1055
|
-
const { type, props } = node;
|
|
1056
|
-
if (typeof type === "function") return renderRenderableSync(inheritRenderableKey(node, executeComponentSync(type, props, ctx, getVNodeContextFrame(node) ?? null)), ctx);
|
|
1057
|
-
if (typeof type === "symbol") {
|
|
1058
|
-
if (type === SSR_PORTAL_HOST) return String(props?.token ?? "");
|
|
1059
|
-
if (type === SSR_PORTAL_ANCHOR) return String(props?.token ?? "");
|
|
1060
|
-
if (isFragmentType(type)) return renderChildrenSync(getRenderableChildren(node), ctx);
|
|
1061
|
-
if (type === __CONTROL_BOUNDARY__) return renderControlChildrenSync(node, ctx);
|
|
1062
|
-
if (type === DEFERRED_BOUNDARY) return renderDeferredBoundarySync(node, ctx);
|
|
1063
|
-
if (type === __ERROR_BOUNDARY__) {
|
|
1064
|
-
const boundaryState = getErrorBoundaryState(node);
|
|
1065
|
-
const fallback = props?.fallback;
|
|
1066
|
-
const reset = createErrorBoundaryReset(node);
|
|
1067
|
-
if (boundaryState?.error != null) return renderErrorBoundaryFallbackValue(fallback, boundaryState.error, reset, ctx);
|
|
1068
|
-
try {
|
|
1069
|
-
return renderChildrenSync(normalizeRenderableChildren(props?.children), ctx);
|
|
1070
|
-
} catch (error) {
|
|
1071
|
-
if (boundaryState) {
|
|
1072
|
-
boundaryState.error = error;
|
|
1073
|
-
boundaryState.notified = true;
|
|
1074
|
-
}
|
|
1075
|
-
logger.error("[Askr] ErrorBoundary caught render error:", error);
|
|
1076
|
-
return renderErrorBoundaryFallbackValue(fallback, error, reset, ctx);
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
throw new Error(`renderNodeSync: unsupported VNode symbol type: ${String(type)}`);
|
|
1080
|
-
}
|
|
1081
|
-
const typeStr = type;
|
|
1082
|
-
assertElementName(typeStr);
|
|
1083
|
-
if (VOID_ELEMENTS.has(typeStr)) return `<${typeStr}${renderAttrs(props)} />`;
|
|
1084
|
-
const maybeDangerous = props?.dangerouslySetInnerHTML;
|
|
1085
|
-
if (maybeDangerous !== void 0 && maybeDangerous !== null) {
|
|
1086
|
-
const { attrs, dangerousHtml } = renderAttrs(props, { returnDangerousHtml: true });
|
|
1087
|
-
if (dangerousHtml !== void 0) return `<${typeStr}${attrs}>${dangerousHtml}</${typeStr}>`;
|
|
1088
|
-
return `<${typeStr}${attrs}>${renderChildrenSync(getRenderableChildren(node), ctx)}</${typeStr}>`;
|
|
1089
|
-
}
|
|
1090
|
-
return `<${typeStr}${renderAttrs(props)}>${renderChildrenSync(getRenderableChildren(node), ctx)}</${typeStr}>`;
|
|
1091
|
-
}
|
|
1092
894
|
function renderNodeSyncToSink(node, sink, ctx) {
|
|
1093
895
|
const { type, props } = node;
|
|
1094
896
|
if (typeof type === "function") {
|
|
@@ -1112,12 +914,11 @@ function renderNodeSyncToSink(node, sink, ctx) {
|
|
|
1112
914
|
return;
|
|
1113
915
|
}
|
|
1114
916
|
if (type === __CONTROL_BOUNDARY__) {
|
|
1115
|
-
|
|
1116
|
-
sink.write(rendered);
|
|
917
|
+
renderControlChildrenToSink(node, sink, ctx);
|
|
1117
918
|
return;
|
|
1118
919
|
}
|
|
1119
920
|
if (type === DEFERRED_BOUNDARY) {
|
|
1120
|
-
|
|
921
|
+
renderDeferredBoundaryToSink(node, sink, ctx);
|
|
1121
922
|
return;
|
|
1122
923
|
}
|
|
1123
924
|
if (type === __ERROR_BOUNDARY__) {
|
|
@@ -1128,8 +929,9 @@ function renderNodeSyncToSink(node, sink, ctx) {
|
|
|
1128
929
|
renderErrorBoundaryFallbackValueToSink(fallback, boundaryState.error, reset, sink, ctx);
|
|
1129
930
|
return;
|
|
1130
931
|
}
|
|
932
|
+
const protectedOutput = new BufferedSink();
|
|
1131
933
|
try {
|
|
1132
|
-
renderChildrenSyncToSink(normalizeRenderableChildren(props?.children),
|
|
934
|
+
renderChildrenSyncToSink(normalizeRenderableChildren(props?.children), protectedOutput, ctx);
|
|
1133
935
|
} catch (error) {
|
|
1134
936
|
if (boundaryState) {
|
|
1135
937
|
boundaryState.error = error;
|
|
@@ -1137,7 +939,9 @@ function renderNodeSyncToSink(node, sink, ctx) {
|
|
|
1137
939
|
}
|
|
1138
940
|
logger.error("[Askr] ErrorBoundary caught render error:", error);
|
|
1139
941
|
renderErrorBoundaryFallbackValueToSink(fallback, error, reset, sink, ctx);
|
|
942
|
+
return;
|
|
1140
943
|
}
|
|
944
|
+
protectedOutput.publishTo(sink);
|
|
1141
945
|
return;
|
|
1142
946
|
}
|
|
1143
947
|
throw new Error(`renderNodeSyncToSink: unsupported VNode symbol type: ${String(type)}`);
|
|
@@ -1266,6 +1070,9 @@ function sameRouteParams(left, right) {
|
|
|
1266
1070
|
}
|
|
1267
1071
|
/** Synchronously render an already-resolved route handler to an HTML string. */
|
|
1268
1072
|
function renderResolvedToStringSync(opts) {
|
|
1073
|
+
return renderResolvedRouteToStringSync(opts);
|
|
1074
|
+
}
|
|
1075
|
+
function renderResolvedRouteToStringSync(opts, authContext) {
|
|
1269
1076
|
const { url, registry, handler, params, options } = opts;
|
|
1270
1077
|
const routes = registry.routes;
|
|
1271
1078
|
const requestUrl = new URL(url, "http://localhost");
|
|
@@ -1281,236 +1088,36 @@ function renderResolvedToStringSync(opts) {
|
|
|
1281
1088
|
handler
|
|
1282
1089
|
} : route);
|
|
1283
1090
|
const matchedRoute = routes[matchedIndex];
|
|
1284
|
-
|
|
1091
|
+
const cspNonce = validateCspNonce(options?.cspNonce);
|
|
1092
|
+
const ctx = createRenderContext(options?.seed, {
|
|
1285
1093
|
url,
|
|
1286
|
-
|
|
1287
|
-
manifest: {
|
|
1288
|
-
records: registry.manifest.records.map((record) => {
|
|
1289
|
-
const effectiveHandler = record.path === matchedRoute.path && record.handler === matchedRoute.handler && record.options.namespace === matchedRoute.namespace ? handler : record.handler;
|
|
1290
|
-
return {
|
|
1291
|
-
...record,
|
|
1292
|
-
component: effectiveHandler,
|
|
1293
|
-
handler: effectiveHandler,
|
|
1294
|
-
layoutChain: [],
|
|
1295
|
-
pageChain: [],
|
|
1296
|
-
options: record.options.namespace ? { namespace: record.options.namespace } : {},
|
|
1297
|
-
metaChain: void 0
|
|
1298
|
-
};
|
|
1299
|
-
}),
|
|
1300
|
-
...registry.manifest.basePath ? { basePath: registry.manifest.basePath } : {}
|
|
1301
|
-
},
|
|
1302
|
-
routes: effectiveRoutes
|
|
1303
|
-
},
|
|
1304
|
-
seed: options?.seed,
|
|
1094
|
+
basePath: registry.manifest.basePath,
|
|
1305
1095
|
data: options?.data,
|
|
1096
|
+
params,
|
|
1097
|
+
routes: effectiveRoutes,
|
|
1098
|
+
routeAuth: registry.manifest.auth,
|
|
1099
|
+
authContext,
|
|
1306
1100
|
dataRuntime: options?.dataRuntime,
|
|
1307
1101
|
envelope: options?.envelope,
|
|
1308
|
-
cspNonce
|
|
1309
|
-
});
|
|
1310
|
-
}
|
|
1311
|
-
//#endregion
|
|
1312
|
-
//#region src/ssr/route-request-render.ts
|
|
1313
|
-
function abortError() {
|
|
1314
|
-
const error = /* @__PURE__ */ new Error("Deferred route rendering was aborted.");
|
|
1315
|
-
error.name = "AbortError";
|
|
1316
|
-
return error;
|
|
1317
|
-
}
|
|
1318
|
-
function settleWithSignal(promise, signal) {
|
|
1319
|
-
if (signal.aborted) return Promise.reject(abortError());
|
|
1320
|
-
return new Promise((resolve, reject) => {
|
|
1321
|
-
const abort = () => reject(abortError());
|
|
1322
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
1323
|
-
promise.then((value) => {
|
|
1324
|
-
signal.removeEventListener("abort", abort);
|
|
1325
|
-
resolve(value);
|
|
1326
|
-
}, (error) => {
|
|
1327
|
-
signal.removeEventListener("abort", abort);
|
|
1328
|
-
reject(error);
|
|
1329
|
-
});
|
|
1102
|
+
cspNonce
|
|
1330
1103
|
});
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
const styles = [];
|
|
1337
|
-
return {
|
|
1338
|
-
html: stripHydrationPayload(renderToStringSync(() => state === "fulfilled" ? boundary.fulfilled(payload) : boundary.rejected(payload), {}, {
|
|
1339
|
-
seed,
|
|
1340
|
-
envelope: data ?? void 0,
|
|
1341
|
-
cspNonce,
|
|
1342
|
-
authContext,
|
|
1343
|
-
onContext: (context) => styles.push(...context.ssrStyles.values())
|
|
1344
|
-
})),
|
|
1345
|
-
styles
|
|
1346
|
-
};
|
|
1347
|
-
}
|
|
1348
|
-
function escapeHtmlAttribute(value) {
|
|
1349
|
-
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
1350
|
-
}
|
|
1351
|
-
function escapeStyleRawText(value) {
|
|
1352
|
-
return value.replace(/<\/style/gi, "<\\/style");
|
|
1353
|
-
}
|
|
1354
|
-
function patchChunk(id, html, styles, cspNonce) {
|
|
1355
|
-
const selector = `askr-resolve[data-askr-deferred=${JSON.stringify(id)}]`;
|
|
1356
|
-
return `${styles.length === 0 ? "" : `<style data-askr-style-registry="true" data-askr-deferred-style="true"${cspNonce ? ` nonce="${escapeHtmlAttribute(cspNonce)}"` : ""}>${escapeStyleRawText(styles.map((style) => style.cssText).join("\n"))}\n</style>`}<template data-askr-deferred-patch="${escapeHtmlAttribute(id)}">${html}</template><script${cspNonce ? ` nonce="${escapeHtmlAttribute(cspNonce)}"` : ""} data-askr-deferred-apply="${escapeHtmlAttribute(id)}">(function(s){var t=s.previousElementSibling;var c=t&&t.previousElementSibling;var r=document.querySelector('style[data-askr-style-registry="true"]:not([data-askr-deferred-style])');if(c&&c.tagName==='STYLE'&&r&&r!==c){r.append(c.textContent||'');c.remove();}else if(c&&c.tagName==='STYLE'){c.removeAttribute('data-askr-deferred-style');}var b=document.querySelector(${JSON.stringify(selector)});if(b&&t){b.replaceWith(t.content.cloneNode(true));}if(t)t.remove();s.remove();})(document.currentScript)<\/script>`;
|
|
1357
|
-
}
|
|
1358
|
-
function createDeferredRenderStream(initialHtml, boundaries, signal, seed, data, runtime, cspNonce, authContext) {
|
|
1359
|
-
const encoder = new TextEncoder();
|
|
1360
|
-
const local = new AbortController();
|
|
1361
|
-
let cancelled = false;
|
|
1362
|
-
let closed = false;
|
|
1363
|
-
let initialPending = true;
|
|
1364
|
-
let boundaryIndex = 0;
|
|
1365
|
-
let controllerRef;
|
|
1366
|
-
const cleanup = () => signal.removeEventListener("abort", abort);
|
|
1367
|
-
const close = (controller) => {
|
|
1368
|
-
if (closed) return;
|
|
1369
|
-
closed = true;
|
|
1370
|
-
cleanup();
|
|
1371
|
-
controller.close();
|
|
1372
|
-
};
|
|
1373
|
-
const abort = () => {
|
|
1374
|
-
local.abort(signal.reason);
|
|
1375
|
-
if (controllerRef) close(controllerRef);
|
|
1376
|
-
};
|
|
1377
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
1378
|
-
return new ReadableStream({
|
|
1379
|
-
start(controller) {
|
|
1380
|
-
controllerRef = controller;
|
|
1381
|
-
},
|
|
1382
|
-
async pull(controller) {
|
|
1383
|
-
if (cancelled || closed) return;
|
|
1384
|
-
if (initialPending) {
|
|
1385
|
-
initialPending = false;
|
|
1386
|
-
controller.enqueue(encoder.encode(stripHydrationPayload(initialHtml)));
|
|
1387
|
-
return;
|
|
1388
|
-
}
|
|
1389
|
-
const boundary = boundaries[boundaryIndex];
|
|
1390
|
-
if (boundary) {
|
|
1391
|
-
boundaryIndex += 1;
|
|
1392
|
-
try {
|
|
1393
|
-
const value = await settleWithSignal(boundary.promise, local.signal);
|
|
1394
|
-
if (!cancelled && !closed) {
|
|
1395
|
-
const rendered = renderBoundary(boundary, "fulfilled", value, seed, data, cspNonce, authContext);
|
|
1396
|
-
controller.enqueue(encoder.encode(patchChunk(boundary.id, rendered.html, rendered.styles, cspNonce)));
|
|
1397
|
-
}
|
|
1398
|
-
} catch (error) {
|
|
1399
|
-
if (local.signal.aborted) {
|
|
1400
|
-
if (!cancelled && !closed) close(controller);
|
|
1401
|
-
return;
|
|
1402
|
-
}
|
|
1403
|
-
if (!cancelled && !closed) {
|
|
1404
|
-
const rendered = renderBoundary(boundary, "rejected", error, seed, data, cspNonce, authContext);
|
|
1405
|
-
controller.enqueue(encoder.encode(patchChunk(boundary.id, rendered.html, rendered.styles, cspNonce)));
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
return;
|
|
1409
|
-
}
|
|
1410
|
-
const hydration = serializeHydrationRenderData(data ?? void 0, runtime);
|
|
1411
|
-
if (hydration) controller.enqueue(encoder.encode(hydration));
|
|
1412
|
-
close(controller);
|
|
1104
|
+
const sink = new StringSink();
|
|
1105
|
+
renderSSRRouteAppToSink({
|
|
1106
|
+
route: {
|
|
1107
|
+
...matchedRoute,
|
|
1108
|
+
handler
|
|
1413
1109
|
},
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
}
|
|
1419
|
-
});
|
|
1420
|
-
}
|
|
1421
|
-
/** Resolve and render a route request for SSR, streaming the body when possible. */
|
|
1422
|
-
async function renderRouteRequest(options) {
|
|
1423
|
-
return renderRouteRequestInternal(options);
|
|
1424
|
-
}
|
|
1425
|
-
/** Resolve and render a route request for SSR, always fully buffering the HTML. */
|
|
1426
|
-
async function renderRouteRequestToString(options) {
|
|
1427
|
-
const result = await renderRouteRequestInternal(options);
|
|
1428
|
-
if (result.kind !== "render" || !result.stream) return result;
|
|
1429
|
-
return {
|
|
1430
|
-
...result,
|
|
1431
|
-
html: await new Response(result.stream).text(),
|
|
1432
|
-
stream: void 0
|
|
1433
|
-
};
|
|
1434
|
-
}
|
|
1435
|
-
async function renderRouteRequestInternal(options) {
|
|
1436
|
-
if (!options?.registry) throw new TypeError("renderRouteRequest requires options.registry.");
|
|
1437
|
-
const cspNonce = validateCspNonce(options.cspNonce);
|
|
1438
|
-
const manifest = options.registry.manifest;
|
|
1439
|
-
const signal = options.signal ?? options.request?.signal ?? new AbortController().signal;
|
|
1440
|
-
const request = options.request ?? new Request(new URL(options.url, "http://localhost"));
|
|
1441
|
-
const runtime = options.dataRuntime ?? createDataRuntime();
|
|
1442
|
-
const prefetch = options.queryPrefetch ?? createQueryPrefetchContext({
|
|
1443
|
-
runtime,
|
|
1444
|
-
registry: options.queryRegistry,
|
|
1445
|
-
request,
|
|
1446
|
-
signal,
|
|
1447
|
-
mode: "ssr",
|
|
1448
|
-
telemetry: options.telemetry
|
|
1449
|
-
});
|
|
1450
|
-
const context = createRenderContext(options.seed, {
|
|
1451
|
-
url: options.url,
|
|
1452
|
-
basePath: manifest.basePath,
|
|
1453
|
-
routes: options.registry.routes,
|
|
1454
|
-
routeAuth: options.auth ?? manifest.auth,
|
|
1455
|
-
data: options.data,
|
|
1456
|
-
framework: options.framework,
|
|
1457
|
-
signal,
|
|
1458
|
-
dataRuntime: runtime,
|
|
1459
|
-
queryPrefetch: prefetch,
|
|
1460
|
-
mode: "ssr",
|
|
1461
|
-
cspNonce
|
|
1110
|
+
params: params ?? {},
|
|
1111
|
+
data: options?.data,
|
|
1112
|
+
ctx,
|
|
1113
|
+
sink
|
|
1462
1114
|
});
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
registry: options.registry,
|
|
1466
|
-
mode: "ssr",
|
|
1467
|
-
auth: options.auth ?? manifest.auth,
|
|
1468
|
-
authContext: options.authContext,
|
|
1469
|
-
request,
|
|
1470
|
-
signal,
|
|
1471
|
-
telemetry: options.telemetry
|
|
1472
|
-
});
|
|
1473
|
-
if (!resolved) return { kind: "no-match" };
|
|
1474
|
-
if (resolved.kind !== "render") return resolved;
|
|
1475
|
-
context.params = resolved.params;
|
|
1476
|
-
const sink = new StringSink();
|
|
1477
|
-
renderSSRRouteAppToSink({
|
|
1478
|
-
route: {
|
|
1479
|
-
path: "",
|
|
1480
|
-
handler: resolved.handler
|
|
1481
|
-
},
|
|
1482
|
-
params: resolved.params,
|
|
1483
|
-
data: options.data,
|
|
1484
|
-
ctx: context,
|
|
1485
|
-
sink
|
|
1486
|
-
});
|
|
1487
|
-
sink.end();
|
|
1488
|
-
const html = sink.toString();
|
|
1489
|
-
const boundaries = [...context.deferredBoundaries];
|
|
1490
|
-
const result = {
|
|
1491
|
-
kind: "render",
|
|
1492
|
-
html,
|
|
1493
|
-
styles: Array.from(context.ssrStyles.values()),
|
|
1494
|
-
...boundaries.length > 0 ? { stream: createDeferredRenderStream(html, boundaries, signal, options.seed, context.hydrationData, runtime, cspNonce, context.authContext) } : {},
|
|
1495
|
-
params: resolved.params
|
|
1496
|
-
};
|
|
1497
|
-
if (resolved.record) Object.defineProperty(result, "record", {
|
|
1498
|
-
value: resolved.record,
|
|
1499
|
-
enumerable: false
|
|
1500
|
-
});
|
|
1501
|
-
return result;
|
|
1502
|
-
}));
|
|
1503
|
-
}
|
|
1504
|
-
//#endregion
|
|
1505
|
-
//#region src/ssr/index-internal.ts
|
|
1506
|
-
const routeRenderHost = { renderAppToSink: renderSSRRouteAppToSink };
|
|
1507
|
-
function renderToString(arg) {
|
|
1508
|
-
if (typeof arg === "function") return renderToStringSync(arg);
|
|
1509
|
-
return renderRouteToString(arg, routeRenderHost);
|
|
1115
|
+
sink.end();
|
|
1116
|
+
return sink.toString();
|
|
1510
1117
|
}
|
|
1511
|
-
/**
|
|
1512
|
-
function
|
|
1513
|
-
|
|
1118
|
+
/** @internal Render the resolved hydration route with its request-local auth context. */
|
|
1119
|
+
function renderResolvedForHydrationSync(opts, authContext) {
|
|
1120
|
+
return renderResolvedRouteToStringSync(opts, authContext);
|
|
1514
1121
|
}
|
|
1515
1122
|
//#endregion
|
|
1516
|
-
export {
|
|
1123
|
+
export { serializeHydrationRenderData as a, createRenderContext as c, withRenderContext as d, withRenderContextAsync as f, renderToStringSync as i, getRenderContext as l, renderResolvedToStringSync as n, StreamSink as o, renderSSRRouteAppToSink as r, StringSink as s, renderResolvedForHydrationSync as t, throwSSRDataMissing as u };
|