@askrjs/askr 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/access-CEA2pOry.js +1046 -0
  3. package/dist/actions/index.d.ts +16 -2
  4. package/dist/actions/index.js +5 -5
  5. package/dist/{activity-FirwPXA_.js → activity-oy2YFTFF.js} +10 -16
  6. package/dist/benchmark.js +8 -9
  7. package/dist/boot/index.d.ts +19 -9
  8. package/dist/boot/index.js +1 -1
  9. package/dist/{boot-tWyF6121.js → boot-DLplW9h4.js} +700 -441
  10. package/dist/capabilities-Bbf0mlp9.js +42 -0
  11. package/dist/components/index.d.ts +11 -8
  12. package/dist/components/index.js +1 -1
  13. package/dist/{compose-ref-Bfaf4kUC.js → compose-ref-v0qGlHoN.js} +7 -1
  14. package/dist/control/index.d.ts +20 -2
  15. package/dist/control/index.js +1 -1
  16. package/dist/{control-C-wSaq9V.js → control-BR_mrX__.js} +4 -4
  17. package/dist/core.d.ts +163 -0
  18. package/dist/csp-nonce-BfFWcd0w.js +173 -0
  19. package/dist/data/index.d.ts +74 -2
  20. package/dist/data/index.js +3 -3
  21. package/dist/{data-ghKopAbp.js → data-2edW0jhF.js} +108 -122
  22. package/dist/{data-runtime-7t_Y0iv3.js → data-runtime-CxoY98lX.js} +22 -15
  23. package/dist/{deferred-CxjWCkIn.js → deferred-CbgNhf67.js} +5 -5
  24. package/dist/dom-renderer.d.ts +77 -0
  25. package/dist/domains/actions.d.ts +70 -0
  26. package/dist/domains/component.d.ts +119 -0
  27. package/dist/domains/context.d.ts +73 -0
  28. package/dist/domains/control.d.ts +328 -0
  29. package/dist/domains/data.d.ts +461 -0
  30. package/dist/domains/diagnostics.d.ts +21 -0
  31. package/dist/domains/lifecycle.d.ts +112 -0
  32. package/dist/domains/refs.d.ts +18 -0
  33. package/dist/domains/renderer.d.ts +127 -0
  34. package/dist/domains/routing.d.ts +501 -0
  35. package/dist/domains/scheduler.d.ts +79 -0
  36. package/dist/domains/server.d.ts +112 -0
  37. package/dist/domains/state.d.ts +129 -0
  38. package/dist/domains/telemetry.d.ts +29 -0
  39. package/dist/eager-control.d.ts +8 -0
  40. package/dist/{effect-CNwstBBD.js → effect-C3Zsc1k6.js} +14 -41
  41. package/dist/{types-DWVrKn9N.d.ts → elements.d.ts} +79 -42
  42. package/dist/{for-internal-DyBHfKJA.js → for-state-BDykeOJL.js} +598 -494
  43. package/dist/foundations/icon/index.d.ts +67 -16
  44. package/dist/foundations/icon/index.js +1 -1
  45. package/dist/foundations/index.d.ts +26 -3
  46. package/dist/foundations/index.js +1 -1
  47. package/dist/foundations/interactions/index.d.ts +73 -34
  48. package/dist/foundations/interactions/index.js +1 -1
  49. package/dist/foundations/state/index.d.ts +12 -5
  50. package/dist/foundations/state/index.js +1 -1
  51. package/dist/foundations/structures/index.d.ts +44 -3
  52. package/dist/foundations/structures/index.js +1 -1
  53. package/dist/foundations/utilities/index.d.ts +34 -3
  54. package/dist/foundations/utilities/index.js +1 -1
  55. package/dist/fx/index.d.ts +72 -25
  56. package/dist/fx/index.js +8 -8
  57. package/dist/index.d.ts +121 -4
  58. package/dist/index.js +316 -47
  59. package/dist/{component-internal-C4hKKQBF.js → instance-VAFYVJOa.js} +495 -283
  60. package/dist/{interactions-D102JxBR.js → interactions-CTeACdT_.js} +1 -1
  61. package/dist/jsx-dev-runtime.d.ts +51 -11
  62. package/dist/jsx-globals.d.ts +2 -0
  63. package/dist/jsx-runtime-CPJXAOBF.js +43 -0
  64. package/dist/jsx-runtime.d.ts +17 -3
  65. package/dist/jsx-runtime.js +2 -42
  66. package/dist/jsx.d.ts +108 -0
  67. package/dist/{manifest-D6rfj_jC.js → manifest-D1P3UNWV.js} +4 -9
  68. package/dist/{navigate-ZZu1IV0B.js → navigate-prC5V8XV.js} +125 -472
  69. package/dist/{navigate-F4sHgWbw.d.ts → navigation.d.ts} +8 -8
  70. package/dist/{readable-Di1fzSwm.js → notify-DxxuDPeY.js} +96 -34
  71. package/dist/{query-registry-Bua3cDyb.js → query-registry-LrYpQsDJ.js} +1 -1
  72. package/dist/{compose-ref-DOJHoW5X.d.ts → refs.d.ts} +11 -7
  73. package/dist/{ssr-Doog-AfJ.js → render-resolved-BC0W7_5b.js} +165 -558
  74. package/dist/{renderer-Cl23O0hV.js → renderer-CT0HeY58.js} +6825 -5692
  75. package/dist/{resolution-ClD-K7Lz.js → resolution-3DskUkjG.js} +189 -180
  76. package/dist/{resource-operation-Vz1S5h2k.js → resource-18-sFRl9.js} +98 -58
  77. package/dist/resources/index.d.ts +64 -12
  78. package/dist/resources/index.js +214 -8
  79. package/dist/route-activity.d.ts +21 -0
  80. package/dist/{route-matching-Bo6UqG-5.js → route-matching-CUzZ31gT.js} +68 -82
  81. package/dist/router/index.d.ts +378 -79
  82. package/dist/router/index.js +9 -9
  83. package/dist/router-internal-7nuNEHfG.js +36 -0
  84. package/dist/scope-Cq_iYbTR.js +403 -0
  85. package/dist/{selector-CsKuomw0.js → selector-BSPVkjKP.js} +26 -27
  86. package/dist/{shared-CYBH3F8I.js → shared-kU8yvryp.js} +3 -6
  87. package/dist/{snapshot-source-BB42xgwb.js → snapshot-source-An4iKajG.js} +1 -1
  88. package/dist/ssg/index.d.ts +51 -15
  89. package/dist/ssg/index.js +269 -209
  90. package/dist/ssr/index.d.ts +121 -68
  91. package/dist/ssr/index.js +2 -1
  92. package/dist/ssr-CiNm6Vzl.js +393 -0
  93. package/dist/{ssr-DSHd_Q9K.js → ssr-EPjwJH5R.js} +5 -1
  94. package/dist/{state-BGJgYSyQ.js → state-BXJfudq1.js} +10 -15
  95. package/dist/{structures-DFyK5AdQ.js → structures-CjEzfj4h.js} +37 -57
  96. package/dist/{index-CTpMmzgN.d.ts → structures.d.ts} +57 -36
  97. package/dist/testing/index.d.ts +123 -24
  98. package/dist/testing/index.js +24 -61
  99. package/dist/timer-C7uqnJVt.js +85 -0
  100. package/dist/{index-B1gu0_m0.d.ts → utilities.d.ts} +24 -13
  101. package/dist/{verify-hydration-BZPJ_kYa.js → verify-hydration-ikaxotf7.js} +8 -5
  102. package/package.json +12 -14
  103. package/dist/access-BucjYZcX.js +0 -509
  104. package/dist/activity-DooIuxQO.d.ts +0 -14
  105. package/dist/benchmark.d.ts +0 -26
  106. package/dist/cleanup-TXSsApEl.js +0 -535
  107. package/dist/component-cleanup-jdbgjkT1.js +0 -143
  108. package/dist/component-scope-4I3yEMkb.js +0 -195
  109. package/dist/control-Cxag1yqV.d.ts +0 -8
  110. package/dist/csp-nonce-9NFUb7hJ.js +0 -72
  111. package/dist/css-BuPaTOfe.js +0 -70
  112. package/dist/fastlane-BKZduiBQ.js +0 -166
  113. package/dist/index-Dgjbo2gU.d.ts +0 -1456
  114. package/dist/jsx-runtime-Cg7GqY1S.d.ts +0 -2
  115. package/dist/jsx-runtime-gVIaTPw0.d.ts +0 -38
  116. package/dist/lifecycle-batch-63aXFf2S.js +0 -253
  117. 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 getVNodeContextFrame, g as getExecutionContextFrame, n as createComponentInstance, p as callWithContext, y as markVNodeTreeWithContextFrame } from "./component-internal-C4hKKQBF.js";
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 { n as getPublicAttributeName, t as sanitizeCssValue } from "./css-BuPaTOfe.js";
12
- import { o as isUnsafeUrlAttribute, r as validateCspNonce, t as CspNonceScope } from "./csp-nonce-9NFUb7hJ.js";
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-7t_Y0iv3.js";
15
- import { c as withTelemetry, i as dehydrateDataRuntime, t as createQueryPrefetchContext } from "./query-registry-Bua3cDyb.js";
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 { n as renderDocument, t as SSR_RENDER_DATA_ATTR } from "./ssr-DSHd_Q9K.js";
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.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
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
- configureRenderContextProvider({ getRenderContext });
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 prev = getCurrentComponentInstance();
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
- setCurrentComponentInstance(temp);
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
- setCurrentComponentInstance(prev);
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 === "children" || key === "imperativeChildren" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML") continue;
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
- function renderRenderableSync(value, ctx) {
942
- if (typeof value === "string") return escapeText(value);
943
- if (typeof value === "number") return escapeText(String(value));
944
- if (value === null || value === void 0 || value === false) return "";
945
- if (Array.isArray(value)) return renderChildrenSync(value, ctx);
946
- if (value && typeof value === "object" && "type" in value) return renderNodeSync(value, ctx);
947
- return "";
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 ? renderRenderableSync(slot.value, ctx) : "";
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
- function renderChildSync(child, ctx) {
991
- return renderRenderableSync(child, ctx);
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
- function renderChildrenSync(children, ctx) {
1013
- if (!children || !Array.isArray(children) || children.length === 0) return "";
1014
- if (children.length === 1) return renderChildSync(children[0], ctx);
1015
- if (children.length <= 8) {
1016
- let result = "";
1017
- for (const child of children) result += renderChildSync(child, ctx);
1018
- return result;
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
- return Array.from({ length: children.length }, (_, index) => renderChildSync(children[index], ctx)).join("");
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
- function renderErrorBoundaryFallbackValue(fallback, error, reset, ctx) {
1023
- const nextValue = resolveErrorBoundaryFallbackNode(fallback, error, reset);
1024
- return Array.isArray(nextValue) ? renderChildrenSync(normalizeRenderableChildren(nextValue), ctx) : renderChildSync(nextValue, ctx);
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
- sink.write(renderErrorBoundaryFallbackValue(fallback, error, reset, ctx));
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
- const rendered = renderControlChildrenSync(node, ctx);
1116
- sink.write(rendered);
917
+ renderControlChildrenToSink(node, sink, ctx);
1117
918
  return;
1118
919
  }
1119
920
  if (type === DEFERRED_BOUNDARY) {
1120
- sink.write(renderDeferredBoundarySync(node, ctx));
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), sink, ctx);
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
- return renderToString({
1091
+ const cspNonce = validateCspNonce(options?.cspNonce);
1092
+ const ctx = createRenderContext(options?.seed, {
1285
1093
  url,
1286
- registry: {
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: options?.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
- function stripHydrationPayload(html) {
1333
- return html.replace(/<script type="application\/json" data-askr-render-data="true">[\s\S]*?<\/script>$/, "");
1334
- }
1335
- function renderBoundary(boundary, state, payload, seed, data, cspNonce, authContext) {
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, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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
- cancel(reason) {
1415
- cancelled = true;
1416
- local.abort(reason);
1417
- cleanup();
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
- return withTelemetry(options.telemetry?.ssrRender, {}, () => withRenderContextAsync(context, async () => {
1464
- const resolved = await resolveRouteRequest(options.url, {
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
- /** Stream a route request's rendered HTML to the response sink described by `opts`. */
1512
- function renderToStream(opts) {
1513
- renderRouteToStream(opts, routeRenderHost);
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 { renderResolvedToStringSync as a, createRenderContext as c, withRenderContextAsync as d, renderRouteRequestToString as i, getRenderContext as l, renderToString as n, renderToStringSync as o, renderRouteRequest as r, resolveRequest as s, renderToStream as t, withRenderContext as u };
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 };