void 0.7.4 → 0.7.6

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.
@@ -1,4 +1,4 @@
1
- import { t as PageObject } from "../protocol-CK4OFwfR.mjs";
1
+ import { t as PageObject } from "../protocol-COz1NHxc.mjs";
2
2
  import { CacheForInput, CacheForResult } from "./prefetch.mjs";
3
3
 
4
4
  //#region src/pages/action-result.d.ts
@@ -147,6 +147,7 @@ interface VoidRouter {
147
147
  readonly url: URL;
148
148
  readonly path: string;
149
149
  readonly query: URLSearchParams;
150
+ readonly params: Record<string, string>;
150
151
  visit(url: string | URL, options?: VisitOptions): Promise<unknown>;
151
152
  refresh(options?: VisitOptions): Promise<unknown>;
152
153
  prefetch(url: string | URL, options?: PrefetchOptions): Promise<unknown>;
@@ -178,8 +179,8 @@ interface VoidRouterConfig {
178
179
  onNavigationChange?(navigation: NavigationState): void;
179
180
  }
180
181
  declare function idleNavigationState(): NavigationState;
181
- declare function createRouterFacade(router: VoidRouter, getCurrentUrl: () => string): VoidRouter;
182
- declare function createSsrRouter(url?: string): VoidRouter;
182
+ declare function createRouterFacade(router: VoidRouter, getCurrentUrl: () => string, getCurrentParams?: () => Record<string, string>): VoidRouter;
183
+ declare function createSsrRouter(url?: string, params?: Record<string, string>): VoidRouter;
183
184
  declare function isFile(value: unknown): boolean;
184
185
  declare function hasFiles(data: unknown): boolean;
185
186
  declare function objectToFormData(source: Record<string, unknown>, form?: FormData, parentKey?: string): FormData;
@@ -756,7 +756,7 @@ function idleNavigationState() {
756
756
  method: null
757
757
  };
758
758
  }
