veryfront 0.1.993 → 0.1.994

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 (28) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/src/html/html-injection.d.ts +7 -0
  3. package/esm/src/html/html-injection.d.ts.map +1 -1
  4. package/esm/src/html/html-injection.js +7 -1
  5. package/esm/src/html/hydration-script-builder/templates/renderer.d.ts.map +1 -1
  6. package/esm/src/html/hydration-script-builder/templates/renderer.js +6 -2
  7. package/esm/src/html/hydration-script-builder/templates/router.d.ts.map +1 -1
  8. package/esm/src/html/hydration-script-builder/templates/router.js +49 -8
  9. package/esm/src/rendering/layouts/layout-applicator.d.ts.map +1 -1
  10. package/esm/src/rendering/layouts/layout-applicator.js +2 -5
  11. package/esm/src/rendering/orchestrator/html.d.ts.map +1 -1
  12. package/esm/src/rendering/orchestrator/html.js +1 -0
  13. package/esm/src/rendering/page-rendering.d.ts.map +1 -1
  14. package/esm/src/rendering/page-rendering.js +2 -5
  15. package/esm/src/rendering/script-page-handling.d.ts.map +1 -1
  16. package/esm/src/rendering/script-page-handling.js +2 -5
  17. package/esm/src/routing/api/context-builder.d.ts +4 -0
  18. package/esm/src/routing/api/context-builder.d.ts.map +1 -1
  19. package/esm/src/routing/api/context-builder.js +6 -5
  20. package/esm/src/routing/flatten-route-params.d.ts +11 -0
  21. package/esm/src/routing/flatten-route-params.d.ts.map +1 -0
  22. package/esm/src/routing/flatten-route-params.js +20 -0
  23. package/esm/src/routing/index.d.ts +1 -0
  24. package/esm/src/routing/index.d.ts.map +1 -1
  25. package/esm/src/routing/index.js +1 -0
  26. package/esm/src/utils/version-constant.d.ts +1 -1
  27. package/esm/src/utils/version-constant.js +1 -1
  28. package/package.json +1 -1
