reroute-js 1.0.0-alpha.39 → 1.0.0-alpha.40

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 (236) hide show
  1. package/README.md +34 -0
  2. package/cli/bin.d.ts +1 -1
  3. package/cli/bin.js +231 -50
  4. package/cli/bin.js.map +13 -13
  5. package/cli/index.d.ts +1 -1
  6. package/cli/index.js +8 -5
  7. package/cli/index.js.map +11 -10
  8. package/cli/src/cli.d.ts +1 -1
  9. package/cli/src/commands/analyze.d.ts +1 -1
  10. package/cli/src/commands/build.d.ts +1 -1
  11. package/cli/src/commands/dev.d.ts +1 -1
  12. package/cli/src/commands/gen.d.ts +1 -1
  13. package/cli/src/commands/gen.d.ts.map +1 -1
  14. package/cli/src/commands/index.d.ts +1 -1
  15. package/cli/src/commands/init.d.ts +1 -1
  16. package/cli/src/commands/lib/assets.d.ts +1 -1
  17. package/cli/src/commands/lib/bundler.d.ts +1 -1
  18. package/cli/src/commands/lib/command.d.ts +1 -1
  19. package/cli/src/commands/lib/env.d.ts +1 -1
  20. package/cli/src/commands/lib/images.d.ts +1 -1
  21. package/cli/src/commands/lib/index.d.ts +1 -1
  22. package/cli/src/commands/lib/log.d.ts +1 -1
  23. package/cli/src/commands/lib/markdown/availability.d.ts +1 -1
  24. package/cli/src/commands/lib/markdown/index.d.ts +1 -1
  25. package/cli/src/commands/lib/markdown/processor.d.ts +1 -1
  26. package/cli/src/commands/lib/prerender.d.ts +1 -1
  27. package/cli/src/commands/lib/production.d.ts +1 -1
  28. package/cli/src/commands/lib/routes.d.ts +1 -1
  29. package/cli/src/commands/lib/server.d.ts +1 -1
  30. package/cli/src/commands/lib/ssr-strip.d.ts +1 -1
  31. package/cli/src/commands/lib/static.d.ts +1 -1
  32. package/cli/src/commands/lib/streaming/analyzer.d.ts +1 -1
  33. package/cli/src/commands/lib/streaming/suspense.d.ts +1 -1
  34. package/cli/src/commands/lib/supervise.d.ts +1 -1
  35. package/cli/src/commands/lib/terminal-ui.d.ts +1 -1
  36. package/cli/src/commands/lib/theme.d.ts +1 -1
  37. package/cli/src/commands/lib/version.d.ts +1 -1
  38. package/cli/src/commands/lib/watch.d.ts +1 -1
  39. package/cli/src/commands/og.d.ts +1 -1
  40. package/cli/src/commands/start.d.ts +1 -1
  41. package/cli/src/commands/static-render.d.ts +1 -1
  42. package/cli/src/index.d.ts +1 -1
  43. package/core/index.d.ts +1 -1
  44. package/core/index.js +221 -46
  45. package/core/index.js.map +12 -12
  46. package/core/src/app-paths.d.ts +1 -1
  47. package/core/src/bundler/hash.d.ts +1 -1
  48. package/core/src/bundler/index.d.ts +1 -1
  49. package/core/src/config.d.ts +1 -1
  50. package/core/src/content/discovery.d.ts +1 -1
  51. package/core/src/content/index.d.ts +1 -1
  52. package/core/src/content/metadata.d.ts +1 -1
  53. package/core/src/i18n/catalog.d.ts +1 -1
  54. package/core/src/i18n/discover.d.ts +1 -1
  55. package/core/src/i18n/discover.d.ts.map +1 -1
  56. package/core/src/i18n/index.d.ts +2 -2
  57. package/core/src/i18n/index.d.ts.map +1 -1
  58. package/core/src/i18n/messages.d.ts +1 -1
  59. package/core/src/i18n/negotiate.d.ts +1 -1
  60. package/core/src/i18n/pathname.d.ts +32 -4
  61. package/core/src/i18n/pathname.d.ts.map +1 -1
  62. package/core/src/i18n/resolve.d.ts +3 -2
  63. package/core/src/i18n/resolve.d.ts.map +1 -1
  64. package/core/src/i18n/types.d.ts +44 -1
  65. package/core/src/i18n/types.d.ts.map +1 -1
  66. package/core/src/image.d.ts +1 -1
  67. package/core/src/index.d.ts +1 -1
  68. package/core/src/llms/discovery.d.ts +1 -1
  69. package/core/src/llms/extractor.d.ts +1 -1
  70. package/core/src/llms/formatter.d.ts +1 -1
  71. package/core/src/llms/full-generator.d.ts +1 -1
  72. package/core/src/llms/index-generator.d.ts +1 -1
  73. package/core/src/llms/index.d.ts +1 -1
  74. package/core/src/og/discovery.d.ts +1 -1
  75. package/core/src/og/index.d.ts +1 -1
  76. package/core/src/og/meta.d.ts +1 -1
  77. package/core/src/og/render.d.ts +1 -1
  78. package/core/src/og/types.d.ts +1 -1
  79. package/core/src/robots/discovery.d.ts +1 -1
  80. package/core/src/robots/generator.d.ts +1 -1
  81. package/core/src/robots/index.d.ts +1 -1
  82. package/core/src/robots/policies.d.ts +1 -1
  83. package/core/src/rss/discovery.d.ts +1 -1
  84. package/core/src/rss/generator.d.ts +1 -1
  85. package/core/src/rss/index.d.ts +1 -1
  86. package/core/src/sitemap/discovery.d.ts +1 -1
  87. package/core/src/sitemap/generator.d.ts +12 -2
  88. package/core/src/sitemap/generator.d.ts.map +1 -1
  89. package/core/src/sitemap/index.d.ts +1 -1
  90. package/core/src/ssr/index.d.ts +1 -1
  91. package/core/src/ssr/lib/cache.d.ts +1 -1
  92. package/core/src/ssr/lib/cleanup.d.ts +1 -1
  93. package/core/src/ssr/lib/collections.d.ts +1 -1
  94. package/core/src/ssr/lib/compression.d.ts +1 -1
  95. package/core/src/ssr/lib/compute/content.d.ts +1 -1
  96. package/core/src/ssr/lib/compute/index.d.ts +1 -1
  97. package/core/src/ssr/lib/compute/layouts.d.ts +1 -1
  98. package/core/src/ssr/lib/compute/layouts.d.ts.map +1 -1
  99. package/core/src/ssr/lib/compute/routes.d.ts +1 -1
  100. package/core/src/ssr/lib/compute/routes.d.ts.map +1 -1
  101. package/core/src/ssr/lib/content-path.d.ts +13 -2
  102. package/core/src/ssr/lib/content-path.d.ts.map +1 -1
  103. package/core/src/ssr/lib/data.d.ts +1 -1
  104. package/core/src/ssr/lib/html.d.ts +1 -1
  105. package/core/src/ssr/lib/imports.d.ts +1 -1
  106. package/core/src/ssr/lib/index.d.ts +1 -1
  107. package/core/src/ssr/lib/layouts.d.ts +1 -1
  108. package/core/src/ssr/lib/metadata.d.ts +1 -1
  109. package/core/src/ssr/lib/metadata.d.ts.map +1 -1
  110. package/core/src/ssr/lib/mime.d.ts +1 -1
  111. package/core/src/ssr/lib/modules.d.ts +1 -1
  112. package/core/src/ssr/lib/path.d.ts +1 -1
  113. package/core/src/ssr/lib/preload.d.ts +1 -1
  114. package/core/src/ssr/lib/route-match.d.ts +1 -1
  115. package/core/src/ssr/lib/scripts/collections.d.ts +1 -1
  116. package/core/src/ssr/lib/scripts/data.d.ts +1 -1
  117. package/core/src/ssr/lib/scripts/escape.d.ts +1 -1
  118. package/core/src/ssr/lib/scripts/feeds.d.ts +1 -1
  119. package/core/src/ssr/lib/scripts/index.d.ts +1 -1
  120. package/core/src/ssr/lib/seed.d.ts +1 -1
  121. package/core/src/ssr/lib/serialize.d.ts +1 -1
  122. package/core/src/ssr/lib/setup.d.ts +1 -1
  123. package/core/src/ssr/lib/styles.d.ts +1 -1
  124. package/core/src/ssr/lib/template.d.ts +1 -1
  125. package/core/src/ssr/lib/types.d.ts +7 -1
  126. package/core/src/ssr/lib/types.d.ts.map +1 -1
  127. package/core/src/ssr/render.d.ts +1 -1
  128. package/core/src/ssr/stream.d.ts +1 -1
  129. package/core/src/ssr/stream.d.ts.map +1 -1
  130. package/elysia/index.d.ts +1 -1
  131. package/elysia/index.js +370 -87
  132. package/elysia/index.js.map +18 -17
  133. package/elysia/src/index.d.ts +1 -1
  134. package/elysia/src/libs/assets.d.ts +1 -1
  135. package/elysia/src/libs/cache.d.ts +1 -1
  136. package/elysia/src/libs/caching.d.ts +1 -1
  137. package/elysia/src/libs/devReload.d.ts +1 -1
  138. package/elysia/src/libs/http.d.ts +1 -1
  139. package/elysia/src/libs/i18n.d.ts +14 -3
  140. package/elysia/src/libs/i18n.d.ts.map +1 -1
  141. package/elysia/src/libs/image.d.ts +1 -1
  142. package/elysia/src/libs/index.d.ts +1 -1
  143. package/elysia/src/libs/lazyData.d.ts +38 -0
  144. package/elysia/src/libs/lazyData.d.ts.map +1 -0
  145. package/elysia/src/libs/llms.d.ts +1 -1
  146. package/elysia/src/libs/response.d.ts +1 -1
  147. package/elysia/src/libs/serving.d.ts +1 -1
  148. package/elysia/src/plugin.d.ts +1 -1
  149. package/elysia/src/routes/artifacts.d.ts +1 -1
  150. package/elysia/src/routes/content.d.ts +1 -1
  151. package/elysia/src/routes/image.d.ts +1 -1
  152. package/elysia/src/routes/index.d.ts +1 -1
  153. package/elysia/src/routes/internal.d.ts +1 -1
  154. package/elysia/src/routes/internal.d.ts.map +1 -1
  155. package/elysia/src/routes/liveReloadClient.d.ts +1 -1
  156. package/elysia/src/routes/llms.d.ts +1 -1
  157. package/elysia/src/routes/og.d.ts +1 -1
  158. package/elysia/src/routes/redirects.d.ts +1 -1
  159. package/elysia/src/routes/robots.d.ts +1 -1
  160. package/elysia/src/routes/rss.d.ts +1 -1
  161. package/elysia/src/routes/search.d.ts +1 -1
  162. package/elysia/src/routes/sitemap.d.ts +1 -1
  163. package/elysia/src/routes/sitemap.d.ts.map +1 -1
  164. package/elysia/src/routes/ssr.d.ts +1 -1
  165. package/elysia/src/routes/ssr.d.ts.map +1 -1
  166. package/elysia/src/routes/static.d.ts +1 -1
  167. package/elysia/src/routes/themeCssRefresh.d.ts +1 -1
  168. package/elysia/src/types.d.ts +1 -1
  169. package/package.json +1 -1
  170. package/react/index.d.ts +1 -1
  171. package/react/index.js +85 -20
  172. package/react/index.js.map +6 -6
  173. package/react/src/components/ClientOnly.d.ts +1 -1
  174. package/react/src/components/ContentRoute.d.ts +1 -1
  175. package/react/src/components/Image.d.ts +1 -1
  176. package/react/src/components/LazyRoute.d.ts +1 -1
  177. package/react/src/components/Link.d.ts +1 -1
  178. package/react/src/components/Markdown.d.ts +1 -1
  179. package/react/src/components/Outlet.d.ts +1 -1
  180. package/react/src/components/index.d.ts +1 -1
  181. package/react/src/hooks/index.d.ts +1 -1
  182. package/react/src/hooks/useContent.d.ts +1 -1
  183. package/react/src/hooks/useData.d.ts +1 -1
  184. package/react/src/hooks/useFeed.d.ts +1 -1
  185. package/react/src/hooks/useLayoutData.d.ts +1 -1
  186. package/react/src/hooks/useLlms.d.ts +1 -1
  187. package/react/src/hooks/useNavigate.d.ts +1 -1
  188. package/react/src/hooks/useParams.d.ts +1 -1
  189. package/react/src/hooks/useRouter.d.ts +1 -1
  190. package/react/src/hooks/useSearch.d.ts +1 -1
  191. package/react/src/hooks/useSearchParams.d.ts +1 -1
  192. package/react/src/hooks/useToc.d.ts +1 -1
  193. package/react/src/i18n/LocaleProvider.d.ts +20 -2
  194. package/react/src/i18n/LocaleProvider.d.ts.map +1 -1
  195. package/react/src/i18n/helpers.d.ts +31 -3
  196. package/react/src/i18n/helpers.d.ts.map +1 -1
  197. package/react/src/i18n/index.d.ts +2 -2
  198. package/react/src/i18n/index.d.ts.map +1 -1
  199. package/react/src/index.d.ts +1 -1
  200. package/react/src/lib/bundle.d.ts +1 -1
  201. package/react/src/lib/collection.d.ts +1 -1
  202. package/react/src/lib/content.d.ts +1 -1
  203. package/react/src/lib/head.d.ts +1 -1
  204. package/react/src/lib/index.d.ts +1 -1
  205. package/react/src/lib/layouts.d.ts +1 -1
  206. package/react/src/lib/lazy-route.d.ts +1 -1
  207. package/react/src/lib/route-loader.d.ts +1 -1
  208. package/react/src/lib/static-data.d.ts +1 -1
  209. package/react/src/providers/ContentProvider.d.ts +1 -1
  210. package/react/src/providers/RerouteProvider.d.ts +1 -1
  211. package/react/src/providers/RerouteProvider.d.ts.map +1 -1
  212. package/react/src/providers/RouterProvider.d.ts +4 -2
  213. package/react/src/providers/RouterProvider.d.ts.map +1 -1
  214. package/react/src/providers/index.d.ts +1 -1
  215. package/react/src/types/any.d.ts +1 -1
  216. package/react/src/types/index.d.ts +1 -1
  217. package/react/src/types/router.d.ts +7 -1
  218. package/react/src/types/router.d.ts.map +1 -1
  219. package/telemetry/react.d.ts +1 -1
  220. package/telemetry/react.js +2 -2
  221. package/telemetry/react.js.map +1 -1
  222. package/telemetry/server.d.ts +1 -1
  223. package/telemetry/server.js +6 -3
  224. package/telemetry/server.js.map +11 -10
  225. package/telemetry/src/react/api.d.ts +1 -1
  226. package/telemetry/src/react/context.d.ts +1 -1
  227. package/telemetry/src/react/index.d.ts +1 -1
  228. package/telemetry/src/react/telemetry.d.ts +1 -1
  229. package/telemetry/src/server/context.d.ts +1 -1
  230. package/telemetry/src/server/headers/extractor.d.ts +1 -1
  231. package/telemetry/src/server/headers/index.d.ts +1 -1
  232. package/telemetry/src/server/headers/presets.d.ts +1 -1
  233. package/telemetry/src/server/index.d.ts +1 -1
  234. package/telemetry/src/server/instrumentation.d.ts +1 -1
  235. package/telemetry/src/server/plugin.d.ts +1 -1
  236. package/telemetry/src/server/sourcemap.d.ts +1 -1