759
- function createRouterFacade(router, getCurrentUrl) {
759
+ function createRouterFacade(router, getCurrentUrl, getCurrentParams = () => router.params) {
760
760
  return {
761
761
  get _hoverDelay() {
762
762
  return router._hoverDelay;
@@ -770,6 +770,9 @@ function createRouterFacade(router, getCurrentUrl) {
770
770
  get query() {
771
771
  return this.url.searchParams;
772
772
  },
773
+ get params() {
774
+ return getCurrentParams();
775
+ },
773
776
  visit(url, options = {}) {
774
777
  return router.visit(url, options);
775
778
  },
@@ -787,7 +790,7 @@ function createRouterFacade(router, getCurrentUrl) {
787
790
  }
788
791
  };
789
792
  }
790
- function createSsrRouter(url = "/") {
793
+ function createSsrRouter(url = "/", params = {}) {
791
794
  return {
792
795
  _hoverDelay: 75,
793
796
  get url() {
@@ -799,6 +802,9 @@ function createSsrRouter(url = "/") {
799
802
  get query() {
800
803
  return this.url.searchParams;
801
804
  },
805
+ get params() {
806
+ return params;
807
+ },
802
808
  visit() {
803
809
  throw new Error("Void Router: visit() is not available during SSR.");
804
810
  },
@@ -870,9 +876,11 @@ async function createVoidRouter(config) {
870
876
  {
871
877
  const engine = await createPageNavigationEngine(config);
872
878
  let routeUrl = currentUrl();
879
+ let routeParams = engine.initialPageData.params ?? {};
873
880
  let activeNavigationId = null;
874
- const setRouteUrl = (url) => {
881
+ const setRouteState = (url, params = routeParams) => {
875
882
  routeUrl = url;
883
+ routeParams = params;
876
884
  config.onRouteChange?.(url);
877
885
  };
878
886
  const setNavigation = (navigation) => {
@@ -884,11 +892,12 @@ async function createVoidRouter(config) {
884
892
  try {
885
893
  const result = await resource.shell;
886
894
  if (!result) return;
895
+ setRouteState(result.pageData.url || resource.fetchUrl + resource.hash, result.pageData.params ?? {});
887
896
  const applyUpdate = () => config.adapter.applyUpdate(result.pageData, result.component, result.layouts);
888
897
  if ((resource.options.viewTransition ?? config.viewTransitions) && document.startViewTransition) await document.startViewTransition(() => applyUpdate()).updateCallbackDone;
889
898
  else await applyUpdate();
890
899
  await resource.commit();
891
- if (activeNavigationId === resource.id) setRouteUrl(currentUrl());
900
+ if (activeNavigationId === resource.id) setRouteState(currentUrl(), result.pageData.params ?? {});
892
901
  return result.pageData;
893
902
  } catch (error) {
894
903
  if (resource.method.toUpperCase() === "GET" && !resource.options._resolveOnShell && isAbortError(error)) return;
@@ -896,7 +905,7 @@ async function createVoidRouter(config) {
896
905
  } finally {
897
906
  if (activeNavigationId === resource.id) {
898
907
  activeNavigationId = null;
899
- setRouteUrl(currentUrl());
908
+ setRouteState(currentUrl());
900
909
  setNavigation(idleNavigationState());
901
910
  }
902
911
  }
@@ -912,6 +921,9 @@ async function createVoidRouter(config) {
912
921
  get query() {
913
922
  return this.url.searchParams;
914
923
  },
924
+ get params() {
925
+ return routeParams;
926
+ },
915
927
  visit(url, options = {}) {
916
928
  return applyNavigationResource(engine.prepareVisit(normalizeUrl(url), options));
917
929
  },
@@ -932,7 +944,7 @@ async function createVoidRouter(config) {
932
944
  engine.flushAll();
933
945
  }
934
946
  };
935
- const router = createRouterFacade(rawRouter, () => routeUrl);
947
+ const router = createRouterFacade(rawRouter, () => routeUrl, () => routeParams);
936
948
  engine.setPopStateHandler((url, options) => {
937
949
  router.visit(url, options);
938
950
  });
@@ -1,5 +1,5 @@
1
1
  import { a as renderHtmlAttrs, i as renderHeadToString, n as HeadDescriptor, o as resolveHead, r as renderBodyAttrs, t as HeadConfig } from "../head-CZGAunBV.mjs";
2
- import { i as PageScanResult, n as NamedLayoutDefinition, r as PageDefinition, t as LayoutDefinition } from "../types-AdKzPp2C.mjs";
2
+ import { a as PageScanResult, i as PageDefinition, n as LayoutDefinition, r as NamedLayoutDefinition } from "../types-D6AuY5Bf.mjs";
3
3
  import { serializePageData } from "./serialize.mjs";
4
4
 
5
5
  //#region src/pages/layout-resolution.d.ts
@@ -1,4 +1,4 @@
1
- import { i as PageScanResult } from "../types-AdKzPp2C.mjs";
1
+ import { a as PageScanResult } from "../types-D6AuY5Bf.mjs";
2
2
  import { ResolvedConfig, ViteDevServer } from "vite";
3
3
 
4
4
  //#region src/pages/islands-plugin.d.ts
@@ -17,8 +17,6 @@ declare function resolveSpecifier(specifier: string, importerDir: string, extens
17
17
  * or any .md file has a `<script>` block.
18
18
  */
19
19
  declare function needsIslandClientBundle(dir: string, islandFilePattern: RegExp, checkStyleBlocks: boolean): boolean;
20
- /** Check if a file has CSS import statements (JS-style imports). */
21
- declare function fileHasCssImports(filePath: string): boolean;
22
20
  /**
23
21
  * Rewrite a matched CSS import line so relative specifiers resolve from the
24
22
  * virtual module. Absolute and bare specifiers are kept as-is.
@@ -26,16 +24,9 @@ declare function fileHasCssImports(filePath: string): boolean;
26
24
  declare function rewriteCssImportForVirtualModule(matchedLine: string, specifier: string, sourceFilePath: string): string;
27
25
  /** Extract CSS import statements from a file and return them as @import rules. */
28
26
  declare function extractCssImports(filePath: string): string;
29
- /** Rebuild islandCssMap from current file contents. */
30
- declare function rebuildIslandCssMap(pageScan: PageScanResult, pagesDir: string, islandCssMap: Map<string, Array<string>>, fileHasCssFn: (filePath: string) => boolean): void;
31
27
  /** Compute which island pages need client-side JavaScript. */
32
28
  declare function computePagesNeedingClientJS(pageScan: PageScanResult, pagesDir: string, resolvedConfig: ResolvedConfig | null): Set<string>;
33
29
  /**
34
- * Configure the Vite dev server for island CSS serving.
35
- * Sets up file watching and a middleware for serving virtual CSS modules.
36
- */
37
- declare function configureIslandCssServer(server: ViteDevServer, islandCssFiles: Set<string>, viteRoot: string, rebuildCssMap: () => void): void;
38
- /**
39
30
  * Watch the pages directory for structural changes (add/remove) and .md content
40
31
  * changes (frontmatter layout field) so that new layouts and named layouts are
41
32
  * picked up without a dev server restart.
@@ -47,4 +38,4 @@ declare function watchPagesForRescan(server: ViteDevServer, pagesDir: string, ro
47
38
  flush: () => Promise<void>;
48
39
  };
49
40
  //#endregion
50
- export { ISLANDS_CLIENT_ENTRY_ID, ISLANDS_RENDERER_ID, IslandEntry, computePagesNeedingClientJS, configureIslandCssServer, extractCssImports, fileHasCssImports, needsIslandClientBundle, rebuildIslandCssMap, resolveSpecifier, rewriteCssImportForVirtualModule, watchPagesForRescan };
41
+ export { ISLANDS_CLIENT_ENTRY_ID, ISLANDS_RENDERER_ID, IslandEntry, computePagesNeedingClientJS, extractCssImports, needsIslandClientBundle, resolveSpecifier, rewriteCssImportForVirtualModule, watchPagesForRescan };
@@ -43,15 +43,6 @@ function needsIslandClientBundle(dir, islandFilePattern, checkStyleBlocks) {
43
43
  } catch {}
44
44
  return false;
45
45
  }
46
- /** Check if a file has CSS import statements (JS-style imports). */
47
- function fileHasCssImports(filePath) {
48
- try {
49
- const code = readFileSync(filePath, "utf-8");
50
- return /import\b[^"']*["'][^"']+\.(?:css|scss|sass|less|styl|stylus|pcss|postcss)["']/.test(code);
51
- } catch {
52
- return false;
53
- }
54
- }
55
46
  /**
56
47
  * Rewrite a matched CSS import line so relative specifiers resolve from the
57
48
  * virtual module. Absolute and bare specifiers are kept as-is.
@@ -72,22 +63,6 @@ function extractCssImports(filePath) {
72
63
  while ((match = re.exec(code)) !== null) imports.push(`@import ${JSON.stringify(match[1])};`);
73
64
  return imports.join("\n");
74
65
  }
75
- /** Rebuild islandCssMap from current file contents. */
76
- function rebuildIslandCssMap(pageScan, pagesDir, islandCssMap, fileHasCssFn) {
77
- islandCssMap.clear();
78
- for (const page of pageScan.pages) {
79
- if (!page.island) continue;
80
- const cssPaths = [];
81
- const layoutChain = resolveEffectiveLayoutChain(page, pageScan.layouts, pageScan.namedLayouts);
82
- for (const layout of layoutChain) {
83
- const layoutPath = resolve(pagesDir, layout.filePath);
84
- if (fileHasCssFn(layoutPath)) cssPaths.push(layoutPath);
85
- }
86
- const pagePath = resolve(pagesDir, page.componentPath);
87
- if (fileHasCssFn(pagePath)) cssPaths.push(pagePath);
88
- if (cssPaths.length > 0) islandCssMap.set(page.componentId, cssPaths);
89
- }
90
- }
91
66
  /** Compute which island pages need client-side JavaScript. */
92
67
  function computePagesNeedingClientJS(pageScan, pagesDir, resolvedConfig) {
93
68
  const mdPlugin = resolvedConfig?.plugins?.find((plugin) => plugin.name === "void-md");
@@ -122,43 +97,6 @@ function computePagesNeedingClientJS(pageScan, pagesDir, resolvedConfig) {
122
97
  return result;
123
98
  }
124
99
  /**
125
- * Configure the Vite dev server for island CSS serving.
126
- * Sets up file watching and a middleware for serving virtual CSS modules.
127
- */
128
- function configureIslandCssServer(server, islandCssFiles, viteRoot, rebuildCssMap) {
129
- server.watcher.on("change", (path) => {
130
- const normalizedPath = normalizePath(path);
131
- if (!islandCssFiles.has(normalizedPath)) return;
132
- rebuildCssMap();
133
- for (const env of Object.values(server.environments)) {
134
- const mod = env.moduleGraph.getModuleById("\0" + ISLANDS_RENDERER_ID);
135
- if (mod) env.moduleGraph.invalidateModule(mod);
136
- }
137
- const cssMod = server.environments.client?.moduleGraph.getModuleById(normalizedPath + ".css");
138
- if (cssMod) server.environments.client.moduleGraph.invalidateModule(cssMod);
139
- });
140
- server.middlewares.use(async (req, res, next) => {
141
- if (!req.url) return next();
142
- const url = req.url.split("?")[0];
143
- if (!url.endsWith(".css")) return next();
144
- const srcPath = normalizePath(resolve(viteRoot, url.slice(1, -4)));
145
- if (!islandCssFiles.has(srcPath)) return next();
146
- try {
147
- const result = await server.environments.client.transformRequest(url);
148
- if (result) {
149
- const m = result.code.match(/\b__vite__css\s*=\s*("(?:[^"\\]|\\.)*")/) || result.code.match(/export\s+default\s+("(?:[^"\\]|\\.)*")/);
150
- if (m) {
151
- res.setHeader("Content-Type", "text/css");
152
- res.statusCode = 200;
153
- res.end(JSON.parse(m[1]));
154
- return;
155
- }
156
- }
157
- } catch {}
158
- next();
159
- });
160
- }
161
- /**
162
100
  * Watch the pages directory for structural changes (add/remove) and .md content
163
101
  * changes (frontmatter layout field) so that new layouts and named layouts are
164
102
  * picked up without a dev server restart.
@@ -192,4 +130,4 @@ function watchPagesForRescan(server, pagesDir, root, onRescan) {
192
130
  return { flush: () => pending ?? Promise.resolve() };
193
131
  }
194
132
  //#endregion
195
- export { ISLANDS_CLIENT_ENTRY_ID, ISLANDS_RENDERER_ID, computePagesNeedingClientJS, configureIslandCssServer, extractCssImports, fileHasCssImports, needsIslandClientBundle, rebuildIslandCssMap, resolveSpecifier, rewriteCssImportForVirtualModule, watchPagesForRescan };
133
+ export { ISLANDS_CLIENT_ENTRY_ID, ISLANDS_RENDERER_ID, computePagesNeedingClientJS, extractCssImports, needsIslandClientBundle, resolveSpecifier, rewriteCssImportForVirtualModule, watchPagesForRescan };
@@ -1,3 +1,3 @@
1
1
  import { S as ValidationError } from "../handler-dKQWyF-G.mjs";
2
- import { a as handlePageMutation, i as handlePageGet, n as PageRenderResult, o as safeJsonStringify, r as flattenValidationErrors, t as PageObject } from "../protocol-CK4OFwfR.mjs";
2
+ import { a as handlePageMutation, i as handlePageGet, n as PageRenderResult, o as safeJsonStringify, r as flattenValidationErrors, t as PageObject } from "../protocol-COz1NHxc.mjs";
3
3
  export { PageObject, PageRenderResult, ValidationError, flattenValidationErrors, handlePageGet, handlePageMutation, safeJsonStringify };
@@ -3,6 +3,7 @@ import { i as ValidationError, r as separateDeferred, t as DEFERRED } from "../d
3
3
  import { resolveHead } from "./head.mjs";
4
4
  //#region src/pages/protocol.ts
5
5
  const POWERED_BY = { "X-Powered-By": "Void" };
6
+ const RENDER_ERROR_ASSET_TAGS_KEY = "__voidPageAssetTags";
6
7
  /** Escape characters that are unsafe inside inline <script> tags to prevent XSS. */
7
8
  function safeJsonStringify(value) {
8
9
  return (JSON.stringify(value) ?? "null").replaceAll("<", "\\u003c").replaceAll(">", "\\u003e").replaceAll("&", "\\u0026").replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
@@ -15,6 +16,85 @@ function pageResponseHeaders(c, headers) {
15
16
  for (const [key, value] of Object.entries(headers)) out.set(key, value);
16
17
  return out;
17
18
  }
19
+ function escapeHtmlAttr(value) {
20
+ return value.replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("<", "&lt;");
21
+ }
22
+ function serializeStyleCss(css) {
23
+ return css.replace(/<\/style/gi, "<\\/style");
24
+ }
25
+ function serializeDevCssAssets(assets, nonce) {
26
+ return assets.map((asset) => `<style data-vite-dev-id="${escapeHtmlAttr(asset.id ?? asset.url)}" nonce="${escapeHtmlAttr(nonce)}">${serializeStyleCss(asset.css)}</style>`).join("\n");
27
+ }
28
+ function hasCompatibleStyleSource(value, nonceSource) {
29
+ return value === "'unsafe-inline'" || value === nonceSource;
30
+ }
31
+ function adjustCspValue(value, nonce) {
32
+ const nonceSource = `'nonce-${nonce}'`;
33
+ const parsed = value.split(";").map((part) => part.trim()).filter(Boolean).map((directive) => {
34
+ const [name = "", ...sources] = directive.split(/\s+/);
35
+ return {
36
+ name: name.toLowerCase(),
37
+ originalName: name,
38
+ sources
39
+ };
40
+ });
41
+ const styleElem = parsed.find((directive) => directive.name === "style-src-elem");
42
+ if (styleElem) {
43
+ if (!styleElem.sources.some((source) => hasCompatibleStyleSource(source, nonceSource))) styleElem.sources.push(nonceSource);
44
+ return parsed.map((directive) => [directive.originalName, ...directive.sources].join(" ")).join("; ");
45
+ }
46
+ const style = parsed.find((directive) => directive.name === "style-src");
47
+ if (style) {
48
+ if (!style.sources.some((source) => hasCompatibleStyleSource(source, nonceSource))) style.sources.push(nonceSource);
49
+ return parsed.map((directive) => [directive.originalName, ...directive.sources].join(" ")).join("; ");
50
+ }
51
+ const defaultSrc = parsed.find((directive) => directive.name === "default-src");
52
+ if (defaultSrc) {
53
+ parsed.push({
54
+ name: "style-src",
55
+ originalName: "style-src",
56
+ sources: [...defaultSrc.sources, nonceSource]
57
+ });
58
+ return parsed.map((directive) => [directive.originalName, ...directive.sources].join(" ")).join("; ");
59
+ }
60
+ parsed.push({
61
+ name: "style-src",
62
+ originalName: "style-src",
63
+ sources: [nonceSource]
64
+ });
65
+ return parsed.map((directive) => [directive.originalName, ...directive.sources].join(" ")).join("; ");
66
+ }
67
+ function adjustDevCssCsp(headers, nonce) {
68
+ if (!nonce) return headers;
69
+ const csp = headers.get("content-security-policy");
70
+ if (csp) headers.set("content-security-policy", adjustCspValue(csp, nonce));
71
+ const cspReportOnly = headers.get("content-security-policy-report-only");
72
+ if (cspReportOnly) headers.set("content-security-policy-report-only", adjustCspValue(cspReportOnly, nonce));
73
+ return headers;
74
+ }
75
+ function prepareAssetTags(assetTags) {
76
+ if (!assetTags.devCss || assetTags.devCss.length === 0) return assetTags;
77
+ const nonce = crypto.randomUUID().replaceAll("-", "");
78
+ return {
79
+ ...assetTags,
80
+ css: [assetTags.css, serializeDevCssAssets(assetTags.devCss, nonce)].filter(Boolean).join("\n"),
81
+ devCssNonce: nonce
82
+ };
83
+ }
84
+ function attachRenderErrorAssetTags(error, assetTags) {
85
+ if ((typeof error === "object" || typeof error === "function") && error !== null) Object.defineProperty(error, RENDER_ERROR_ASSET_TAGS_KEY, {
86
+ configurable: true,
87
+ value: assetTags
88
+ });
89
+ throw error;
90
+ }
91
+ async function renderWithAssetTags(renderFn, pageObj, assetTags) {
92
+ try {
93
+ return await renderFn(pageObj, assetTags);
94
+ } catch (error) {
95
+ attachRenderErrorAssetTags(error, assetTags);
96
+ }
97
+ }
18
98
  function pageJsonResponse(c, pageObj) {
19
99
  return Response.json(pageObj, { headers: pageResponseHeaders(c, {
20
100
  "X-VoidPages": "true",
@@ -25,6 +105,9 @@ function pageJsonResponse(c, pageObj) {
25
105
  function requestPathWithSearch(c) {
26
106
  return c.req.path + new URL(c.req.url).search;
27
107
  }
108
+ function requestParams(c) {
109
+ return c.req.param();
110
+ }
28
111
  /** Extract props from a handler result, handling Response wrapping from withValidator */
29
112
  async function extractProps(result) {
30
113
  if (result instanceof Response) {
@@ -62,14 +145,14 @@ async function writeRenderResult(writer, encoder, result) {
62
145
  await writer.write(value);
63
146
  }
64
147
  }
65
- function renderedResponse(c, result, contentType) {
66
- return new Response(result, { headers: pageResponseHeaders(c, {
148
+ function renderedResponse(c, result, contentType, assetTags) {
149
+ return new Response(result, { headers: adjustDevCssCsp(pageResponseHeaders(c, {
67
150
  "content-type": contentType,
68
151
  ...POWERED_BY
69
- }) });
152
+ }), assetTags?.devCssNonce) });
70
153
  }
71
154
  /** Build a streaming response that sends an initial chunk then streams deferred results. */
72
- function streamDeferredResponse(initialChunk, deferredMap, c, contentType, formatResolved, formatRejected) {
155
+ function streamDeferredResponse(initialChunk, deferredMap, c, contentType, formatResolved, formatRejected, assetTags) {
73
156
  const encoder = new TextEncoder();
74
157
  const { readable, writable } = new TransformStream();
75
158
  const writer = writable.getWriter();
@@ -81,10 +164,10 @@ function streamDeferredResponse(initialChunk, deferredMap, c, contentType, forma
81
164
  })().catch((error) => {
82
165
  writer.abort(error).catch(() => {});
83
166
  });
84
- return new Response(readable, { headers: pageResponseHeaders(c, {
167
+ return new Response(readable, { headers: adjustDevCssCsp(pageResponseHeaders(c, {
85
168
  "content-type": contentType,
86
169
  ...POWERED_BY
87
- }) });
170
+ }), assetTags?.devCssNonce) });
88
171
  }
89
172
  /** Handle GET page request — run handler, return page object or SSR HTML */
90
173
  async function handlePageGet(handler, headFn, c, componentId, renderFn, getAssetTags, configHead, island) {
@@ -107,6 +190,7 @@ async function handlePageGet(handler, headFn, c, componentId, renderFn, getAsset
107
190
  const pageObj = {
108
191
  component: componentId,
109
192
  props: immediateProps,
193
+ params: requestParams(c),
110
194
  shared,
111
195
  url: requestPathWithSearch(c),
112
196
  errors: null,
@@ -146,9 +230,10 @@ async function handlePageGet(handler, headFn, c, componentId, renderFn, getAsset
146
230
  error
147
231
  }) + "\n");
148
232
  }
149
- const html = await renderFn(pageObj, await getAssetTags(c.req.raw, c.env, componentId));
150
- if (!hasDeferred) return renderedResponse(c, html, "text/html; charset=utf-8");
151
- return streamDeferredResponse(html, deferredMap, c, "text/html; charset=utf-8", (group, data) => `\n<script>window.__resolveDeferred?window.__resolveDeferred(${safeJsonStringify(group)},${safeJsonStringify(data)}):(window.__deferredQueue??=[]).push([${safeJsonStringify(group)},${safeJsonStringify(data)}])<\/script>`, (group, error) => `\n<script>window.__rejectDeferred?window.__rejectDeferred(${safeJsonStringify(group)},${safeJsonStringify(error)}):(window.__deferredQueue??=[]).push([${safeJsonStringify(group)},0,${safeJsonStringify(error)}])<\/script>`);
233
+ const assetTags = prepareAssetTags(await getAssetTags(c.req.raw, c.env, componentId));
234
+ const html = await renderWithAssetTags(renderFn, pageObj, assetTags);
235
+ if (!hasDeferred) return renderedResponse(c, html, "text/html; charset=utf-8", assetTags);
236
+ return streamDeferredResponse(html, deferredMap, c, "text/html; charset=utf-8", (group, data) => `\n<script>window.__resolveDeferred?window.__resolveDeferred(${safeJsonStringify(group)},${safeJsonStringify(data)}):(window.__deferredQueue??=[]).push([${safeJsonStringify(group)},${safeJsonStringify(data)}])<\/script>`, (group, error) => `\n<script>window.__rejectDeferred?window.__rejectDeferred(${safeJsonStringify(group)},${safeJsonStringify(error)}):(window.__deferredQueue??=[]).push([${safeJsonStringify(group)},0,${safeJsonStringify(error)}])<\/script>`, assetTags);
152
237
  }
153
238
  /** Handle POST/PUT/DELETE page request — run mutation, then re-run GET or redirect */
154
239
  async function handlePageMutation(mutationHandler, getHandler, headFn, c, componentId, renderFn, getAssetTags, configHead, island) {
@@ -159,6 +244,7 @@ async function handlePageMutation(mutationHandler, getHandler, headFn, c, compon
159
244
  return {
160
245
  component: componentId,
161
246
  props,
247
+ params: requestParams(c),
162
248
  shared,
163
249
  url: requestPathWithSearch(c),
164
250
  errors,
@@ -173,7 +259,8 @@ async function handlePageMutation(mutationHandler, getHandler, headFn, c, compon
173
259
  if (island) return Response.json({ errors: e.errors }, { status: 422 });
174
260
  const pageObj = buildMutationPageObj(stripDeferred(getHandler ? await extractProps(await getHandler(c)) : {}), e.errors);
175
261
  if (isPagesRequest(c)) return pageJsonResponse(c, pageObj);
176
- return renderedResponse(c, await renderFn(pageObj, await getAssetTags(c.req.raw, c.env, componentId)), "text/html; charset=utf-8");
262
+ const assetTags = prepareAssetTags(await getAssetTags(c.req.raw, c.env, componentId));
263
+ return renderedResponse(c, await renderWithAssetTags(renderFn, pageObj, assetTags), "text/html; charset=utf-8", assetTags);
177
264
  }
178
265
  throw e;
179
266
  }
@@ -185,14 +272,16 @@ async function handlePageMutation(mutationHandler, getHandler, headFn, c, compon
185
272
  if (island) return Response.json({ errors }, { status: 422 });
186
273
  const pageObj = buildMutationPageObj(stripDeferred(getHandler ? await extractProps(await getHandler(c)) : {}), errors);
187
274
  if (isPagesRequest(c)) return pageJsonResponse(c, pageObj);
188
- return renderedResponse(c, await renderFn(pageObj, await getAssetTags(c.req.raw, c.env, componentId)), "text/html; charset=utf-8");
275
+ const assetTags = prepareAssetTags(await getAssetTags(c.req.raw, c.env, componentId));
276
+ return renderedResponse(c, await renderWithAssetTags(renderFn, pageObj, assetTags), "text/html; charset=utf-8", assetTags);
189
277
  }
190
278
  if (status !== 204) return result;
191
279
  }
192
280
  if (island) return Response.json({ success: true });
193
281
  const pageObj = buildMutationPageObj(stripDeferred(getHandler ? await extractProps(await getHandler(c)) : {}), null);
194
282
  if (isPagesRequest(c)) return pageJsonResponse(c, pageObj);
195
- return renderedResponse(c, await renderFn(pageObj, await getAssetTags(c.req.raw, c.env, componentId)), "text/html; charset=utf-8");
283
+ const assetTags = prepareAssetTags(await getAssetTags(c.req.raw, c.env, componentId));
284
+ return renderedResponse(c, await renderWithAssetTags(renderFn, pageObj, assetTags), "text/html; charset=utf-8", assetTags);
196
285
  }
197
286
  //#endregion
198
287
  export { ValidationError, flattenValidationErrors, handlePageGet, handlePageMutation, safeJsonStringify };