package/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.993",
3
+ "version": "0.1.994",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "minimumDependencyAge": {
@@ -9,6 +9,13 @@ export interface InjectHTMLContentOptions {
9
9
  projectDir?: string;
10
10
  /** Whether the page has 'use client' directive */
11
11
  isClientPage?: boolean;
12
+ /**
13
+ * Route params from the initial match, seeded into the 'use client' hydration
14
+ * payload so full-HTML-document client pages hydrate with their params
15
+ * instead of an empty object (issue #2741). Catch-all arrays are preserved;
16
+ * the client runtime joins them (issue #2742).
17
+ */
18
+ params?: Record<string, string | string[]>;
12
19
  /** Whether page is embedded in Studio iframe */
13
20
  studioEmbed?: boolean;
14
21
  /** Project ID for Studio communication */
@@ -1 +1 @@
1
- {"version":3,"file":"html-injection.d.ts","sourceRoot":"","sources":["../../../src/src/html/html-injection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAkB/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gDAAgD;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,WAAW,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IACvC,+DAA+D;IAC/D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAgBD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,wBAAwB,GAChC,MAAM,CAwGR"}
1
+ {"version":3,"file":"html-injection.d.ts","sourceRoot":"","sources":["../../../src/src/html/html-injection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAmB/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC3C,gDAAgD;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,WAAW,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IACvC,+DAA+D;IAC/D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAgBD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,wBAAwB,GAChC,MAAM,CA6GR"}
@@ -2,6 +2,7 @@ import { resolveRelativePath } from "../modules/react-loader/path-resolver.js";
2
2
  import { determineClientModuleStrategy } from "../rendering/rsc/client-module-strategy.js";
3
3
  import { generateLinkTags, generateMetaTags, generateScriptTags, generateStyleTags, } from "./tag-generators.js";
4
4
  import { buildNonceAttribute } from "./html-escape.js";
5
+ import { jsonForInlineScript } from "../security/client/html-sanitizer.js";
5
6
  import { getDevScripts, getDevStyles, getPreviewStylesheetLink, getProdScripts, getStudioScripts, } from "./dev-scripts.js";
6
7
  function toProjectRelativePath(absolutePath, projectDir) {
7
8
  const normalizedPath = absolutePath.replace(/\\/g, "/");
@@ -49,10 +50,15 @@ export function injectHTMLContent(template, content, metadata, options) {
49
50
  const hasBodyClose = /<\/body>/i.test(html);
50
51
  // Inject hydration data for 'use client' pages (before scripts, so client.js can find it)
51
52
  if (options.pagePath && options.isClientPage && hasBodyClose) {
52
- const hydrationData = JSON.stringify({
53
+ // Serialize with jsonForInlineScript, not raw JSON.stringify: route params
54
+ // (and slug) are URL-derived and decoded, so a segment like `%3C/script%3E`
55
+ // would otherwise break out of the <script> tag (reflected XSS). This escapes
56
+ // `<`, `>`, `&`, and line separators, matching the main shell hydration path.
57
+ const hydrationData = jsonForInlineScript({
53
58
  pagePath: toProjectRelativePath(options.pagePath, options.projectDir),
54
59
  slug: options.slug,
55
60
  isClientPage: true,
61
+ params: options.params ?? {},
56
62
  clientModuleStrategy: determineClientModuleStrategy({
57
63
  isLocalProject: options.isLocalProject ?? options.mode === "development",
58
64
  environment: options.environment,
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../../../src/src/html/hydration-script-builder/templates/renderer.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,cAwN7B,CAAC"}
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../../../src/src/html/hydration-script-builder/templates/renderer.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,cA4N7B,CAAC"}
@@ -112,7 +112,11 @@ export const getRendererScript = () => `
112
112
  return;
113
113
  }
114
114
 
115
- const pageProps = { ...(data.props || {}), params: data.params || {} };
115
+ // Normalize catch-all params (arrays -> joined strings) so the hydrated
116
+ // props and page context match the server render. normalizeRouteParams
117
+ // is defined in router.ts, which loads first (issue #2742).
118
+ const normalizedParams = normalizeRouteParams(data.params);
119
+ const pageProps = { ...(data.props || {}), params: normalizedParams };
116
120
  let tree = React.createElement(PageComponent, pageProps);
117
121
 
118
122
  const layouts = data.layouts;
@@ -143,7 +147,7 @@ export const getRendererScript = () => `
143
147
  const pageContext = {
144
148
  slug: data.slug || '',
145
149
  path: data.pagePath || resolvedPathname,
146
- params: data.params || {},
150
+ params: normalizedParams,
147
151
  query: Object.fromEntries(new URLSearchParams(window.location.search)),
148
152
  frontmatter: data.frontmatter || {},
149
153
  headings,
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../../../src/src/html/hydration-script-builder/templates/router.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,cAklC3B,CAAC"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../../../src/src/html/hydration-script-builder/templates/router.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,cA2nC3B,CAAC"}
@@ -604,13 +604,18 @@ export const getRouterScript = () => `
604
604
  window.history.pushState({ pageData, scrollY: 0 }, '', href);
605
605
  }
606
606
 
607
- perfStart('nav:render:' + href);
608
- await renderPageFromData(pageData, targetPath);
609
- perfEnd('nav:render:' + href);
610
-
607
+ // Update the shared router snapshot BEFORE rendering. RouterProvider
608
+ // reads router.params during render, so mutating after renderPageFromData
609
+ // would leave the new page's first render with the previous route's
610
+ // params (issue #2741). pathname/query move up for the same reason.
611
611
  currentPath = targetPath;
612
612
  window.__veryfrontRouter.pathname = targetPath;
613
613
  window.__veryfrontRouter.query = Object.fromEntries(new URLSearchParams(window.location.search));
614
+ window.__veryfrontRouter.params = normalizeRouteParams(pageData.params);
615
+
616
+ perfStart('nav:render:' + href);
617
+ await renderPageFromData(pageData, targetPath);
618
+ perfEnd('nav:render:' + href);
614
619
 
615
620
  if (restoreScroll) {
616
621
  restoreScrollPosition(targetPath);
@@ -708,9 +713,15 @@ export const getRouterScript = () => `
708
713
  }
709
714
  }
710
715
 
716
+ // Normalize catch-all params (arrays -> joined strings) so page props and
717
+ // page context match the server render exactly. SSR emits joined strings
718
+ // via flattenRouteParams; without this the client would hand raw arrays to
719
+ // props and usePageContext() after navigation (issue #2742).
720
+ const normalizedParams = normalizeRouteParams(pageData.params);
721
+
711
722
  let tree = React.createElement(PageComponent, {
712
723
  ...pageData.props,
713
- params: pageData.params
724
+ params: normalizedParams
714
725
  });
715
726
 
716
727
  if (pageData.layouts?.length) {
@@ -733,7 +744,7 @@ export const getRouterScript = () => `
733
744
  const pageContext = {
734
745
  slug: pageData.slug || '',
735
746
  path: pageData.pagePath || targetPath,
736
- params: pageData.params || {},
747
+ params: normalizedParams,
737
748
  query: Object.fromEntries(new URLSearchParams(window.location.search)),
738
749
  frontmatter: pageData.frontmatter || {},
739
750
  headings: headingsArray,
@@ -951,6 +962,22 @@ export const getRouterScript = () => `
951
962
  }, IDLE_PREFETCH_DELAY_MS);
952
963
  }
953
964
 
965
+ // ============================================
966
+ // Route params normalization
967
+ // ============================================
968
+ // Catch-all segments arrive as arrays and are joined so no path info is
969
+ // lost, matching the server flattenRouteParams + RSC hydration normalizer.
970
+ function normalizeRouteParams(raw) {
971
+ const out = {};
972
+ if (!raw) return out;
973
+ for (const key in raw) {
974
+ const value = raw[key];
975
+ if (value === undefined) continue;
976
+ out[key] = Array.isArray(value) ? value.join('/') : value;
977
+ }
978
+ return out;
979
+ }
980
+
954
981
  // ============================================
955
982
  // Router object
956
983
  // ============================================
@@ -974,7 +1001,17 @@ export const getRouterScript = () => `
974
1001
  },
975
1002
  pathname: window.location.pathname,
976
1003
  query: Object.fromEntries(new URLSearchParams(window.location.search)),
977
- params: {},
1004
+ // Seed route params from the hydration data (issue #2741). Catch-all
1005
+ // segments arrive as arrays and are joined so no path info is lost.
1006
+ params: (function () {
1007
+ try {
1008
+ const el = document.getElementById('veryfront-hydration-data');
1009
+ const raw = (JSON.parse(el && el.textContent ? el.textContent : '{}') || {}).params || {};
1010
+ return normalizeRouteParams(raw);
1011
+ } catch (_) {
1012
+ return {};
1013
+ }
1014
+ })(),
978
1015
  isPreview: false,
979
1016
  isMounted: true,
980
1017
  navigate: (path) => navigateSPA(path, true),
@@ -999,10 +1036,14 @@ export const getRouterScript = () => `
999
1036
 
1000
1037
  showNavigationProgress();
1001
1038
  try {
1002
- await renderPageFromData(e.state.pageData, path);
1039
+ // Update the router snapshot before rendering so RouterProvider reads
1040
+ // this route's params, not the previous route's (issue #2741).
1003
1041
  currentPath = path;
1004
1042
  window.__veryfrontRouter.pathname = path;
1005
1043
  window.__veryfrontRouter.query = Object.fromEntries(new URLSearchParams(window.location.search));
1044
+ window.__veryfrontRouter.params = normalizeRouteParams(e.state.pageData.params);
1045
+
1046
+ await renderPageFromData(e.state.pageData, path);
1006
1047
 
1007
1048
  restoreScrollPosition(path);
1008
1049
  hideNavigationProgress();
@@ -1 +1 @@
1
- {"version":3,"file":"layout-applicator.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/layouts/layout-applicator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,YAAY,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAKzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAiBxE,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAC5C,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,eAAe,CAAC;IACxB,WAAW,EAAE,oBAAoB,CAAC;IAClC,gBAAgB,EAAE,aAAa,CAAC;IAChC,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/D;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,IAAI,CAA+B;IAC3C,OAAO,CAAC,UAAU,CAAC,CAAM;IACzB,OAAO,CAAC,MAAM,CAAC,CAAoC;IACnD,OAAO,CAAC,WAAW,CAAC,CAA0B;IAC9C,OAAO,CAAC,QAAQ,CAAC,CAAqD;IACtE,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,kBAAkB,CAAC,CAAyB;IACpD,OAAO,CAAC,+BAA+B,CAAC,CAGrC;gBAES,OAAO,EAAE,wBAAwB;IAiBvC,YAAY,CAChB,WAAW,EAAE,YAAY,CAAC,YAAY,EACtC,QAAQ,EAAE,UAAU,EACpB,YAAY,EAAE,SAAS,GAAG,SAAS,EACnC,aAAa,EAAE,UAAU,EAAE,EAC3B,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACnD,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC;YA+FvB,gBAAgB;YAuDhB,sBAAsB;YAWtB,8BAA8B;YA4D9B,oBAAoB;YAqDpB,mBAAmB;YAuCnB,0BAA0B;CAgEzC"}
1
+ {"version":3,"file":"layout-applicator.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/layouts/layout-applicator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,YAAY,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAKzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAiBxE,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAC5C,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,eAAe,CAAC;IACxB,WAAW,EAAE,oBAAoB,CAAC;IAClC,gBAAgB,EAAE,aAAa,CAAC;IAChC,IAAI,EAAE,aAAa,GAAG,YAAY,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,GAAG,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/D;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,gBAAgB,CAAgB;IACxC,OAAO,CAAC,IAAI,CAA+B;IAC3C,OAAO,CAAC,UAAU,CAAC,CAAM;IACzB,OAAO,CAAC,MAAM,CAAC,CAAoC;IACnD,OAAO,CAAC,WAAW,CAAC,CAA0B;IAC9C,OAAO,CAAC,QAAQ,CAAC,CAAqD;IACtE,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,kBAAkB,CAAC,CAAyB;IACpD,OAAO,CAAC,+BAA+B,CAAC,CAGrC;gBAES,OAAO,EAAE,wBAAwB;IAiBvC,YAAY,CAChB,WAAW,EAAE,YAAY,CAAC,YAAY,EACtC,QAAQ,EAAE,UAAU,EACpB,YAAY,EAAE,SAAS,GAAG,SAAS,EACnC,aAAa,EAAE,UAAU,EAAE,EAC3B,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACnD,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC;YAyFvB,gBAAgB;YAuDhB,sBAAsB;YAWtB,8BAA8B;YA4D9B,oBAAoB;YAqDpB,mBAAmB;YAuCnB,0BAA0B;CAgEzC"}
@@ -1,5 +1,6 @@
1
1
  import { dirname, join } from "../../platform/compat/path/index.js";
2
2
  import { rendererLogger } from "../../utils/index.js";
3
+ import { flattenRouteParams } from "../../routing/index.js";
3
4
  import { withSpan } from "../../observability/tracing/otlp-setup.js";
4
5
  import { SpanNames } from "../../observability/tracing/span-names.js";
5
6
  import { resolve as resolveContract } from "../../extensions/contracts.js";
@@ -66,11 +67,7 @@ export class LayoutApplicator {
66
67
  }
67
68
  const React = await getProjectReact();
68
69
  const headingsArray = this.headings ?? [];
69
- const flatParams = this.params
70
- ? Object.fromEntries(Object.entries(this.params)
71
- .map(([key, value]) => [key, Array.isArray(value) ? value[0] : value])
72
- .filter((entry) => entry[1] !== undefined))
73
- : {};
70
+ const flatParams = flattenRouteParams(this.params);
74
71
  const query = this.requestUrl ? Object.fromEntries(this.requestUrl.searchParams) : {};
75
72
  const pageContext = {
76
73
  slug: pageInfo.entity.slug || "",
@@ -1 +1 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/html.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAClF,YAAY,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AA+FlF,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAsB;gBAExB,MAAM,EAAE,mBAAmB;IAIjC,gBAAgB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC;IA0BjE,kBAAkB,CACtB,WAAW,EAAE,cAAc,EAC3B,OAAO,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,GAC3C,OAAO,CAAC,cAAc,CAAC;YAqEZ,sBAAsB;YAuDtB,4BAA4B;YAiB5B,wBAAwB;YAmBxB,gBAAgB;YAyBhB,kBAAkB;IAwDhC,OAAO,CAAC,cAAc;YAQR,eAAe;YAaf,gBAAgB;IAoF9B;;;;OAIG;YACW,gBAAgB;CAc/B"}
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/orchestrator/html.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAClF,YAAY,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AA+FlF,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAsB;gBAExB,MAAM,EAAE,mBAAmB;IAIjC,gBAAgB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC;IA0BjE,kBAAkB,CACtB,WAAW,EAAE,cAAc,EAC3B,OAAO,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,GAC3C,OAAO,CAAC,cAAc,CAAC;YAqEZ,sBAAsB;YAwDtB,4BAA4B;YAiB5B,wBAAwB;YAmBxB,gBAAgB;YAyBhB,kBAAkB;IAwDhC,OAAO,CAAC,cAAc;YAQR,eAAe;YAaf,gBAAgB;IAoF9B;;;;OAIG;YACW,gBAAgB;CAc/B"}
@@ -163,6 +163,7 @@ export class HTMLGenerator {
163
163
  pagePath,
164
164
  projectDir: this.config.projectDir,
165
165
  isClientPage,
166
+ params: context.options?.params,
166
167
  environment: context.options?.environment,
167
168
  isLocalProject: this.config.mode === "development",
168
169
  nonce: context.options?.nonce,
@@ -1 +1 @@
1
- {"version":3,"file":"page-rendering.d.ts","sourceRoot":"","sources":["../../../src/src/rendering/page-rendering.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAa,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAQrG,UAAU,aAAa;IACrB,WAAW,EAAE,YAAY,CAAC,YAAY,CAAC;IACvC,UAAU,EAAE,UAAU,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAED,UAAU,sBAAsB;IAC9B,UAAU,EAAE,UAAU,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,UAAU,0BAA0B;IAClC,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAInE;AAED,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,OAAO,CAAC,CAyBlB;AAED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,UAAU,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IACR,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GACA,OAAO,CAAC,sBAAsB,CAAC,CA4CjC;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,UAAU,EACpB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,aAAa,EAC/B,WAAW,EAAE,CACX,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,QAAQ,CAAC,EAAE,MAAM,KACd,OAAO,CAAC,SAAS,CAAC,EACvB,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC3C,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gFAAgF;IAChF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GACA,OAAO,CAAC,aAAa,CAAC,CAyHxB"}
1
+ {"version":3,"file":"page-rendering.d.ts","sourceRoot":"","sources":["../../../src/src/rendering/page-rendering.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,YAAY,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAa,UAAU,EAAE,MAAM,mBAAmB,CAAC;AASrG,UAAU,aAAa;IACrB,WAAW,EAAE,YAAY,CAAC,YAAY,CAAC;IACvC,UAAU,EAAE,UAAU,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAED,UAAU,sBAAsB;IAC9B,UAAU,EAAE,UAAU,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,UAAU,0BAA0B;IAClC,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAInE;AAED,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,OAAO,CAAC,CAyBlB;AAED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,UAAU,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IACR,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GACA,OAAO,CAAC,sBAAsB,CAAC,CA4CjC;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,UAAU,EACpB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,aAAa,EAC/B,WAAW,EAAE,CACX,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,QAAQ,CAAC,EAAE,MAAM,KACd,OAAO,CAAC,SAAS,CAAC,EACvB,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC3C,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gFAAgF;IAChF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GACA,OAAO,CAAC,aAAa,CAAC,CAmHxB"}
@@ -3,6 +3,7 @@ import { RENDER_ERROR } from "../errors/error-registry.js";
3
3
  import { mdxRenderer } from "../transforms/mdx/index.js";
4
4
  import { clearMdxEsmCacheNamespace } from "../transforms/mdx/esm-module-loader/index.js";
5
5
  import { getProjectReact } from "../react/index.js";
6
+ import { flattenRouteParams } from "../routing/index.js";
6
7
  import { compileContent } from "../transforms/mdx/compiler/index.js";
7
8
  import { ensureError, getErrorMessage } from "../errors/veryfront-error.js";
8
9
  import { withSpan } from "../observability/tracing/otlp-setup.js";
@@ -77,11 +78,7 @@ export function handleMDXPage(pageInfo, slug, projectDir, mergedComponents, _com
77
78
  }
78
79
  if (typeof mod.generateMetadata === "function") {
79
80
  try {
80
- const params = options?.params
81
- ? Object.fromEntries(Object.entries(options.params)
82
- .map(([k, v]) => [k, Array.isArray(v) ? v[0] : v])
83
- .filter((entry) => entry[1] !== undefined))
84
- : {};
81
+ const params = flattenRouteParams(options?.params);
85
82
  const query = options?.url ? Object.fromEntries(options.url.searchParams) : {};
86
83
  const gen = await mod.generateMetadata({
87
84
  params,
@@ -1 +1 @@
1
- {"version":3,"file":"script-page-handling.d.ts","sourceRoot":"","sources":["../../../src/src/rendering/script-page-handling.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EAGV,YAAY,EAEb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAenE,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC3C,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqHD,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,UAAU,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,YAAY,CAAC,CAiDvB"}
1
+ {"version":3,"file":"script-page-handling.d.ts","sourceRoot":"","sources":["../../../src/src/rendering/script-page-handling.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EAGV,YAAY,EAEb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAenE,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC3C,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+GD,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,UAAU,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,YAAY,CAAC,CAiDvB"}
@@ -9,6 +9,7 @@ import { rewriteNpmImports } from "../transforms/npm-import-rewrites.js";
9
9
  import { dirname, join } from "../platform/compat/path/index.js";
10
10
  import { cwd } from "../platform/compat/process.js";
11
11
  import { RENDER_ERROR } from "../errors/error-registry.js";
12
+ import { flattenRouteParams } from "../routing/index.js";
12
13
  import { createError, toError } from "../errors/veryfront-error.js";
13
14
  import { computeHash } from "./utils/index.js";
14
15
  import { wrapInHTMLShell } from "../html/index.js";
@@ -75,11 +76,7 @@ function extractHtmlAndMetadata(output) {
75
76
  }));
76
77
  }
77
78
  function buildPageContext(pageInfo, slug, params, url) {
78
- const flatParams = params
79
- ? Object.fromEntries(Object.entries(params)
80
- .map(([k, v]) => [k, Array.isArray(v) ? v[0] : v])
81
- .filter((entry) => entry[1] !== undefined))
82
- : {};
79
+ const flatParams = flattenRouteParams(params);
83
80
  return {
84
81
  params: flatParams,
85
82
  query: url ? Object.fromEntries(url.searchParams) : {},
@@ -16,5 +16,9 @@ export interface APIContext {
16
16
  fs: FileSystemAdapter;
17
17
  }
18
18
  export declare function createContext(request: Request, match: RouteMatch, fs: FileSystemAdapter): APIContext;
19
+ /**
20
+ * @deprecated Use {@link flattenRouteParams} directly. Kept as a thin alias so
21
+ * the routing barrel exposes a single flattener implementation (issue #2742).
22
+ */
19
23
  export declare function normalizeParams(params: Record<string, string | string[]>): Record<string, string>;
20
24
  //# sourceMappingURL=context-builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context-builder.d.ts","sourceRoot":"","sources":["../../../../src/src/routing/api/context-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB,mDAAmD;AACnD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,QAAQ,CAAC;IACvD,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,QAAQ,CAAC;IACtD,EAAE,EAAE,iBAAiB,CAAC;CACvB;AAgBD,wBAAgB,aAAa,CAC3B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,iBAAiB,GACpB,UAAU,CAmBZ;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GACxC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxB"}
1
+ {"version":3,"file":"context-builder.d.ts","sourceRoot":"","sources":["../../../../src/src/routing/api/context-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAG3D,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB,mDAAmD;AACnD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,QAAQ,CAAC;IACvD,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,QAAQ,CAAC;IACtD,EAAE,EAAE,iBAAiB,CAAC;CACvB;AAgBD,wBAAgB,aAAa,CAC3B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,iBAAiB,GACpB,UAAU,CAmBZ;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GACxC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAExB"}
@@ -1,4 +1,5 @@
1
1
  import { parseCookies } from "../../utils/cookie-utils.js";
2
+ import { flattenRouteParams } from "../flatten-route-params.js";
2
3
  export { parseCookies };
3
4
  function createResponse(body, contentType, init) {
4
5
  return new Response(body, {
@@ -26,10 +27,10 @@ export function createContext(request, match, fs) {
26
27
  fs,
27
28
  };
28
29
  }
30
+ /**
31
+ * @deprecated Use {@link flattenRouteParams} directly. Kept as a thin alias so
32
+ * the routing barrel exposes a single flattener implementation (issue #2742).
33
+ */
29
34
  export function normalizeParams(params) {
30
- const out = {};
31
- for (const [key, value] of Object.entries(params)) {
32
- out[key] = Array.isArray(value) ? value.join("/") : value;
33
- }
34
- return out;
35
+ return flattenRouteParams(params);
35
36
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Flattens matched route params (which may be arrays for catch-all `[...slug]`
3
+ * segments) into the `Record<string, string>` shape the router value exposes.
4
+ *
5
+ * Catch-all segments are **joined with `/`** so no path information is lost —
6
+ * `/docs/guides/intro` -> `{ slug: "guides/intro" }`, not `{ slug: "guides" }`.
7
+ * This matches the client SPA normalizer and the RSC hydration normalizer, so
8
+ * server and client agree.
9
+ */
10
+ export declare function flattenRouteParams(params?: Record<string, string | string[]>): Record<string, string>;
11
+ //# sourceMappingURL=flatten-route-params.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flatten-route-params.d.ts","sourceRoot":"","sources":["../../../src/src/routing/flatten-route-params.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GACzC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxB"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Flattens matched route params (which may be arrays for catch-all `[...slug]`
3
+ * segments) into the `Record<string, string>` shape the router value exposes.
4
+ *
5
+ * Catch-all segments are **joined with `/`** so no path information is lost —
6
+ * `/docs/guides/intro` -> `{ slug: "guides/intro" }`, not `{ slug: "guides" }`.
7
+ * This matches the client SPA normalizer and the RSC hydration normalizer, so
8
+ * server and client agree.
9
+ */
10
+ export function flattenRouteParams(params) {
11
+ if (!params)
12
+ return {};
13
+ const flat = {};
14
+ for (const [key, value] of Object.entries(params)) {
15
+ if (value === undefined)
16
+ continue;
17
+ flat[key] = Array.isArray(value) ? value.join("/") : value;
18
+ }
19
+ return flat;
20
+ }
@@ -5,6 +5,7 @@
5
5
  * @module routing
6
6
  */
7
7
  export type { Route, RouteMatch } from "./matchers/index.js";
8
+ export { flattenRouteParams } from "./flatten-route-params.js";
8
9
  export { getSpecificityScore, matchRoute, normalizePath, PageRouteMatcher, parseRoute, } from "./matchers/index.js";
9
10
  export type { PathCandidates, RouteParams } from "./slug-mapper/index.js";
10
11
  export { extractParams, getPathCandidates, getSlugFromPath, getSupportedExtensions, isDynamicRoute, matchesPattern, normalizeSlug, pathToSlug, slugToPath, } from "./slug-mapper/index.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/routing/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,UAAU,GACX,MAAM,qBAAqB,CAAC;AAE7B,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,cAAc,EACd,aAAa,EACb,UAAU,EACV,UAAU,GACX,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,yBAAyB,EACzB,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EACL,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,SAAS,EACT,mBAAmB,EACnB,IAAI,EACJ,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/routing/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,UAAU,GACX,MAAM,qBAAqB,CAAC;AAE7B,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,cAAc,EACd,aAAa,EACb,UAAU,EACV,UAAU,GACX,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,yBAAyB,EACzB,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EACL,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,SAAS,EACT,mBAAmB,EACnB,IAAI,EACJ,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,YAAY,GACb,MAAM,gBAAgB,CAAC"}
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * @module routing
6
6
  */
7
+ export { flattenRouteParams } from "./flatten-route-params.js";
7
8
  export { getSpecificityScore, matchRoute, normalizePath, PageRouteMatcher, parseRoute, } from "./matchers/index.js";
8
9
  export { extractParams, getPathCandidates, getSlugFromPath, getSupportedExtensions, isDynamicRoute, matchesPattern, normalizeSlug, pathToSlug, slugToPath, } from "./slug-mapper/index.js";
9
10
  export { extractPageDataFromScript, NavigationHandlers, PageLoader, PageTransition, ViewportPrefetch, } from "./client/index.js";
@@ -1,3 +1,3 @@
1
1
  /** Shared version value. */
2
- export declare const VERSION = "0.1.993";
2
+ export declare const VERSION = "0.1.994";
3
3
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,4 +1,4 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
3
  /** Shared version value. */
4
- export const VERSION = "0.1.993";
4
+ export const VERSION = "0.1.994";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.993",
3
+ "version": "0.1.994",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",