package/elysia/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * reroute-js v1.0.0-alpha.39
2
+ * reroute-js v1.0.0-alpha.40
3
3
  *
4
4
  * @license MIT
5
5
  * @copyright 2026 stewones <hi@stewan.io>
@@ -286,6 +286,9 @@ async function discoverI18n(cwd, options = {}) {
286
286
  const defaultLocale = options.defaultLocale ?? DEFAULT_LOCALE;
287
287
  const cookie = options.cookie ?? DEFAULT_COOKIE;
288
288
  const redirectContentOnNegotiate = options.redirectContentOnNegotiate ?? true;
289
+ const prefixPages = options.prefixPages ?? false;
290
+ const unprefixedPaths = options.unprefixedPaths ?? [];
291
+ const localeParam = options.localeParam ?? "";
289
292
  const absDir = join3(cwd, localesDir);
290
293
  const disabled = {
291
294
  enabled: false,
@@ -294,6 +297,9 @@ async function discoverI18n(cwd, options = {}) {
294
297
  localesDir,
295
298
  cookie,
296
299
  redirectContentOnNegotiate,
300
+ prefixPages,
301
+ unprefixedPaths,
302
+ localeParam,
297
303
  catalogs: {}
298
304
  };
299
305
  let catalogs;
@@ -317,6 +323,9 @@ async function discoverI18n(cwd, options = {}) {
317
323
  localesDir,
318
324
  cookie,
319
325
  redirectContentOnNegotiate,
326
+ prefixPages,
327
+ unprefixedPaths,
328
+ localeParam,
320
329
  catalogs
321
330
  };
322
331
  }
@@ -417,11 +426,15 @@ var exports_pathname = {};
417
426
  __export(exports_pathname, {
418
427
  contentKey: () => contentKey,
419
428
  hreflangTags: () => hreflangTags,
429
+ isUnprefixedPath: () => isUnprefixedPath,
420
430
  localizeContentHref: () => localizeContentHref,
421
431
  localizeHref: () => localizeHref,
432
+ localizePagePath: () => localizePagePath,
422
433
  looksLikeLocale: () => looksLikeLocale,
423
434
  normalizePathname: () => normalizePathname,
435
+ pageHreflangTags: () => pageHreflangTags,
424
436
  parseContentPath: () => parseContentPath,
437
+ parseLocalePrefix: () => parseLocalePrefix,
425
438
  toOgLocale: () => toOgLocale
426
439
  });
427
440
  function looksLikeLocale(segment) {
@@ -469,13 +482,36 @@ function localizeContentHref(collection, name, locale, defaultLocale) {
469
482
  }
470
483
  return `/${collection}/${locale}/${name}`;
471
484
  }
472
- function localizeHref(to, locale, defaultLocale, availableLocales = [], collections) {
485
+ function parseLocalePrefix(pathname, availableLocales) {
486
+ const [, segment = "", ...rest] = normalizePathname(pathname).split("/");
487
+ const locale = availableLocales.find((l) => l.toLowerCase() === segment.toLowerCase());
488
+ if (!locale)
489
+ return;
490
+ return { locale, segment, routingPathname: `/${rest.join("/")}` };
491
+ }
492
+ function localizePagePath(pathname, locale, defaultLocale) {
493
+ if (locale === defaultLocale)
494
+ return pathname;
495
+ return pathname === "/" ? `/${locale}` : `/${locale}${pathname}`;
496
+ }
497
+ function isUnprefixedPath(pathname, unprefixedPaths = []) {
498
+ const path = normalizePathname(pathname);
499
+ return unprefixedPaths.some((prefix) => {
500
+ const base = prefix.replace(/\/+$/, "");
501
+ return base === "" || path === base || path.startsWith(`${base}/`);
502
+ });
503
+ }
504
+ function localizeHref(to, locale, defaultLocale, availableLocales = [], collections, prefixPages) {
473
505
  try {
474
506
  const url = new URL(to, "http://local");
475
- const parsed = parseContentPath(url.pathname, availableLocales, collections);
476
- if (!parsed)
507
+ const parsed = parseContentPath(url.pathname, availableLocales, prefixPages ? collections ?? [] : collections);
508
+ if (parsed) {
509
+ url.pathname = localizeContentHref(parsed.collection, parsed.name, locale, defaultLocale);
510
+ } else if (prefixPages && to.startsWith("/") && url.origin === "http://local" && !/\.[^/]+$/.test(url.pathname) && !parseLocalePrefix(url.pathname, availableLocales) && !isUnprefixedPath(url.pathname, prefixPages.unprefixedPaths)) {
511
+ url.pathname = localizePagePath(url.pathname, locale, defaultLocale);
512
+ } else {
477
513
  return to;
478
- url.pathname = localizeContentHref(parsed.collection, parsed.name, locale, defaultLocale);
514
+ }
479
515
  return `${url.pathname}${url.search}${url.hash}`;
480
516
  } catch {
481
517
  return to;
@@ -490,18 +526,20 @@ function contentKey(collection, name, locale, defaultLocale = "en-US") {
490
526
  function toOgLocale(locale) {
491
527
  return locale.replace(/-/g, "_");
492
528
  }
493
- function hreflangTags(baseUrl, collection, name, locales, defaultLocale) {
529
+ function alternateLinks(baseUrl, locales, defaultLocale, pathFor) {
494
530
  const base = baseUrl.replace(/\/$/, "");
495
- const tags = [];
496
- for (const locale of locales) {
497
- const path = localizeContentHref(collection, name, locale, defaultLocale);
498
- tags.push(`<link rel="alternate" hreflang="${locale}" href="${base}${path}" />`);
499
- }
500
- const defaultPath = localizeContentHref(collection, name, defaultLocale, defaultLocale);
501
- tags.push(`<link rel="alternate" hreflang="x-default" href="${base}${defaultPath}" />`);
502
- return tags.join(`
531
+ return [...locales, "x-default"].map((hreflang) => {
532
+ const path = pathFor(hreflang === "x-default" ? defaultLocale : hreflang);
533
+ return `<link rel="alternate" hreflang="${hreflang}" href="${base}${path}" />`;
534
+ }).join(`
503
535
  `);
504
536
  }
537
+ function hreflangTags(baseUrl, collection, name, locales, defaultLocale) {
538
+ return alternateLinks(baseUrl, locales, defaultLocale, (locale) => localizeContentHref(collection, name, locale, defaultLocale));
539
+ }
540
+ function pageHreflangTags(baseUrl, routingPathname, locales, defaultLocale) {
541
+ return alternateLinks(baseUrl, locales, defaultLocale, (locale) => localizePagePath(routingPathname, locale, defaultLocale));
542
+ }
505
543
  var init_pathname = () => {};
506
544
 
507
545
  // packages/core/src/i18n/resolve.ts
@@ -525,7 +563,71 @@ function stripLangParam(search) {
525
563
  const rest = params.toString();
526
564
  return rest ? `?${rest}` : "";
527
565
  }
566
+ function unprefix(pagePathname, search, defaultLocale) {
567
+ const query = search ? `?${search.replace(/^\?/, "")}` : "";
568
+ return {
569
+ locale: defaultLocale,
570
+ routingPathname: pagePathname,
571
+ fromPath: true,
572
+ fromQuery: false,
573
+ redirectTo: `${pagePathname}${query}`,
574
+ permanent: true,
575
+ urlOnly: true
576
+ };
577
+ }
578
+ function resolvePrefixedLocale(input) {
579
+ const prefix = parseLocalePrefix(input.pathname, input.availableLocales);
580
+ const pagePathname = prefix?.routingPathname ?? normalizePathname(input.pathname);
581
+ if (!isUnprefixedPath(pagePathname, input.unprefixedPaths)) {
582
+ return resolvePrefixedPage(input, prefix, pagePathname);
583
+ }
584
+ return prefix ? unprefix(pagePathname, input.search, input.defaultLocale) : resolveNegotiatedLocale(input);
585
+ }
586
+ function resolvePrefixedPage(input, prefix, pagePathname) {
587
+ const { search, availableLocales, defaultLocale } = input;
588
+ const isAvailable = (locale2) => !!locale2 && availableLocales.some((l) => l.toLowerCase() === locale2.toLowerCase());
589
+ const parsed = parseContentPath(pagePathname, availableLocales, input.collections ?? []);
590
+ const urlLocale = parsed?.locale ?? prefix?.locale ?? defaultLocale;
591
+ const queryLocale = parseLangParam(search, isAvailable, availableLocales);
592
+ const locale = queryLocale ?? urlLocale;
593
+ const misplaced = !!prefix && (!!parsed || prefix.locale === defaultLocale || prefix.segment !== prefix.locale);
594
+ const defaultSegment = !prefix && parsed?.locale === defaultLocale;
595
+ const canonical = parsed ? localizeContentHref(parsed.collection, parsed.name, locale, defaultLocale) : localizePagePath(pagePathname, locale, defaultLocale);
596
+ return {
597
+ locale,
598
+ routingPathname: parsed?.routingPathname ?? pagePathname,
599
+ fromPath: !!(parsed?.locale || prefix),
600
+ fromQuery: !!queryLocale,
601
+ redirectTo: queryLocale || misplaced || defaultSegment ? `${canonical}${stripLangParam(search)}` : undefined,
602
+ permanent: !queryLocale && (misplaced || defaultSegment),
603
+ urlOnly: true,
604
+ parsed: parsed ? { ...parsed, locale } : undefined
605
+ };
606
+ }
607
+ function resolveParamLocale(input, localeParam) {
608
+ const { pathname, search, availableLocales, defaultLocale } = input;
609
+ const parsed = parseContentPath(pathname, availableLocales, input.collections);
610
+ const named = new URLSearchParams(search ?? "").get(localeParam);
611
+ const param = named ? availableLocales.find((l) => l.toLowerCase() === named.toLowerCase()) : undefined;
612
+ const pathLocale = parsed?.locale && canonicalLocale(parsed.locale, availableLocales);
613
+ const locale = pathLocale || param || defaultLocale;
614
+ return {
615
+ locale,
616
+ routingPathname: parsed?.routingPathname ?? normalizePathname(pathname),
617
+ fromPath: !!pathLocale,
618
+ fromQuery: false,
619
+ urlOnly: true,
620
+ parsed: parsed ? { ...parsed, locale: pathLocale || undefined } : undefined
621
+ };
622
+ }
528
623
  function resolveLocale(input) {
624
+ if (input.prefixPages)
625
+ return resolvePrefixedLocale(input);
626
+ if (input.localeParam)
627
+ return resolveParamLocale(input, input.localeParam);
628
+ return resolveNegotiatedLocale(input);
629
+ }
630
+ function resolveNegotiatedLocale(input) {
529
631
  const {
530
632
  pathname,
531
633
  search,
@@ -1416,7 +1518,9 @@ function matchRoutes(routes, pathname) {
1416
1518
  // packages/core/src/ssr/lib/content-path.ts
1417
1519
  var exports_content_path = {};
1418
1520
  __export(exports_content_path, {
1419
- resolveContentPathParts: () => resolveContentPathParts
1521
+ pagePathname: () => pagePathname,
1522
+ resolveContentPathParts: () => resolveContentPathParts,
1523
+ routingPathname: () => routingPathname
1420
1524
  });
1421
1525
  function resolveContentPathParts(pathname) {
1422
1526
  const pathOnly = pathname.split("?")[0].split("#")[0];
@@ -1454,6 +1558,17 @@ function resolveContentPathParts(pathname) {
1454
1558
  routingPathname: `/${parts.join("/")}`
1455
1559
  };
1456
1560
  }
1561
+ function pagePathname(pathname) {
1562
+ const pathOnly = pathname.split("?")[0].split("#")[0];
1563
+ const i18n = globalThis.__REROUTE_I18N__;
1564
+ if (!i18n?.prefixPages)
1565
+ return pathOnly;
1566
+ return parseLocalePrefix(pathOnly, i18n.locales || [])?.routingPathname ?? pathOnly;
1567
+ }
1568
+ function routingPathname(pathname) {
1569
+ const page = pagePathname(pathname);
1570
+ return resolveContentPathParts(page)?.routingPathname ?? page;
1571
+ }
1457
1572
  var init_content_path = __esm(() => {
1458
1573
  init_pathname();
1459
1574
  });
@@ -1696,7 +1811,7 @@ async function computeLayoutData(pathname, clientDir, _cwd, isWatchMode, options
1696
1811
  try {
1697
1812
  const layouts = routesModule?.layouts;
1698
1813
  const routes = routesModule?.routes;
1699
- const matchingLayouts = getMatchingLayouts(pathname, layouts, routes);
1814
+ const matchingLayouts = getMatchingLayouts(routingPathname(pathname), layouts, routes);
1700
1815
  for (const layout of matchingLayouts) {
1701
1816
  try {
1702
1817
  const layoutStatus = await processLayout(layout, pathname, clientDir, isWatchMode, streaming, maxAgeMs, searchParams, ssrData, pendingTasks, options.headers);
@@ -1725,6 +1840,7 @@ function mergeLayoutData(ssrData, pathname) {
1725
1840
  }
1726
1841
  var init_layouts2 = __esm(() => {
1727
1842
  init_cache();
1843
+ init_content_path();
1728
1844
  init_imports();
1729
1845
  init_layouts();
1730
1846
  });
@@ -1769,9 +1885,7 @@ async function computeRouteData(pathname, clientDir, _cwd, isWatchMode, options,
1769
1885
  }).catch(() => {}));
1770
1886
  };
1771
1887
  try {
1772
- const resolved = resolveContentPathParts(pathname);
1773
- const pathnameOnly = (resolved?.routingPathname || pathname).split("?")[0];
1774
- const match = typeof routesModule?.matchRoute === "function" ? routesModule.matchRoute(pathnameOnly) : null;
1888
+ const match = typeof routesModule?.matchRoute === "function" ? routesModule.matchRoute(routingPathname(pathname)) : null;
1775
1889
  const route = match?.route;
1776
1890
  const params = match?.params || {};
1777
1891
  if (!route || typeof route.path !== "string") {
@@ -2767,9 +2881,8 @@ async function extractPageMetadata(pathname, clientDir, cwd, isWatchMode, curren
2767
2881
  let perPageHead = "";
2768
2882
  let pageLang;
2769
2883
  let statusOverride = currentStatusOverride;
2770
- const resolved = resolveContentPathParts(pathname);
2771
- const routingPathname = resolved?.routingPathname || pathname;
2772
- await extractLayoutMetadata(routingPathname, clientDir, cwd, isWatchMode, (head) => {
2884
+ const routingPathname2 = routingPathname(pathname);
2885
+ await extractLayoutMetadata(routingPathname2, pathname, clientDir, cwd, isWatchMode, (head) => {
2773
2886
  perPageHead += head;
2774
2887
  }, (lang) => {
2775
2888
  if (!pageLang)
@@ -2805,11 +2918,14 @@ ${ogMetaTags}`;
2805
2918
  }, (lang) => {
2806
2919
  pageLang = lang;
2807
2920
  }, ogConfig, titleSuffix, baseUrl);
2808
- const routeResult = await extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix, routingPathname);
2921
+ const routeResult = await extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix, routingPathname2);
2809
2922
  perPageHead += routeResult.head;
2810
2923
  if (routeResult.lang)
2811
2924
  pageLang = routeResult.lang;
2812
2925
  statusOverride = routeResult.statusOverride;
2926
+ if (baseUrl && !(statusOverride && statusOverride >= 400)) {
2927
+ perPageHead += pageI18nHead(baseUrl, pathname);
2928
+ }
2813
2929
  return {
2814
2930
  perPageHead,
2815
2931
  pageLang,
@@ -2817,7 +2933,7 @@ ${ogMetaTags}`;
2817
2933
  ssrExport: routeResult.ssrExport
2818
2934
  };
2819
2935
  }
2820
- async function processLayoutForMetadata(layout, clientDir, isWatchMode, addHead, setLang, ogConfig, titleSuffix) {
2936
+ async function processLayoutForMetadata(layout, requestPathname, clientDir, isWatchMode, addHead, setLang, ogConfig, titleSuffix) {
2821
2937
  if (typeof layout?.path !== "string")
2822
2938
  return;
2823
2939
  try {
@@ -2826,7 +2942,7 @@ async function processLayoutForMetadata(layout, clientDir, isWatchMode, addHead,
2826
2942
  if (meta)
2827
2943
  addHead(buildHeadFromMeta(meta, ogConfig, titleSuffix));
2828
2944
  if (ssr) {
2829
- const ssrHead = extractSSRHead(ssr, undefined, ogConfig, undefined, meta, titleSuffix);
2945
+ const ssrHead = extractSSRHead(ssr, requestPathname, undefined, ogConfig, undefined, meta, titleSuffix);
2830
2946
  if (ssrHead)
2831
2947
  addHead(`
2832
2948
  ${ssrHead}`);
@@ -2836,15 +2952,36 @@ ${ssrHead}`);
2836
2952
  }
2837
2953
  } catch {}
2838
2954
  }
2839
- async function extractLayoutMetadata(pathname, clientDir, cwd, isWatchMode, addHead, setLang, ogConfig, titleSuffix) {
2955
+ async function extractLayoutMetadata(pathname, requestPathname, clientDir, cwd, isWatchMode, addHead, setLang, ogConfig, titleSuffix) {
2840
2956
  try {
2841
2957
  const m = await loadRoutesModule(cwd, isWatchMode);
2842
2958
  const matchingLayouts = getMatchingLayouts(pathname, m?.layouts, m?.routes);
2843
2959
  for (const layout of matchingLayouts) {
2844
- await processLayoutForMetadata(layout, clientDir, isWatchMode, addHead, setLang, ogConfig, titleSuffix);
2960
+ await processLayoutForMetadata(layout, requestPathname, clientDir, isWatchMode, addHead, setLang, ogConfig, titleSuffix);
2845
2961
  }
2846
2962
  } catch {}
2847
2963
  }
2964
+ function readI18n() {
2965
+ return globalThis.__REROUTE_I18N__;
2966
+ }
2967
+ function isContentUrl(i18n, collection) {
2968
+ return !i18n.prefixPages || !!i18n.collections?.includes(collection);
2969
+ }
2970
+ function pageI18nHead(baseUrl, pathname) {
2971
+ const i18n = readI18n();
2972
+ if (!(i18n?.enabled && i18n.prefixPages && i18n.locales))
2973
+ return "";
2974
+ if (i18n.locales.length < 2)
2975
+ return "";
2976
+ const resolved = resolveContentPathParts(pathname);
2977
+ if (resolved && isContentUrl(i18n, resolved.collection))
2978
+ return "";
2979
+ const page = pagePathname(pathname);
2980
+ if (isUnprefixedPath(page, i18n.unprefixedPaths))
2981
+ return "";
2982
+ return `
2983
+ ${pageHreflangTags(baseUrl, page, i18n.locales, i18n.defaultLocale || "en-US")}`;
2984
+ }
2848
2985
  function resolveContentLocales(collection, name, fallbackLocales) {
2849
2986
  const g = globalThis;
2850
2987
  const byCollection = g.__REROUTE_COLLECTIONS__;
@@ -2884,15 +3021,15 @@ function extractContentMetadata(pathname, addHead, setLang, ogConfig, titleSuffi
2884
3021
  const meta = exp?.meta;
2885
3022
  const ssr = exp?.ssr;
2886
3023
  addHead(buildHeadFromMeta(meta, ogConfig, titleSuffix));
2887
- const ssrHead = extractSSRHead(ssr, undefined, ogConfig, undefined, meta, titleSuffix);
3024
+ const ssrHead = extractSSRHead(ssr, pathname, undefined, ogConfig, undefined, meta, titleSuffix);
2888
3025
  if (ssrHead)
2889
3026
  addHead(`
2890
3027
  ${ssrHead}`);
2891
3028
  const lang = extractSSRLang(ssr);
2892
3029
  if (lang)
2893
3030
  setLang(lang);
2894
- const i18n = g.__REROUTE_I18N__;
2895
- if (i18n?.enabled && baseUrl && i18n.locales && i18n.locales.length > 1) {
3031
+ const i18n = readI18n();
3032
+ if (i18n?.enabled && baseUrl && i18n.locales && i18n.locales.length > 1 && isContentUrl(i18n, resolved.collection)) {
2896
3033
  const locales = resolveContentLocales(resolved.collection, resolved.name, i18n.locales);
2897
3034
  addContentI18nHead(addHead, baseUrl, resolved.collection, resolved.name, locales, i18n.defaultLocale || "en-US", i18n.locale || i18n.defaultLocale || "en-US");
2898
3035
  }
@@ -2918,9 +3055,9 @@ function getContentMetaFromExports(pathname) {
2918
3055
  return;
2919
3056
  }
2920
3057
  }
2921
- async function extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix, routingPathname) {
3058
+ async function extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix, routingPathname2) {
2922
3059
  const pathnameOnly = pathname.split("?")[0];
2923
- const matchPath = (routingPathname || pathname).split("?")[0];
3060
+ const matchPath = (routingPathname2 || pathname).split("?")[0];
2924
3061
  const match = typeof m.matchRoute === "function" ? m.matchRoute(matchPath) : null;
2925
3062
  const r = match?.route;
2926
3063
  if (!r) {
@@ -2945,7 +3082,7 @@ async function extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode,
2945
3082
  }
2946
3083
  const contentMeta = getContentMetaFromExports(pathnameOnly);
2947
3084
  const headMeta = contentMeta || meta;
2948
- const ssrHead = extractSSRHead(ssr, routeData, ogConfig, head, headMeta, titleSuffix);
3085
+ const ssrHead = extractSSRHead(ssr, pathname, routeData, ogConfig, head, headMeta, titleSuffix);
2949
3086
  if (ssrHead)
2950
3087
  head += `
2951
3088
  ${ssrHead}`;
@@ -2961,7 +3098,7 @@ ${ssrHead}`;
2961
3098
  }
2962
3099
  return { head: "", statusOverride };
2963
3100
  }
2964
- async function extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, currentStatusOverride, ssrData, ogConfig, titleSuffix, routingPathname) {
3101
+ async function extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, currentStatusOverride, ssrData, ogConfig, titleSuffix, routingPathname2) {
2965
3102
  let head = "";
2966
3103
  let lang;
2967
3104
  let statusOverride = currentStatusOverride;
@@ -2971,14 +3108,14 @@ async function extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, curre
2971
3108
  if (!m) {
2972
3109
  return { head, lang, statusOverride: statusOverride || 404 };
2973
3110
  }
2974
- const matchPath = (routingPathname || pathname).split("?")[0];
3111
+ const matchPath = (routingPathname2 || pathname).split("?")[0];
2975
3112
  const match = typeof m.matchRoute === "function" ? m.matchRoute(matchPath) : null;
2976
3113
  const r = match?.route;
2977
3114
  if (!r) {
2978
3115
  statusOverride = statusOverride || 404;
2979
3116
  }
2980
3117
  if (r && typeof r.path === "string") {
2981
- const result = await extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix, routingPathname);
3118
+ const result = await extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig, titleSuffix, routingPathname2);
2982
3119
  head = result.head;
2983
3120
  lang = result.lang;
2984
3121
  statusOverride = result.statusOverride;
@@ -3018,7 +3155,7 @@ async function extractNotFoundMetadata(pathname, clientDir, routesModule, isWatc
3018
3155
  const { meta, ssr } = await loadModuleMetaAndSSR(chosen, abs, isWatchMode);
3019
3156
  if (meta)
3020
3157
  head += buildHeadFromMeta(meta, ogConfig, titleSuffix);
3021
- const ssrHead = extractSSRHead(ssr, undefined, ogConfig, undefined, meta, titleSuffix);
3158
+ const ssrHead = extractSSRHead(ssr, pathname, undefined, ogConfig, undefined, meta, titleSuffix);
3022
3159
  if (ssrHead)
3023
3160
  head += `
3024
3161
  ${ssrHead}`;
@@ -3081,13 +3218,17 @@ function generateMissingOGTags(head, ogConfig) {
3081
3218
  ${additionalTags.join(`
3082
3219
  `)}` : "";
3083
3220
  }
3084
- function extractSSRHead(ssr, data, ogConfig, existingHead, meta, titleSuffix) {
3221
+ function extractSSRHead(ssr, pathname, data, ogConfig, existingHead, meta, titleSuffix) {
3085
3222
  if (!ssr)
3086
3223
  return "";
3087
3224
  let headContent = "";
3088
3225
  if (typeof ssr.head === "function") {
3089
3226
  try {
3090
- const result = ssr.head({ data, meta });
3227
+ const result = ssr.head({
3228
+ pathname,
3229
+ data,
3230
+ meta
3231
+ });
3091
3232
  if (typeof result === "string")
3092
3233
  headContent = result;
3093
3234
  else if (Array.isArray(result))
@@ -6219,18 +6360,43 @@ function createStaticEntry(route, baseUrl, defaultChangefreq, defaultPriority) {
6219
6360
  priority: route.priority !== undefined ? route.priority : loc === "/" ? 1 : defaultPriority
6220
6361
  };
6221
6362
  }
6222
- function collectAllEntries(discovery4, baseUrl, changefreq, priority, excludeRoutes) {
6363
+ function localizePageEntry(entry, baseUrl, i18n) {
6364
+ const base = baseUrl.replace(/\/$/, "");
6365
+ if (!(i18n && entry.loc.startsWith(base)))
6366
+ return [entry];
6367
+ const path2 = entry.loc.slice(base.length) || "/";
6368
+ if (parseContentPath(path2, i18n.locales, i18n.collections))
6369
+ return [entry];
6370
+ if (isUnprefixedPath(path2, i18n.unprefixedPaths))
6371
+ return [entry];
6372
+ const { locales, defaultLocale } = i18n;
6373
+ const urlFor = (locale) => `${base}${localizePagePath(path2, locale, defaultLocale)}`;
6374
+ const alternates = [
6375
+ ...locales.map((locale) => ({ hreflang: locale, href: urlFor(locale) })),
6376
+ { hreflang: "x-default", href: urlFor(defaultLocale) }
6377
+ ];
6378
+ return locales.map((locale) => ({
6379
+ ...entry,
6380
+ loc: urlFor(locale),
6381
+ alternates
6382
+ }));
6383
+ }
6384
+ function collectAllEntries(discovery4, baseUrl, changefreq, priority, excludeRoutes, i18n) {
6223
6385
  const allEntries = [];
6386
+ const addPage = (entry) => {
6387
+ allEntries.push(...localizePageEntry(entry, baseUrl, i18n));
6388
+ };
6224
6389
  for (const route of discovery4.staticRoutes) {
6225
6390
  if (shouldIncludeRoute(route.pattern, excludeRoutes)) {
6226
- allEntries.push(createStaticEntry(route, baseUrl, changefreq, priority));
6391
+ addPage(createStaticEntry(route, baseUrl, changefreq, priority));
6227
6392
  }
6228
6393
  }
6229
6394
  for (const [_collection, entries] of discovery4.collections.entries()) {
6230
6395
  allEntries.push(...entries);
6231
6396
  }
6232
6397
  for (const [_pattern, entries] of discovery4.ssrDataRoutes.entries()) {
6233
- allEntries.push(...entries);
6398
+ for (const entry of entries)
6399
+ addPage(entry);
6234
6400
  }
6235
6401
  return allEntries;
6236
6402
  }
@@ -6276,7 +6442,7 @@ async function generateSitemap(discovery4, baseUrl, options = {}) {
6276
6442
  priority = 0.5,
6277
6443
  maxUrlsPerPage = DEFAULT_MAX_URLS
6278
6444
  } = options;
6279
- const allEntries = collectAllEntries(discovery4, baseUrl, changefreq, priority, options.excludeRoutes);
6445
+ const allEntries = collectAllEntries(discovery4, baseUrl, changefreq, priority, options.excludeRoutes, options.i18n);
6280
6446
  sortEntries(allEntries);
6281
6447
  const needsIndex = allEntries.length > maxUrlsPerPage;
6282
6448
  if (needsIndex) {
@@ -6336,7 +6502,9 @@ function escapeXml2(unsafe) {
6336
6502
  return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
6337
6503
  }
6338
6504
  var DEFAULT_MAX_URLS = 50000;
6339
- var init_generator2 = () => {};
6505
+ var init_generator2 = __esm(() => {
6506
+ init_pathname();
6507
+ });
6340
6508
 
6341
6509
  // packages/core/src/sitemap/index.ts
6342
6510
  var init_sitemap = __esm(() => {
@@ -6606,7 +6774,10 @@ function generateMetaUpdateScript(ctx, ssrDataSnapshot) {
6606
6774
  }
6607
6775
  let headResult;
6608
6776
  try {
6609
- headResult = ctx.ssrExport.head({ data: routeData });
6777
+ headResult = ctx.ssrExport.head({
6778
+ pathname: ctx.pathname,
6779
+ data: routeData
6780
+ });
6610
6781
  } catch (err) {
6611
6782
  console.error("[reroute] Error calling ssr.head() at stream end:", err);
6612
6783
  return "";
@@ -6826,6 +6997,7 @@ __export(exports_core, {
6826
6997
  inlineThemeCSS: () => inlineThemeCSS,
6827
6998
  isCompressible: () => isCompressible,
6828
6999
  isThenable: () => isThenable,
7000
+ isUnprefixedPath: () => isUnprefixedPath,
6829
7001
  join: () => join2,
6830
7002
  layoutCovers: () => layoutCovers,
6831
7003
  layoutWraps: () => layoutWraps,
@@ -6838,6 +7010,7 @@ __export(exports_core, {
6838
7010
  loadRoutesModule: () => loadRoutesModule,
6839
7011
  localizeContentHref: () => localizeContentHref,
6840
7012
  localizeHref: () => localizeHref,
7013
+ localizePagePath: () => localizePagePath,
6841
7014
  looksLikeLocale: () => looksLikeLocale,
6842
7015
  lookupMessage: () => lookupMessage,
6843
7016
  markPageHead: () => markPageHead,
@@ -6848,8 +7021,10 @@ __export(exports_core, {
6848
7021
  mergeVary: () => mergeVary,
6849
7022
  negotiateLocale: () => negotiateLocale,
6850
7023
  normalizePathname: () => normalizePathname,
7024
+ pageHreflangTags: () => pageHreflangTags,
6851
7025
  parseContentPath: () => parseContentPath,
6852
7026
  parseCookieValue: () => parseCookieValue,
7027
+ parseLocalePrefix: () => parseLocalePrefix,
6853
7028
  performSSRSetup: () => performSSRSetup,
6854
7029
  preloadContentModule: () => preloadContentModule,
6855
7030
  processCollections: () => processCollections,
@@ -7761,21 +7936,24 @@ function jsonError(message, status = 500) {
7761
7936
  init_core();
7762
7937
  import { join as join7 } from "node:path";
7763
7938
  var cachedI18n = null;
7939
+ var NO_I18N = {
7940
+ enabled: false,
7941
+ defaultLocale: "en-US",
7942
+ locales: [],
7943
+ localesDir: "locales",
7944
+ cookie: "reroute-locale",
7945
+ redirectContentOnNegotiate: true,
7946
+ prefixPages: false,
7947
+ unprefixedPaths: [],
7948
+ localeParam: "",
7949
+ catalogs: {}
7950
+ };
7764
7951
  async function loadI18nState(cwd) {
7765
7952
  const path2 = join7(cwd, ".reroute", "i18n.json");
7766
7953
  try {
7767
7954
  const file = Bun.file(path2);
7768
- if (!await file.exists()) {
7769
- return {
7770
- enabled: false,
7771
- defaultLocale: "en-US",
7772
- locales: [],
7773
- localesDir: "locales",
7774
- cookie: "reroute-locale",
7775
- redirectContentOnNegotiate: true,
7776
- catalogs: {}
7777
- };
7778
- }
7955
+ if (!await file.exists())
7956
+ return NO_I18N;
7779
7957
  const stat6 = await file.stat();
7780
7958
  const mtime = stat6?.mtime?.getTime?.() ?? Date.now();
7781
7959
  if (cachedI18n && cachedI18n.cwd === cwd && cachedI18n.mtime === mtime) {
@@ -7785,15 +7963,7 @@ async function loadI18nState(cwd) {
7785
7963
  cachedI18n = { cwd, data, mtime };
7786
7964
  return data;
7787
7965
  } catch {
7788
- return {
7789
- enabled: false,
7790
- defaultLocale: "en-US",
7791
- locales: [],
7792
- localesDir: "locales",
7793
- cookie: "reroute-locale",
7794
- redirectContentOnNegotiate: true,
7795
- catalogs: {}
7796
- };
7966
+ return NO_I18N;
7797
7967
  }
7798
7968
  }
7799
7969
  async function listContentCollections(cwd) {
@@ -7829,13 +7999,16 @@ async function resolveI18nForRequest(cwd, pathname, headers2, collections2, sear
7829
7999
  availableLocales: i18n2.locales,
7830
8000
  defaultLocale: i18n2.defaultLocale,
7831
8001
  cookieName: i18n2.cookie,
7832
- collections: knownCollections.length > 0 ? knownCollections : undefined
8002
+ collections: i18n2.prefixPages || knownCollections.length > 0 ? knownCollections : undefined,
8003
+ prefixPages: i18n2.prefixPages,
8004
+ unprefixedPaths: i18n2.unprefixedPaths,
8005
+ localeParam: i18n2.localeParam
7833
8006
  });
7834
8007
  let redirectTo = resolution.redirectTo;
7835
- if (!redirectTo && i18n2.redirectContentOnNegotiate && knownCollections.length > 0) {
8008
+ if (!(redirectTo || i18n2.prefixPages || i18n2.localeParam) && i18n2.redirectContentOnNegotiate && knownCollections.length > 0) {
7836
8009
  redirectTo = contentNegotiateRedirect(resolution, i18n2.defaultLocale, true);
7837
8010
  }
7838
- const cookieValue = buildLocaleCookie(i18n2.cookie, resolution.locale);
8011
+ const cookieValue = !resolution.urlOnly || resolution.fromQuery ? buildLocaleCookie(i18n2.cookie, resolution.locale) : undefined;
7839
8012
  const hydration = {
7840
8013
  enabled: true,
7841
8014
  locale: resolution.locale,
@@ -7843,7 +8016,11 @@ async function resolveI18nForRequest(cwd, pathname, headers2, collections2, sear
7843
8016
  locales: i18n2.locales,
7844
8017
  cookie: i18n2.cookie,
7845
8018
  catalogs: i18n2.catalogs,
7846
- collections: knownCollections
8019
+ collections: knownCollections,
8020
+ ...i18n2.prefixPages && { prefixPages: true },
8021
+ ...i18n2.prefixPages && i18n2.unprefixedPaths?.length && {
8022
+ unprefixedPaths: i18n2.unprefixedPaths
8023
+ }
7847
8024
  };
7848
8025
  try {
7849
8026
  globalThis.__REROUTE_I18N__ = hydration;
@@ -7858,10 +8035,89 @@ async function resolveI18nForRequest(cwd, pathname, headers2, collections2, sear
7858
8035
  collections: knownCollections,
7859
8036
  setCookie: cookieValue,
7860
8037
  redirectTo,
8038
+ permanent: resolution.permanent,
7861
8039
  hydration
7862
8040
  };
7863
8041
  }
7864
8042
 
8043
+ // packages/elysia/src/libs/lazyData.ts
8044
+ import { createHash as createHash2 } from "node:crypto";
8045
+ var ignoredHeaders = new Set([
8046
+ "if-none-match",
8047
+ "if-modified-since",
8048
+ "cache-control",
8049
+ "pragma",
8050
+ "cf-ray",
8051
+ "cdn-loop",
8052
+ "x-request-id",
8053
+ "x-amzn-trace-id",
8054
+ "x-cloud-trace-context",
8055
+ "traceparent",
8056
+ "tracestate",
8057
+ "sentry-trace",
8058
+ "baggage"
8059
+ ]);
8060
+ function lazyDataRequest(headers2) {
8061
+ const lines = Object.entries(headers2 ?? {}).map(([name, value]) => [name.toLowerCase(), value ?? ""]).filter(([name]) => !ignoredHeaders.has(name)).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([name, value]) => `${name}:${value}`);
8062
+ return createHash2("sha256").update(lines.join(`
8063
+ `)).digest("hex");
8064
+ }
8065
+ function createLazyDataStore(options = {}) {
8066
+ const ttlMs = options.ttlMs ?? 30000;
8067
+ const maxEntries = options.maxEntries ?? 1000;
8068
+ const now = options.now ?? Date.now;
8069
+ const entries = new Map;
8070
+ const sweep = () => {
8071
+ const at = now();
8072
+ for (const [id, entry] of entries) {
8073
+ if (entry.expires <= at)
8074
+ entries.delete(id);
8075
+ }
8076
+ while (entries.size > maxEntries) {
8077
+ const oldest = entries.keys().next().value;
8078
+ if (oldest === undefined)
8079
+ break;
8080
+ entries.delete(oldest);
8081
+ }
8082
+ };
8083
+ return {
8084
+ keep(request, pathname, data) {
8085
+ const promises = new Map;
8086
+ for (const [key, value] of Object.entries(data)) {
8087
+ if (value instanceof Promise) {
8088
+ value.catch(() => {});
8089
+ promises.set(key, value);
8090
+ }
8091
+ }
8092
+ if (promises.size === 0)
8093
+ return;
8094
+ const id = `${request}
8095
+ ${pathname}`;
8096
+ entries.delete(id);
8097
+ entries.set(id, { promises, expires: now() + ttlMs });
8098
+ sweep();
8099
+ },
8100
+ take(request, pathname, key) {
8101
+ const id = `${request}
8102
+ ${pathname}`;
8103
+ const entry = entries.get(id);
8104
+ if (!entry)
8105
+ return;
8106
+ if (entry.expires <= now()) {
8107
+ entries.delete(id);
8108
+ return;
8109
+ }
8110
+ const promise = entry.promises.get(key);
8111
+ if (!promise)
8112
+ return;
8113
+ entry.promises.delete(key);
8114
+ if (entry.promises.size === 0)
8115
+ entries.delete(id);
8116
+ return promise;
8117
+ }
8118
+ };
8119
+ }
8120
+
7865
8121
  // packages/elysia/src/routes/liveReloadClient.ts
7866
8122
  function parseConnectedBootId(data) {
7867
8123
  if (data === "connected")
@@ -7898,6 +8154,13 @@ function refreshThemeCss() {
7898
8154
  function isPersonal(headers2) {
7899
8155
  return Boolean(headers2?.cookie || headers2?.authorization);
7900
8156
  }
8157
+ function withLocaleParam(search, query, localeParam) {
8158
+ const named = localeParam ? query[localeParam] : undefined;
8159
+ if (!named)
8160
+ return search;
8161
+ const param = `${localeParam}=${encodeURIComponent(String(named))}`;
8162
+ return search ? `${search}&${param}` : `?${param}`;
8163
+ }
7901
8164
  function parsePathname(p) {
7902
8165
  try {
7903
8166
  const u = new URL(p, "http://localhost");
@@ -7923,6 +8186,7 @@ function registerInternalRoutes(app, opts) {
7923
8186
  maxAge = 0,
7924
8187
  pageHead
7925
8188
  } = opts;
8189
+ const lazyData = createLazyDataStore();
7926
8190
  app.get("/__reroute_preload", async ({ query }) => {
7927
8191
  const rawSrc = String(query.src || "").trim();
7928
8192
  const key = String(query.key || "").trim();
@@ -7952,10 +8216,16 @@ function registerInternalRoutes(app, opts) {
7952
8216
  }
7953
8217
  });
7954
8218
  });
7955
- async function handleKeyRequest(pathname, dataKey, headers2) {
8219
+ async function handleKeyRequest(pathname, dataKey, headers2, i18n2) {
8220
+ const kept = lazyData.take(lazyDataRequest(headers2), pathname, dataKey);
8221
+ if (kept) {
8222
+ try {
8223
+ return jsonWithCache(await withHead(pathname, dataKey, await kept, i18n2), dataCacheControl || "no-cache", headers2, "HIT");
8224
+ } catch {}
8225
+ }
7956
8226
  const { computeSSRData: computeSSRData2 } = await Promise.resolve().then(() => (init_core(), exports_core));
7957
8227
  const only = dataKey.startsWith("__layout:") ? "layouts" : "page";
7958
- const cacheKey = `${pathname}:promises:${only}`;
8228
+ const cacheKey = `${pathname}:${i18n2.locale}:promises:${only}`;
7959
8229
  const cacheable = !isWatchMode && maxAge > 0 && !isPersonal(headers2);
7960
8230
  let promiseData = cacheable ? dataCache?.get(cacheKey) : undefined;
7961
8231
  const promiseDataCached = promiseData !== undefined;
@@ -7971,21 +8241,21 @@ function registerInternalRoutes(app, opts) {
7971
8241
  const value = promiseData[dataKey];
7972
8242
  if (value instanceof Promise) {
7973
8243
  const resolved = await value;
7974
- const resolvedCacheKey = `${pathname}:${dataKey}:resolved`;
8244
+ const resolvedCacheKey = `${pathname}:${i18n2.locale}:${dataKey}:resolved`;
7975
8245
  if (cacheable) {
7976
8246
  dataCache?.set(resolvedCacheKey, resolved);
7977
8247
  }
7978
- return jsonWithCache(await withHead(pathname, dataKey, resolved, headers2), dataCacheControl || "no-cache", headers2, promiseDataCached ? "HIT" : "MISS");
8248
+ return jsonWithCache(await withHead(pathname, dataKey, resolved, i18n2), dataCacheControl || "no-cache", headers2, promiseDataCached ? "HIT" : "MISS");
7979
8249
  }
7980
8250
  if (value !== undefined) {
7981
- return jsonWithCache(await withHead(pathname, dataKey, value, headers2), dataCacheControl || "no-cache", headers2, promiseDataCached ? "HIT" : "MISS");
8251
+ return jsonWithCache(await withHead(pathname, dataKey, value, i18n2), dataCacheControl || "no-cache", headers2, promiseDataCached ? "HIT" : "MISS");
7982
8252
  }
7983
8253
  }
7984
8254
  return jsonError("key not found", 404);
7985
8255
  }
7986
- async function handleFullDataRequest(pathname, headers2) {
8256
+ async function handleFullDataRequest(pathname, headers2, locale) {
7987
8257
  const { computeSSRData: computeSSRData2 } = await Promise.resolve().then(() => (init_core(), exports_core));
7988
- const cacheKey = `${pathname}:streaming`;
8258
+ const cacheKey = `${pathname}:${locale}:streaming`;
7989
8259
  const cacheable = !isWatchMode && maxAge > 0 && !isPersonal(headers2);
7990
8260
  let data = cacheable ? dataCache?.get(cacheKey) : undefined;
7991
8261
  const cacheHit = data !== undefined;
@@ -7993,6 +8263,7 @@ function registerInternalRoutes(app, opts) {
7993
8263
  const sp = extractSearchParams(pathname);
7994
8264
  try {
7995
8265
  const result = await computeSSRData2(pathname, clientDir, cwd, isWatchMode, { streaming: true, maxAge, headers: headers2 }, sp);
8266
+ lazyData.keep(lazyDataRequest(headers2), pathname, result.data);
7996
8267
  data = convertPromisesToLazyMarkers(result.data, pathname);
7997
8268
  } catch (error) {
7998
8269
  console.error("[__reroute_data] Error computing data:", error);
@@ -8004,17 +8275,16 @@ function registerInternalRoutes(app, opts) {
8004
8275
  }
8005
8276
  return { data, cacheHit };
8006
8277
  }
8007
- async function withHead(pathname, dataKey, data, headers2) {
8278
+ async function withHead(pathname, dataKey, data, i18n2) {
8008
8279
  if (dataKey !== pathname)
8009
8280
  return { data };
8010
- return { data, head: await navigationHead(pathname, headers2, { data }) };
8281
+ return { data, head: await navigationHead(pathname, i18n2, { data }) };
8011
8282
  }
8012
- async function navigationHead(pathname, headers2, routeData) {
8283
+ async function navigationHead(pathname, i18n2, routeData) {
8013
8284
  const { deduplicateMetaTags: deduplicateMetaTags2, extractPageMetadata: extractPageMetadata2, seedSSRModuleForPath: seedSSRModuleForPath2 } = await Promise.resolve().then(() => (init_core(), exports_core));
8014
8285
  const url = new URL(pathname, "http://localhost");
8015
8286
  await seedSSRModuleForPath2(url.pathname, clientDir, cwd, isWatchMode);
8016
8287
  const { perPageHead, pageLang } = await extractPageMetadata2(pathname, clientDir, cwd, isWatchMode, undefined, pageHead?.ogConfig, pageHead?.ogMetaConfig, pageHead?.baseUrl, pageHead?.autoCanonical, { [pathname]: routeData.data }, pageHead?.titleSuffix);
8017
- const i18n2 = await resolveI18nForRequest(cwd, url.pathname, headers2, undefined, url.search);
8018
8288
  return {
8019
8289
  path: pathname,
8020
8290
  html: deduplicateMetaTags2(perPageHead),
@@ -8060,21 +8330,24 @@ function registerInternalRoutes(app, opts) {
8060
8330
  const pathname = parsePathname(p);
8061
8331
  const requestHeaders = headers2;
8062
8332
  try {
8333
+ const url = new URL(pathname, "http://localhost");
8334
+ const { localeParam } = await loadI18nState(cwd);
8335
+ const i18n2 = await resolveI18nForRequest(cwd, url.pathname, requestHeaders, undefined, withLocaleParam(url.search, query, localeParam));
8063
8336
  if (query.head !== undefined) {
8064
8337
  return jsonWithCache({
8065
- head: await navigationHead(pathname, requestHeaders, {
8338
+ head: await navigationHead(pathname, i18n2, {
8066
8339
  data: undefined
8067
8340
  })
8068
8341
  }, dataCacheControl || "no-cache", requestHeaders, "MISS");
8069
8342
  }
8070
8343
  if (dataKey) {
8071
- return await handleKeyRequest(pathname, dataKey, headers2);
8344
+ return await handleKeyRequest(pathname, dataKey, headers2, i18n2);
8072
8345
  }
8073
- const { data, cacheHit } = await handleFullDataRequest(pathname, headers2);
8346
+ const { data, cacheHit } = await handleFullDataRequest(pathname, headers2, i18n2.locale);
8074
8347
  const routeData = readyRouteData(data, pathname);
8075
8348
  return jsonWithCache(routeData ? {
8076
8349
  data,
8077
- head: await navigationHead(pathname, requestHeaders, routeData)
8350
+ head: await navigationHead(pathname, i18n2, routeData)
8078
8351
  } : { data }, dataCacheControl || "no-cache", headers2, cacheHit ? "HIT" : "MISS");
8079
8352
  } catch {
8080
8353
  return jsonError("failed", 500);
@@ -9652,11 +9925,18 @@ async function getOrDiscoverSitemapData(cwd, collections2, baseUrl, isWatchMode,
9652
9925
  async function generateAndCacheSitemap(options, _ttlMs) {
9653
9926
  const { cwd, collections: collections2, baseUrl, isWatchMode, sitemapConfig } = options;
9654
9927
  const discovery5 = await getOrDiscoverSitemapData(cwd, collections2, baseUrl, isWatchMode, sitemapConfig, options.cacheEnabled);
9928
+ const i18n2 = await loadI18nState(cwd);
9655
9929
  const result = await generateSitemap(discovery5, baseUrl, {
9656
9930
  changefreq: sitemapConfig?.changefreq,
9657
9931
  priority: sitemapConfig?.priority,
9658
9932
  maxUrlsPerPage: sitemapConfig?.maxUrlsPerPage,
9659
- excludeRoutes: sitemapConfig?.excludeRoutes
9933
+ excludeRoutes: sitemapConfig?.excludeRoutes,
9934
+ i18n: i18n2.enabled && i18n2.prefixPages ? {
9935
+ locales: i18n2.locales,
9936
+ defaultLocale: i18n2.defaultLocale,
9937
+ collections: collections2,
9938
+ unprefixedPaths: i18n2.unprefixedPaths
9939
+ } : undefined
9660
9940
  });
9661
9941
  return {
9662
9942
  xml: result.needsIndex ? result.indexXml ?? "" : result.pages[0]?.xml ?? "",
@@ -9763,7 +10043,10 @@ async function applyI18nForSSR(cwd, pathname, headers2, set, fallbackLang, searc
9763
10043
  redirectHeaders["Set-Cookie"] = i18nResult.setCookie;
9764
10044
  }
9765
10045
  return {
9766
- redirect: new Response(null, { status: 302, headers: redirectHeaders }),
10046
+ redirect: new Response(null, {
10047
+ status: i18nResult.permanent ? 301 : 302,
10048
+ headers: redirectHeaders
10049
+ }),
9767
10050
  effectiveLang: fallbackLang
9768
10051
  };
9769
10052
  }
@@ -10775,4 +11058,4 @@ export {
10775
11058
  reroute
10776
11059
  };
10777
11060
 
10778
- //# debugId=FBEDF17D2EE64A0B64756E2164756E21
11061
+ //# debugId=CBD692301821766B64756E2164756E21