@inlang/paraglide-js 2.23.2 → 2.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/bundler-plugins/per-locale-build/vite-environments.js +12 -7
  2. package/dist/bundler-plugins/per-locale-build/vite-environments.js.map +1 -1
  3. package/dist/bundler-plugins/unplugin.js +134 -4
  4. package/dist/bundler-plugins/unplugin.js.map +1 -1
  5. package/dist/bundler-plugins/unplugin.test.js +77 -0
  6. package/dist/bundler-plugins/unplugin.test.js.map +1 -1
  7. package/dist/cli/commands/compile/command.js +8 -1
  8. package/dist/cli/commands/compile/command.js.map +1 -1
  9. package/dist/cli/commands/compile/command.test.js +45 -0
  10. package/dist/cli/commands/compile/command.test.js.map +1 -1
  11. package/dist/compiler/compile.js +0 -15
  12. package/dist/compiler/compile.js.map +1 -1
  13. package/dist/compiler/compile.test.js +0 -52
  14. package/dist/compiler/compile.test.js.map +1 -1
  15. package/dist/compiler/compiler-options.d.ts +8 -0
  16. package/dist/compiler/compiler-options.js.map +1 -1
  17. package/dist/compiler/runtime/create-runtime.d.ts +1 -0
  18. package/dist/compiler/runtime/create-runtime.js +11 -0
  19. package/dist/compiler/runtime/create-runtime.js.map +1 -1
  20. package/dist/compiler/runtime/exec-url-pattern.d.ts +9 -0
  21. package/dist/compiler/runtime/exec-url-pattern.js +31 -0
  22. package/dist/compiler/runtime/exec-url-pattern.js.map +1 -0
  23. package/dist/compiler/runtime/extract-locale-from-url.js +8 -3
  24. package/dist/compiler/runtime/extract-locale-from-url.js.map +1 -1
  25. package/dist/compiler/runtime/generate-static-localized-urls.js +12 -6
  26. package/dist/compiler/runtime/generate-static-localized-urls.js.map +1 -1
  27. package/dist/compiler/runtime/generate-static-localized-urls.test.js +46 -0
  28. package/dist/compiler/runtime/generate-static-localized-urls.test.js.map +1 -1
  29. package/dist/compiler/runtime/localize-href.bench.d.ts +1 -0
  30. package/dist/compiler/runtime/localize-href.bench.js +77 -0
  31. package/dist/compiler/runtime/localize-href.bench.js.map +1 -0
  32. package/dist/compiler/runtime/localize-href.test.js +43 -0
  33. package/dist/compiler/runtime/localize-href.test.js.map +1 -1
  34. package/dist/compiler/runtime/localize-url.d.ts +14 -0
  35. package/dist/compiler/runtime/localize-url.js +376 -18
  36. package/dist/compiler/runtime/localize-url.js.map +1 -1
  37. package/dist/compiler/runtime/localize-url.test.js +163 -0
  38. package/dist/compiler/runtime/localize-url.test.js.map +1 -1
  39. package/dist/compiler/runtime/normalize-trailing-slash.d.ts +10 -0
  40. package/dist/compiler/runtime/normalize-trailing-slash.js +23 -0
  41. package/dist/compiler/runtime/normalize-trailing-slash.js.map +1 -0
  42. package/dist/compiler/runtime/route-strategy.js +4 -2
  43. package/dist/compiler/runtime/route-strategy.js.map +1 -1
  44. package/dist/compiler/runtime/should-redirect.js +4 -1
  45. package/dist/compiler/runtime/should-redirect.js.map +1 -1
  46. package/dist/compiler/runtime/should-redirect.test.js +30 -2
  47. package/dist/compiler/runtime/should-redirect.test.js.map +1 -1
  48. package/dist/compiler/runtime/type.d.ts +1 -0
  49. package/dist/compiler/runtime/type.js.map +1 -1
  50. package/dist/compiler/runtime/variables.d.ts +6 -0
  51. package/dist/compiler/runtime/variables.js +6 -0
  52. package/dist/compiler/runtime/variables.js.map +1 -1
  53. package/dist/compiler/server/middleware.js +6 -2
  54. package/dist/compiler/server/middleware.js.map +1 -1
  55. package/dist/compiler/server/middleware.test.js +34 -0
  56. package/dist/compiler/server/middleware.test.js.map +1 -1
  57. package/dist/index.test.d.ts +1 -0
  58. package/dist/index.test.js +9 -0
  59. package/dist/index.test.js.map +1 -0
  60. package/dist/services/env-variables/index.js +1 -1
  61. package/dist/services/env-variables/index.js.map +1 -1
  62. package/dist/services/file-handling/write-output.bench.d.ts +1 -0
  63. package/dist/services/file-handling/write-output.bench.js +24 -0
  64. package/dist/services/file-handling/write-output.bench.js.map +1 -0
  65. package/dist/services/file-handling/write-output.js +16 -20
  66. package/dist/services/file-handling/write-output.js.map +1 -1
  67. package/dist/services/file-handling/write-output.test.js +27 -0
  68. package/dist/services/file-handling/write-output.test.js.map +1 -1
  69. package/package.json +2 -2
  70. package/dist/services/account/index.d.ts +0 -14
  71. package/dist/services/account/index.js +0 -43
  72. package/dist/services/account/index.js.map +0 -1
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Applies the configured trailing slash policy to a URL.
3
+ *
4
+ * The root pathname always remains `/`. Query parameters and hashes are not
5
+ * modified.
6
+ *
7
+ * @param {URL} url
8
+ * @returns {URL}
9
+ */
10
+ export declare function normalizeTrailingSlash(url: URL): URL;
@@ -0,0 +1,23 @@
1
+ import { trailingSlash } from "./variables.js";
2
+ /**
3
+ * Applies the configured trailing slash policy to a URL.
4
+ *
5
+ * The root pathname always remains `/`. Query parameters and hashes are not
6
+ * modified.
7
+ *
8
+ * @param {URL} url
9
+ * @returns {URL}
10
+ */
11
+ export function normalizeTrailingSlash(url) {
12
+ if (trailingSlash === undefined || url.pathname === "/") {
13
+ return url;
14
+ }
15
+ if (trailingSlash === "never") {
16
+ url.pathname = url.pathname.replace(/\/+$/, "") || "/";
17
+ }
18
+ else if (trailingSlash === "always") {
19
+ url.pathname = url.pathname.replace(/\/+$/, "") + "/";
20
+ }
21
+ return url;
22
+ }
23
+ //# sourceMappingURL=normalize-trailing-slash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize-trailing-slash.js","sourceRoot":"","sources":["../../../src/compiler/runtime/normalize-trailing-slash.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAG;IACzC,IAAI,aAAa,KAAK,SAAS,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;QACzD,OAAO,GAAG,CAAC;IACZ,CAAC;IAED,IAAI,aAAa,KAAK,OAAO,EAAE,CAAC;QAC/B,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;IACxD,CAAC;SAAM,IAAI,aAAa,KAAK,QAAQ,EAAE,CAAC;QACvC,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC;IACvD,CAAC;IAED,OAAO,GAAG,CAAC;AACZ,CAAC","sourcesContent":["import { trailingSlash } from \"./variables.js\";\n\n/**\n * Applies the configured trailing slash policy to a URL.\n *\n * The root pathname always remains `/`. Query parameters and hashes are not\n * modified.\n *\n * @param {URL} url\n * @returns {URL}\n */\nexport function normalizeTrailingSlash(url) {\n\tif (trailingSlash === undefined || url.pathname === \"/\") {\n\t\treturn url;\n\t}\n\n\tif (trailingSlash === \"never\") {\n\t\turl.pathname = url.pathname.replace(/\\/+$/, \"\") || \"/\";\n\t} else if (trailingSlash === \"always\") {\n\t\turl.pathname = url.pathname.replace(/\\/+$/, \"\") + \"/\";\n\t}\n\n\treturn url;\n}\n"]}
@@ -1,4 +1,6 @@
1
1
  import { deLocalizeUrl } from "./localize-url.js";
2
+ import { normalizeTrailingSlash } from "./normalize-trailing-slash.js";
3
+ import { execUrlPattern } from "./exec-url-pattern.js";
2
4
  import { routeStrategies, strategy } from "./variables.js";
3
5
  /** @type {string | undefined} */
4
6
  let cachedRouteStrategyUrl;
@@ -21,7 +23,7 @@ export function findMatchingRouteStrategy(url) {
21
23
  if (cachedRouteStrategyUrl === urlString) {
22
24
  return cachedRouteStrategy;
23
25
  }
24
- const publicUrl = new URL(urlString, "http://example.com");
26
+ const publicUrl = normalizeTrailingSlash(new URL(urlString, "http://example.com"));
25
27
  const canonicalUrl = deLocalizeUrl(publicUrl);
26
28
  const candidateUrls = canonicalUrl.href === publicUrl.href
27
29
  ? [publicUrl]
@@ -30,7 +32,7 @@ export function findMatchingRouteStrategy(url) {
30
32
  for (const candidateUrl of candidateUrls) {
31
33
  for (const routeStrategy of routeStrategies) {
32
34
  const pattern = new URLPattern(routeStrategy.match, candidateUrl.href);
33
- if (pattern.exec(candidateUrl.href)) {
35
+ if (execUrlPattern(pattern, candidateUrl)) {
34
36
  match = routeStrategy;
35
37
  break;
36
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"route-strategy.js","sourceRoot":"","sources":["../../../src/compiler/runtime/route-strategy.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE3D,iCAAiC;AACjC,IAAI,sBAAsB,CAAC;AAC3B,2FAA2F;AAC3F,IAAI,mBAAmB,CAAC;AAExB;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAG;IAC5C,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;IAC3D,IAAI,sBAAsB,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,mBAAmB,CAAC;IAC5B,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;IAC3D,MAAM,YAAY,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,aAAa,GAClB,YAAY,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI;QACnC,CAAC,CAAC,CAAC,SAAS,CAAC;QACb,CAAC,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC;IACV,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;QAC1C,KAAK,MAAM,aAAa,IAAI,eAAe,EAAE,CAAC;YAC7C,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;YACvE,IAAI,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrC,KAAK,GAAG,aAAa,CAAC;gBACtB,MAAM;YACP,CAAC;QACF,CAAC;QACD,IAAI,KAAK;YAAE,MAAM;IAClB,CAAC;IAED,sBAAsB,GAAG,SAAS,CAAC;IACnC,mBAAmB,GAAG,KAAK,CAAC;IAC5B,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAG;IACpC,MAAM,aAAa,GAAG,yBAAyB,CAAC,GAAG,CAAC,CAAC;IACrD,IACC,aAAa;QACb,aAAa,CAAC,OAAO,KAAK,IAAI;QAC9B,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,EACpC,CAAC;QACF,OAAO,aAAa,CAAC,QAAQ,CAAC;IAC/B,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAG;IAC5C,OAAO,yBAAyB,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AACzD,CAAC","sourcesContent":["import { deLocalizeUrl } from \"./localize-url.js\";\nimport { routeStrategies, strategy } from \"./variables.js\";\n\n/** @type {string | undefined} */\nlet cachedRouteStrategyUrl;\n/** @type {{ match: string; strategy?: typeof strategy; exclude?: boolean } | undefined} */\nlet cachedRouteStrategy;\n\n/**\n * Match route policy against both the public URL and its canonical URL.\n *\n * The function is deliberately separate from variables.js: configuration is\n * inert data, while canonicalization and route selection form a routing layer.\n *\n * @param {string | URL} url\n * @returns {{ match: string; strategy?: typeof strategy; exclude?: boolean } | undefined}\n */\nexport function findMatchingRouteStrategy(url) {\n\tif (routeStrategies.length === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst urlString = typeof url === \"string\" ? url : url.href;\n\tif (cachedRouteStrategyUrl === urlString) {\n\t\treturn cachedRouteStrategy;\n\t}\n\n\tconst publicUrl = new URL(urlString, \"http://example.com\");\n\tconst canonicalUrl = deLocalizeUrl(publicUrl);\n\tconst candidateUrls =\n\t\tcanonicalUrl.href === publicUrl.href\n\t\t\t? [publicUrl]\n\t\t\t: [publicUrl, canonicalUrl];\n\tlet match;\n\tfor (const candidateUrl of candidateUrls) {\n\t\tfor (const routeStrategy of routeStrategies) {\n\t\t\tconst pattern = new URLPattern(routeStrategy.match, candidateUrl.href);\n\t\t\tif (pattern.exec(candidateUrl.href)) {\n\t\t\t\tmatch = routeStrategy;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (match) break;\n\t}\n\n\tcachedRouteStrategyUrl = urlString;\n\tcachedRouteStrategy = match;\n\treturn match;\n}\n\n/**\n * Returns the strategy to use for a specific URL.\n *\n * If no route strategy matches (or the matching rule is `exclude: true`),\n * the global strategy is returned.\n *\n * @param {string | URL} url\n * @returns {typeof strategy}\n */\nexport function getStrategyForUrl(url) {\n\tconst routeStrategy = findMatchingRouteStrategy(url);\n\tif (\n\t\trouteStrategy &&\n\t\trouteStrategy.exclude !== true &&\n\t\tArray.isArray(routeStrategy.strategy)\n\t) {\n\t\treturn routeStrategy.strategy;\n\t}\n\treturn strategy;\n}\n\n/**\n * Returns whether the given URL is excluded from middleware i18n processing.\n *\n * @param {string | URL} url\n * @returns {boolean}\n */\nexport function isExcludedByRouteStrategy(url) {\n\treturn findMatchingRouteStrategy(url)?.exclude === true;\n}\n"]}
1
+ {"version":3,"file":"route-strategy.js","sourceRoot":"","sources":["../../../src/compiler/runtime/route-strategy.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE3D,iCAAiC;AACjC,IAAI,sBAAsB,CAAC;AAC3B,2FAA2F;AAC3F,IAAI,mBAAmB,CAAC;AAExB;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAG;IAC5C,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;IAC3D,IAAI,sBAAsB,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,mBAAmB,CAAC;IAC5B,CAAC;IAED,MAAM,SAAS,GAAG,sBAAsB,CACvC,IAAI,GAAG,CAAC,SAAS,EAAE,oBAAoB,CAAC,CACxC,CAAC;IACF,MAAM,YAAY,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,aAAa,GAClB,YAAY,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI;QACnC,CAAC,CAAC,CAAC,SAAS,CAAC;QACb,CAAC,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC;IACV,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;QAC1C,KAAK,MAAM,aAAa,IAAI,eAAe,EAAE,CAAC;YAC7C,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;YACvE,IAAI,cAAc,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,CAAC;gBAC3C,KAAK,GAAG,aAAa,CAAC;gBACtB,MAAM;YACP,CAAC;QACF,CAAC;QACD,IAAI,KAAK;YAAE,MAAM;IAClB,CAAC;IAED,sBAAsB,GAAG,SAAS,CAAC;IACnC,mBAAmB,GAAG,KAAK,CAAC;IAC5B,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAG;IACpC,MAAM,aAAa,GAAG,yBAAyB,CAAC,GAAG,CAAC,CAAC;IACrD,IACC,aAAa;QACb,aAAa,CAAC,OAAO,KAAK,IAAI;QAC9B,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,EACpC,CAAC;QACF,OAAO,aAAa,CAAC,QAAQ,CAAC;IAC/B,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,GAAG;IAC5C,OAAO,yBAAyB,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AACzD,CAAC","sourcesContent":["import { deLocalizeUrl } from \"./localize-url.js\";\nimport { normalizeTrailingSlash } from \"./normalize-trailing-slash.js\";\nimport { execUrlPattern } from \"./exec-url-pattern.js\";\nimport { routeStrategies, strategy } from \"./variables.js\";\n\n/** @type {string | undefined} */\nlet cachedRouteStrategyUrl;\n/** @type {{ match: string; strategy?: typeof strategy; exclude?: boolean } | undefined} */\nlet cachedRouteStrategy;\n\n/**\n * Match route policy against both the public URL and its canonical URL.\n *\n * The function is deliberately separate from variables.js: configuration is\n * inert data, while canonicalization and route selection form a routing layer.\n *\n * @param {string | URL} url\n * @returns {{ match: string; strategy?: typeof strategy; exclude?: boolean } | undefined}\n */\nexport function findMatchingRouteStrategy(url) {\n\tif (routeStrategies.length === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst urlString = typeof url === \"string\" ? url : url.href;\n\tif (cachedRouteStrategyUrl === urlString) {\n\t\treturn cachedRouteStrategy;\n\t}\n\n\tconst publicUrl = normalizeTrailingSlash(\n\t\tnew URL(urlString, \"http://example.com\")\n\t);\n\tconst canonicalUrl = deLocalizeUrl(publicUrl);\n\tconst candidateUrls =\n\t\tcanonicalUrl.href === publicUrl.href\n\t\t\t? [publicUrl]\n\t\t\t: [publicUrl, canonicalUrl];\n\tlet match;\n\tfor (const candidateUrl of candidateUrls) {\n\t\tfor (const routeStrategy of routeStrategies) {\n\t\t\tconst pattern = new URLPattern(routeStrategy.match, candidateUrl.href);\n\t\t\tif (execUrlPattern(pattern, candidateUrl)) {\n\t\t\t\tmatch = routeStrategy;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (match) break;\n\t}\n\n\tcachedRouteStrategyUrl = urlString;\n\tcachedRouteStrategy = match;\n\treturn match;\n}\n\n/**\n * Returns the strategy to use for a specific URL.\n *\n * If no route strategy matches (or the matching rule is `exclude: true`),\n * the global strategy is returned.\n *\n * @param {string | URL} url\n * @returns {typeof strategy}\n */\nexport function getStrategyForUrl(url) {\n\tconst routeStrategy = findMatchingRouteStrategy(url);\n\tif (\n\t\trouteStrategy &&\n\t\trouteStrategy.exclude !== true &&\n\t\tArray.isArray(routeStrategy.strategy)\n\t) {\n\t\treturn routeStrategy.strategy;\n\t}\n\treturn strategy;\n}\n\n/**\n * Returns whether the given URL is excluded from middleware i18n processing.\n *\n * @param {string | URL} url\n * @returns {boolean}\n */\nexport function isExcludedByRouteStrategy(url) {\n\treturn findMatchingRouteStrategy(url)?.exclude === true;\n}\n"]}
@@ -4,6 +4,7 @@ import { getLocale, getLocaleForUrl } from "./get-locale.js";
4
4
  import { getUrlOrigin } from "./get-url-origin.js";
5
5
  import { extractLocaleFromRequestAsync } from "./extract-locale-from-request-async.js";
6
6
  import { getStrategyForUrl, isExcludedByRouteStrategy, } from "./route-strategy.js";
7
+ import { trailingSlash } from "./variables.js";
7
8
  /**
8
9
  * @typedef {object} ShouldRedirectServerInput
9
10
  * @property {Request} request
@@ -149,7 +150,9 @@ function resolveUrl(input) {
149
150
  */
150
151
  function normalizeUrl(url) {
151
152
  const urlObj = new URL(url);
152
- urlObj.pathname = urlObj.pathname.replace(/\/$/, "");
153
+ if (trailingSlash === undefined) {
154
+ urlObj.pathname = urlObj.pathname.replace(/\/$/, "");
155
+ }
153
156
  return urlObj.href;
154
157
  }
155
158
  //# sourceMappingURL=should-redirect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"should-redirect.js","sourceRoot":"","sources":["../../../src/compiler/runtime/should-redirect.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,6BAA6B,EAAE,MAAM,wCAAwC,CAAC;AACvF,OAAO,EACN,iBAAiB,EACjB,yBAAyB,GACzB,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAK,GAAG,EAAE;IAC9C,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAEpD,IAAI,yBAAyB,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7E,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;IAClE,CAAC;IAED,MAAM,YAAY,GAAG,WAAW,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAE9D,MAAM,4BAA4B,GACjC,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAEnE,OAAO;QACN,cAAc,EAAE,4BAA4B;QAC5C,MAAM;QACN,WAAW,EAAE,4BAA4B,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS;KACpE,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,aAAa,CAAC,KAAK,EAAE,UAAU;IAC7C,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,MAAM,EAAE,CAAC;QACZ,OAAO,MAAM,CAAC;IACf,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,6BAA6B,CAAC,KAAK,CAAC,OAAO,EAAE;YACnD,mBAAmB,EAAE,UAAU;SAC/B,CAAC,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,GAAG,KAAK,WAAW,EAAE,CAAC;QACxD,OAAO,eAAe,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,SAAS,EAAE,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CAAC,KAAK;IACxB,IACC,qBAAqB,IAAI,KAAK;QAC9B,KAAK,CAAC,mBAAmB,YAAY,GAAG,EACvC,CAAC;QACF,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;IAED,IACC,qBAAqB,IAAI,KAAK;QAC9B,OAAO,KAAK,CAAC,mBAAmB,KAAK,QAAQ,EAC5C,CAAC;QACF,OAAO,IAAI,GAAG,CACb,KAAK,CAAC,mBAAmB,EACzB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,EAAE,CAClD,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,CAAC,GAAG,YAAY,GAAG,EAAE,CAAC;QAChD,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,KAAK,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;QACrD,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC7D,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,IAAI,KAAK,CACd,oGAAoG,CACpG,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,GAAG;IACxB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC,IAAI,CAAC;AACpB,CAAC","sourcesContent":["import { localizeUrl } from \"./localize-url.js\";\nimport { toLocale } from \"./check-locale.js\";\nimport { getLocale, getLocaleForUrl } from \"./get-locale.js\";\nimport { getUrlOrigin } from \"./get-url-origin.js\";\nimport { extractLocaleFromRequestAsync } from \"./extract-locale-from-request-async.js\";\nimport {\n\tgetStrategyForUrl,\n\tisExcludedByRouteStrategy,\n} from \"./route-strategy.js\";\n\n/**\n * @typedef {object} ShouldRedirectServerInput\n * @property {Request} request\n * @property {string | URL} [effectiveRequestUrl] - Effective request URL to use for route matching, locale detection with the URL strategy, and redirect targets.\n * @property {Locale} [locale]\n *\n * @typedef {object} ShouldRedirectClientInput\n * @property {undefined} [request]\n * @property {string | URL} [url]\n * @property {Locale} [locale]\n *\n * @typedef {ShouldRedirectServerInput | ShouldRedirectClientInput} ShouldRedirectInput\n *\n * @typedef {object} ShouldRedirectResult\n * @property {boolean} shouldRedirect - Indicates whether the consumer should perform a redirect.\n * @property {Locale} locale - Locale resolved using the configured strategies.\n * @property {URL | undefined} redirectUrl - Destination URL when a redirect is required.\n */\n\n/**\n * Determines whether a redirect is required to align the current URL with the active locale.\n *\n * This helper mirrors the logic that powers `paraglideMiddleware`, but works in both server\n * and client environments. It evaluates the configured strategies in order, computes the\n * canonical localized URL, and reports when the current URL does not match.\n *\n * When called in the browser without arguments, the current `window.location.href` is used.\n *\n * @see https://paraglidejs.com/i18n-routing#redirects\n *\n * @example\n * // Client side usage (e.g. TanStack Router beforeLoad hook)\n * async function beforeLoad({ location }) {\n * const decision = await shouldRedirect({ url: location.href });\n *\n * if (decision.shouldRedirect) {\n * throw redirect({ to: decision.redirectUrl.href });\n * }\n * }\n *\n * @example\n * // Server side usage with a Request\n * export async function handle(request) {\n * const decision = await shouldRedirect({ request });\n *\n * if (decision.shouldRedirect) {\n * return Response.redirect(decision.redirectUrl, 307);\n * }\n *\n * return render(request, decision.locale);\n * }\n *\n * @example\n * // Server side usage behind a proxy where request.url is not public-facing\n * export async function handle(request) {\n * const effectiveRequestUrl = new URL(request.url);\n * effectiveRequestUrl.protocol = \"https:\";\n * effectiveRequestUrl.host = \"example.com\";\n *\n * const decision = await shouldRedirect({\n * request,\n * effectiveRequestUrl,\n * });\n *\n * if (decision.shouldRedirect) {\n * return Response.redirect(decision.redirectUrl, 307);\n * }\n * }\n *\n * @param {ShouldRedirectInput} [input]\n * @returns {Promise<ShouldRedirectResult>}\n */\nexport async function shouldRedirect(input = {}) {\n\tconst currentUrl = resolveUrl(input);\n\tconst locale = await resolveLocale(input, currentUrl);\n\tconst strategy = getStrategyForUrl(currentUrl.href);\n\n\tif (isExcludedByRouteStrategy(currentUrl.href) || !strategy.includes(\"url\")) {\n\t\treturn { shouldRedirect: false, locale, redirectUrl: undefined };\n\t}\n\n\tconst localizedUrl = localizeUrl(currentUrl.href, { locale });\n\n\tconst shouldRedirectToLocalizedUrl =\n\t\tnormalizeUrl(localizedUrl.href) !== normalizeUrl(currentUrl.href);\n\n\treturn {\n\t\tshouldRedirect: shouldRedirectToLocalizedUrl,\n\t\tlocale,\n\t\tredirectUrl: shouldRedirectToLocalizedUrl ? localizedUrl : undefined,\n\t};\n}\n\n/**\n * Resolves the locale either from the provided input or by using the configured strategies.\n *\n * @param {ShouldRedirectInput} input\n * @param {URL} currentUrl\n * @returns {Promise<Locale>}\n */\nasync function resolveLocale(input, currentUrl) {\n\tconst locale = toLocale(input.locale);\n\tif (locale) {\n\t\treturn locale;\n\t}\n\n\tif (input.request) {\n\t\treturn extractLocaleFromRequestAsync(input.request, {\n\t\t\teffectiveRequestUrl: currentUrl,\n\t\t});\n\t}\n\n\tif (\"url\" in input && typeof input.url !== \"undefined\") {\n\t\treturn getLocaleForUrl(currentUrl.href);\n\t}\n\n\treturn getLocale();\n}\n\n/**\n * Resolves the current URL from the provided input or runtime context.\n *\n * @param {ShouldRedirectInput} input\n * @returns {URL}\n */\nfunction resolveUrl(input) {\n\tif (\n\t\t\"effectiveRequestUrl\" in input &&\n\t\tinput.effectiveRequestUrl instanceof URL\n\t) {\n\t\treturn new URL(input.effectiveRequestUrl.href);\n\t}\n\n\tif (\n\t\t\"effectiveRequestUrl\" in input &&\n\t\ttypeof input.effectiveRequestUrl === \"string\"\n\t) {\n\t\treturn new URL(\n\t\t\tinput.effectiveRequestUrl,\n\t\t\tinput.request ? input.request.url : getUrlOrigin()\n\t\t);\n\t}\n\n\tif (input.request) {\n\t\treturn new URL(input.request.url);\n\t}\n\n\tif (\"url\" in input && input.url instanceof URL) {\n\t\treturn new URL(input.url.href);\n\t}\n\n\tif (\"url\" in input && typeof input.url === \"string\") {\n\t\treturn new URL(input.url, getUrlOrigin());\n\t}\n\n\tif (typeof window !== \"undefined\" && window?.location?.href) {\n\t\treturn new URL(window.location.href);\n\t}\n\n\tthrow new Error(\n\t\t\"shouldRedirect() requires either a request, an absolute URL, or must run in a browser environment.\"\n\t);\n}\n\n/**\n * Normalize url for comparison by stripping the trailing slash.\n *\n * @param {string} url\n * @returns {string}\n */\nfunction normalizeUrl(url) {\n\tconst urlObj = new URL(url);\n\turlObj.pathname = urlObj.pathname.replace(/\\/$/, \"\");\n\treturn urlObj.href;\n}\n"]}
1
+ {"version":3,"file":"should-redirect.js","sourceRoot":"","sources":["../../../src/compiler/runtime/should-redirect.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,6BAA6B,EAAE,MAAM,wCAAwC,CAAC;AACvF,OAAO,EACN,iBAAiB,EACjB,yBAAyB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAK,GAAG,EAAE;IAC9C,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAEpD,IAAI,yBAAyB,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7E,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;IAClE,CAAC;IAED,MAAM,YAAY,GAAG,WAAW,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAE9D,MAAM,4BAA4B,GACjC,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAEnE,OAAO;QACN,cAAc,EAAE,4BAA4B;QAC5C,MAAM;QACN,WAAW,EAAE,4BAA4B,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS;KACpE,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,aAAa,CAAC,KAAK,EAAE,UAAU;IAC7C,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,MAAM,EAAE,CAAC;QACZ,OAAO,MAAM,CAAC;IACf,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,6BAA6B,CAAC,KAAK,CAAC,OAAO,EAAE;YACnD,mBAAmB,EAAE,UAAU;SAC/B,CAAC,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,GAAG,KAAK,WAAW,EAAE,CAAC;QACxD,OAAO,eAAe,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,SAAS,EAAE,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CAAC,KAAK;IACxB,IACC,qBAAqB,IAAI,KAAK;QAC9B,KAAK,CAAC,mBAAmB,YAAY,GAAG,EACvC,CAAC;QACF,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;IAED,IACC,qBAAqB,IAAI,KAAK;QAC9B,OAAO,KAAK,CAAC,mBAAmB,KAAK,QAAQ,EAC5C,CAAC;QACF,OAAO,IAAI,GAAG,CACb,KAAK,CAAC,mBAAmB,EACzB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,EAAE,CAClD,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,CAAC,GAAG,YAAY,GAAG,EAAE,CAAC;QAChD,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,KAAK,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;QACrD,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC7D,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,IAAI,KAAK,CACd,oGAAoG,CACpG,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,GAAG;IACxB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACpB,CAAC","sourcesContent":["import { localizeUrl } from \"./localize-url.js\";\nimport { toLocale } from \"./check-locale.js\";\nimport { getLocale, getLocaleForUrl } from \"./get-locale.js\";\nimport { getUrlOrigin } from \"./get-url-origin.js\";\nimport { extractLocaleFromRequestAsync } from \"./extract-locale-from-request-async.js\";\nimport {\n\tgetStrategyForUrl,\n\tisExcludedByRouteStrategy,\n} from \"./route-strategy.js\";\nimport { trailingSlash } from \"./variables.js\";\n\n/**\n * @typedef {object} ShouldRedirectServerInput\n * @property {Request} request\n * @property {string | URL} [effectiveRequestUrl] - Effective request URL to use for route matching, locale detection with the URL strategy, and redirect targets.\n * @property {Locale} [locale]\n *\n * @typedef {object} ShouldRedirectClientInput\n * @property {undefined} [request]\n * @property {string | URL} [url]\n * @property {Locale} [locale]\n *\n * @typedef {ShouldRedirectServerInput | ShouldRedirectClientInput} ShouldRedirectInput\n *\n * @typedef {object} ShouldRedirectResult\n * @property {boolean} shouldRedirect - Indicates whether the consumer should perform a redirect.\n * @property {Locale} locale - Locale resolved using the configured strategies.\n * @property {URL | undefined} redirectUrl - Destination URL when a redirect is required.\n */\n\n/**\n * Determines whether a redirect is required to align the current URL with the active locale.\n *\n * This helper mirrors the logic that powers `paraglideMiddleware`, but works in both server\n * and client environments. It evaluates the configured strategies in order, computes the\n * canonical localized URL, and reports when the current URL does not match.\n *\n * When called in the browser without arguments, the current `window.location.href` is used.\n *\n * @see https://paraglidejs.com/i18n-routing#redirects\n *\n * @example\n * // Client side usage (e.g. TanStack Router beforeLoad hook)\n * async function beforeLoad({ location }) {\n * const decision = await shouldRedirect({ url: location.href });\n *\n * if (decision.shouldRedirect) {\n * throw redirect({ to: decision.redirectUrl.href });\n * }\n * }\n *\n * @example\n * // Server side usage with a Request\n * export async function handle(request) {\n * const decision = await shouldRedirect({ request });\n *\n * if (decision.shouldRedirect) {\n * return Response.redirect(decision.redirectUrl, 307);\n * }\n *\n * return render(request, decision.locale);\n * }\n *\n * @example\n * // Server side usage behind a proxy where request.url is not public-facing\n * export async function handle(request) {\n * const effectiveRequestUrl = new URL(request.url);\n * effectiveRequestUrl.protocol = \"https:\";\n * effectiveRequestUrl.host = \"example.com\";\n *\n * const decision = await shouldRedirect({\n * request,\n * effectiveRequestUrl,\n * });\n *\n * if (decision.shouldRedirect) {\n * return Response.redirect(decision.redirectUrl, 307);\n * }\n * }\n *\n * @param {ShouldRedirectInput} [input]\n * @returns {Promise<ShouldRedirectResult>}\n */\nexport async function shouldRedirect(input = {}) {\n\tconst currentUrl = resolveUrl(input);\n\tconst locale = await resolveLocale(input, currentUrl);\n\tconst strategy = getStrategyForUrl(currentUrl.href);\n\n\tif (isExcludedByRouteStrategy(currentUrl.href) || !strategy.includes(\"url\")) {\n\t\treturn { shouldRedirect: false, locale, redirectUrl: undefined };\n\t}\n\n\tconst localizedUrl = localizeUrl(currentUrl.href, { locale });\n\n\tconst shouldRedirectToLocalizedUrl =\n\t\tnormalizeUrl(localizedUrl.href) !== normalizeUrl(currentUrl.href);\n\n\treturn {\n\t\tshouldRedirect: shouldRedirectToLocalizedUrl,\n\t\tlocale,\n\t\tredirectUrl: shouldRedirectToLocalizedUrl ? localizedUrl : undefined,\n\t};\n}\n\n/**\n * Resolves the locale either from the provided input or by using the configured strategies.\n *\n * @param {ShouldRedirectInput} input\n * @param {URL} currentUrl\n * @returns {Promise<Locale>}\n */\nasync function resolveLocale(input, currentUrl) {\n\tconst locale = toLocale(input.locale);\n\tif (locale) {\n\t\treturn locale;\n\t}\n\n\tif (input.request) {\n\t\treturn extractLocaleFromRequestAsync(input.request, {\n\t\t\teffectiveRequestUrl: currentUrl,\n\t\t});\n\t}\n\n\tif (\"url\" in input && typeof input.url !== \"undefined\") {\n\t\treturn getLocaleForUrl(currentUrl.href);\n\t}\n\n\treturn getLocale();\n}\n\n/**\n * Resolves the current URL from the provided input or runtime context.\n *\n * @param {ShouldRedirectInput} input\n * @returns {URL}\n */\nfunction resolveUrl(input) {\n\tif (\n\t\t\"effectiveRequestUrl\" in input &&\n\t\tinput.effectiveRequestUrl instanceof URL\n\t) {\n\t\treturn new URL(input.effectiveRequestUrl.href);\n\t}\n\n\tif (\n\t\t\"effectiveRequestUrl\" in input &&\n\t\ttypeof input.effectiveRequestUrl === \"string\"\n\t) {\n\t\treturn new URL(\n\t\t\tinput.effectiveRequestUrl,\n\t\t\tinput.request ? input.request.url : getUrlOrigin()\n\t\t);\n\t}\n\n\tif (input.request) {\n\t\treturn new URL(input.request.url);\n\t}\n\n\tif (\"url\" in input && input.url instanceof URL) {\n\t\treturn new URL(input.url.href);\n\t}\n\n\tif (\"url\" in input && typeof input.url === \"string\") {\n\t\treturn new URL(input.url, getUrlOrigin());\n\t}\n\n\tif (typeof window !== \"undefined\" && window?.location?.href) {\n\t\treturn new URL(window.location.href);\n\t}\n\n\tthrow new Error(\n\t\t\"shouldRedirect() requires either a request, an absolute URL, or must run in a browser environment.\"\n\t);\n}\n\n/**\n * Normalize url for comparison by stripping the trailing slash.\n *\n * @param {string} url\n * @returns {string}\n */\nfunction normalizeUrl(url) {\n\tconst urlObj = new URL(url);\n\tif (trailingSlash === undefined) {\n\t\turlObj.pathname = urlObj.pathname.replace(/\\/$/, \"\");\n\t}\n\treturn urlObj.href;\n}\n"]}
@@ -31,6 +31,33 @@ test("shouldRedirect redirects to the strategy-preferred locale on the server",
31
31
  expect(decision.redirectUrl?.href).toBe("https://example.com/fr/dashboard");
32
32
  expect(decision.locale).toBe("fr");
33
33
  });
34
+ test("shouldRedirect enforces the configured trailing slash policy", async () => {
35
+ const runtime = await createParaglide({
36
+ blob: await newProject({
37
+ settings: {
38
+ baseLocale: "en",
39
+ locales: ["en", "de"],
40
+ },
41
+ }),
42
+ strategy: ["url", "baseLocale"],
43
+ trailingSlash: "never",
44
+ urlPatterns: [
45
+ {
46
+ pattern: "/about",
47
+ localized: [
48
+ ["de", "/de/ueber"],
49
+ ["en", "/about"],
50
+ ],
51
+ },
52
+ ],
53
+ });
54
+ const decision = await runtime.shouldRedirect({
55
+ request: new Request("https://example.com/de/ueber/"),
56
+ });
57
+ expect(decision.locale).toBe("de");
58
+ expect(decision.shouldRedirect).toBe(true);
59
+ expect(decision.redirectUrl?.href).toBe("https://example.com/de/ueber");
60
+ });
34
61
  test("shouldRedirect uses the provided public url when the transport request url differs", async () => {
35
62
  const runtime = await createParaglide({
36
63
  blob: await newProject({
@@ -217,7 +244,7 @@ test("shouldRedirect({ url }) resolves locale using target URL routeStrategies o
217
244
  ],
218
245
  routeStrategies: [
219
246
  {
220
- match: "/dashboard/:path(.*)?",
247
+ match: "/dashboard",
221
248
  strategy: ["cookie", "baseLocale"],
222
249
  },
223
250
  ],
@@ -287,6 +314,7 @@ test("shouldRedirect respects routeStrategies that disable url strategy per rout
287
314
  }),
288
315
  strategy: ["url", "cookie", "baseLocale"],
289
316
  cookieName: "PARAGLIDE_LOCALE",
317
+ trailingSlash: "never",
290
318
  routeStrategies: [
291
319
  {
292
320
  match: "/dashboard/:path(.*)?",
@@ -294,7 +322,7 @@ test("shouldRedirect respects routeStrategies that disable url strategy per rout
294
322
  },
295
323
  ],
296
324
  });
297
- const request = new Request("https://example.com/dashboard", {
325
+ const request = new Request("https://example.com/dashboard/", {
298
326
  headers: {
299
327
  cookie: "PARAGLIDE_LOCALE=fr",
300
328
  },
@@ -1 +1 @@
1
- {"version":3,"file":"should-redirect.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/should-redirect.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, test } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\nimport { newProject } from \"@inlang/sdk\";\n\ntest(\"shouldRedirect redirects to the strategy-preferred locale on the server\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect uses the provided public url when the transport request url differs\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"http://internal.example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest,\n\t\teffectiveRequestUrl: \"https://example.com/en/dashboard\",\n\t});\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect resolves relative effectiveRequestUrl strings against request.url\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest,\n\t\teffectiveRequestUrl: \"/en/dashboard\",\n\t});\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect keeps request precedence over url on the server\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest,\n\t\turl: \"/dashboard\",\n\t} as any);\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect does nothing when the URL already matches\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/fr/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(false);\n\texpect(decision.redirectUrl).toBeUndefined();\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect falls back to the browser URL when no input is provided\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\", \"globalVariable\"],\n\t\tisServer: \"false\",\n\t\turlPatterns: undefined,\n\t});\n\n\tconst originalWindow = globalThis.window;\n\ttry {\n\t\tglobalThis.window = {\n\t\t\tlocation: {\n\t\t\t\thref: \"https://example.com/en/profile\",\n\t\t\t\torigin: \"https://example.com\",\n\t\t\t},\n\t\t} as any;\n\n\t\truntime.overwriteGetLocale(() => \"de\");\n\n\t\tconst decision = await runtime.shouldRedirect();\n\n\t\texpect(decision.shouldRedirect).toBe(true);\n\t\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/de/profile\");\n\t\texpect(decision.locale).toBe(\"de\");\n\t} finally {\n\t\tif (originalWindow === undefined) {\n\t\t\tReflect.deleteProperty(globalThis, \"window\");\n\t\t} else {\n\t\t\tglobalThis.window = originalWindow;\n\t\t}\n\t}\n});\n\ntest(\"shouldRedirect({ url }) resolves locale using target URL routeStrategies on client\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\", \"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\tisServer: \"false\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t\trouteStrategies: [\n\t\t\t{\n\t\t\t\tmatch: \"/dashboard/:path(.*)?\",\n\t\t\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst originalWindow = globalThis.window;\n\tconst originalDocument = globalThis.document;\n\n\ttry {\n\t\tglobalThis.window = {\n\t\t\tlocation: {\n\t\t\t\thref: \"https://example.com/dashboard\",\n\t\t\t\torigin: \"https://example.com\",\n\t\t\t},\n\t\t} as any;\n\t\tglobalThis.document = {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t} as any;\n\n\t\tconst decision = await runtime.shouldRedirect({\n\t\t\turl: \"https://example.com/en/profile\",\n\t\t});\n\n\t\texpect(decision.shouldRedirect).toBe(false);\n\t\texpect(decision.redirectUrl).toBeUndefined();\n\t\texpect(decision.locale).toBe(\"en\");\n\t} finally {\n\t\tif (originalWindow === undefined) {\n\t\t\tReflect.deleteProperty(globalThis, \"window\");\n\t\t} else {\n\t\t\tglobalThis.window = originalWindow;\n\t\t}\n\n\t\tif (originalDocument === undefined) {\n\t\t\tReflect.deleteProperty(globalThis, \"document\");\n\t\t} else {\n\t\t\tglobalThis.document = originalDocument;\n\t\t}\n\t}\n});\n\ntest(\"shouldRedirect never suggests a redirect without the url strategy\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(false);\n\texpect(decision.redirectUrl).toBeUndefined();\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect respects routeStrategies that disable url strategy per route\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\", \"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\trouteStrategies: [\n\t\t\t{\n\t\t\t\tmatch: \"/dashboard/:path(.*)?\",\n\t\t\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(false);\n\texpect(decision.redirectUrl).toBeUndefined();\n\texpect(decision.locale).toBe(\"fr\");\n});\n"]}
1
+ {"version":3,"file":"should-redirect.test.js","sourceRoot":"","sources":["../../../src/compiler/runtime/should-redirect.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, test } from \"vitest\";\nimport { createParaglide } from \"../create-paraglide.js\";\nimport { newProject } from \"@inlang/sdk\";\n\ntest(\"shouldRedirect redirects to the strategy-preferred locale on the server\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect enforces the configured trailing slash policy\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\", \"baseLocale\"],\n\t\ttrailingSlash: \"never\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"/about\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"de\", \"/de/ueber\"],\n\t\t\t\t\t[\"en\", \"/about\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest: new Request(\"https://example.com/de/ueber/\"),\n\t});\n\n\texpect(decision.locale).toBe(\"de\");\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/de/ueber\");\n});\n\ntest(\"shouldRedirect uses the provided public url when the transport request url differs\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"http://internal.example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest,\n\t\teffectiveRequestUrl: \"https://example.com/en/dashboard\",\n\t});\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect resolves relative effectiveRequestUrl strings against request.url\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest,\n\t\teffectiveRequestUrl: \"/en/dashboard\",\n\t});\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect keeps request precedence over url on the server\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest,\n\t\turl: \"/dashboard\",\n\t} as any);\n\n\texpect(decision.shouldRedirect).toBe(true);\n\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/fr/dashboard\");\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect does nothing when the URL already matches\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\", \"url\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/fr/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(false);\n\texpect(decision.redirectUrl).toBeUndefined();\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect falls back to the browser URL when no input is provided\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"de\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\", \"globalVariable\"],\n\t\tisServer: \"false\",\n\t\turlPatterns: undefined,\n\t});\n\n\tconst originalWindow = globalThis.window;\n\ttry {\n\t\tglobalThis.window = {\n\t\t\tlocation: {\n\t\t\t\thref: \"https://example.com/en/profile\",\n\t\t\t\torigin: \"https://example.com\",\n\t\t\t},\n\t\t} as any;\n\n\t\truntime.overwriteGetLocale(() => \"de\");\n\n\t\tconst decision = await runtime.shouldRedirect();\n\n\t\texpect(decision.shouldRedirect).toBe(true);\n\t\texpect(decision.redirectUrl?.href).toBe(\"https://example.com/de/profile\");\n\t\texpect(decision.locale).toBe(\"de\");\n\t} finally {\n\t\tif (originalWindow === undefined) {\n\t\t\tReflect.deleteProperty(globalThis, \"window\");\n\t\t} else {\n\t\t\tglobalThis.window = originalWindow;\n\t\t}\n\t}\n});\n\ntest(\"shouldRedirect({ url }) resolves locale using target URL routeStrategies on client\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\", \"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\tisServer: \"false\",\n\t\turlPatterns: [\n\t\t\t{\n\t\t\t\tpattern: \"https://example.com/:path(.*)?\",\n\t\t\t\tlocalized: [\n\t\t\t\t\t[\"en\", \"https://example.com/en/:path(.*)?\"],\n\t\t\t\t\t[\"fr\", \"https://example.com/fr/:path(.*)?\"],\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t\trouteStrategies: [\n\t\t\t{\n\t\t\t\tmatch: \"/dashboard\",\n\t\t\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst originalWindow = globalThis.window;\n\tconst originalDocument = globalThis.document;\n\n\ttry {\n\t\tglobalThis.window = {\n\t\t\tlocation: {\n\t\t\t\thref: \"https://example.com/dashboard\",\n\t\t\t\torigin: \"https://example.com\",\n\t\t\t},\n\t\t} as any;\n\t\tglobalThis.document = {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t} as any;\n\n\t\tconst decision = await runtime.shouldRedirect({\n\t\t\turl: \"https://example.com/en/profile\",\n\t\t});\n\n\t\texpect(decision.shouldRedirect).toBe(false);\n\t\texpect(decision.redirectUrl).toBeUndefined();\n\t\texpect(decision.locale).toBe(\"en\");\n\t} finally {\n\t\tif (originalWindow === undefined) {\n\t\t\tReflect.deleteProperty(globalThis, \"window\");\n\t\t} else {\n\t\t\tglobalThis.window = originalWindow;\n\t\t}\n\n\t\tif (originalDocument === undefined) {\n\t\t\tReflect.deleteProperty(globalThis, \"document\");\n\t\t} else {\n\t\t\tglobalThis.document = originalDocument;\n\t\t}\n\t}\n});\n\ntest(\"shouldRedirect never suggests a redirect without the url strategy\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"cookie\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t});\n\n\tconst request = new Request(\"https://example.com/en/dashboard\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(false);\n\texpect(decision.redirectUrl).toBeUndefined();\n\texpect(decision.locale).toBe(\"fr\");\n});\n\ntest(\"shouldRedirect respects routeStrategies that disable url strategy per route\", async () => {\n\tconst runtime = await createParaglide({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\"],\n\t\t\t},\n\t\t}),\n\t\tstrategy: [\"url\", \"cookie\", \"baseLocale\"],\n\t\tcookieName: \"PARAGLIDE_LOCALE\",\n\t\ttrailingSlash: \"never\",\n\t\trouteStrategies: [\n\t\t\t{\n\t\t\t\tmatch: \"/dashboard/:path(.*)?\",\n\t\t\t\tstrategy: [\"cookie\", \"baseLocale\"],\n\t\t\t},\n\t\t],\n\t});\n\n\tconst request = new Request(\"https://example.com/dashboard/\", {\n\t\theaders: {\n\t\t\tcookie: \"PARAGLIDE_LOCALE=fr\",\n\t\t},\n\t});\n\n\tconst decision = await runtime.shouldRedirect({ request });\n\n\texpect(decision.shouldRedirect).toBe(false);\n\texpect(decision.redirectUrl).toBeUndefined();\n\texpect(decision.locale).toBe(\"fr\");\n});\n"]}
@@ -9,6 +9,7 @@ export type Runtime = {
9
9
  cookieName: typeof import("./variables.js").cookieName;
10
10
  cookieMaxAge: typeof import("./variables.js").cookieMaxAge;
11
11
  urlPatterns: typeof import("./variables.js").urlPatterns;
12
+ trailingSlash: typeof import("./variables.js").trailingSlash;
12
13
  disableAsyncLocalStorage: typeof import("./variables.js").disableAsyncLocalStorage;
13
14
  serverAsyncLocalStorage: typeof import("./variables.js").serverAsyncLocalStorage;
14
15
  getServerAsyncLocalStorage: typeof import("./variables.js").getServerAsyncLocalStorage;
@@ -1 +1 @@
1
- {"version":3,"file":"type.js","sourceRoot":"","sources":["../../../src/compiler/runtime/type.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * The Paraglide runtime API.\n */\nexport type Runtime = {\n\tbaseLocale: typeof import(\"./variables.js\").baseLocale;\n\tlocales: typeof import(\"./variables.js\").locales;\n\tstrategy: typeof import(\"./variables.js\").strategy;\n\trouteStrategies: typeof import(\"./variables.js\").routeStrategies;\n\tcookieName: typeof import(\"./variables.js\").cookieName;\n\tcookieMaxAge: typeof import(\"./variables.js\").cookieMaxAge;\n\turlPatterns: typeof import(\"./variables.js\").urlPatterns;\n\tdisableAsyncLocalStorage: typeof import(\"./variables.js\").disableAsyncLocalStorage;\n\tserverAsyncLocalStorage: typeof import(\"./variables.js\").serverAsyncLocalStorage;\n\tgetServerAsyncLocalStorage: typeof import(\"./variables.js\").getServerAsyncLocalStorage;\n\texperimentalMiddlewareLocaleSplitting: typeof import(\"./variables.js\").experimentalMiddlewareLocaleSplitting;\n\tisServer: typeof import(\"./variables.js\").isServer;\n\texperimentalStaticLocale: typeof import(\"./variables.js\").experimentalStaticLocale;\n\tgetLocale: typeof import(\"./get-locale.js\").getLocale;\n\tgetTextDirection: typeof import(\"./get-text-direction.js\").getTextDirection;\n\tsetLocale: typeof import(\"./set-locale.js\").setLocale;\n\tgetUrlOrigin: typeof import(\"./get-url-origin.js\").getUrlOrigin;\n\toverwriteGetLocale: typeof import(\"./get-locale.js\").overwriteGetLocale;\n\toverwriteSetLocale: typeof import(\"./set-locale.js\").overwriteSetLocale;\n\toverwriteGetUrlOrigin: typeof import(\"./get-url-origin.js\").overwriteGetUrlOrigin;\n\toverwriteServerAsyncLocalStorage: typeof import(\"./variables.js\").overwriteServerAsyncLocalStorage;\n\ttoLocale: typeof import(\"./check-locale.js\").toLocale;\n\tassertIsLocale: typeof import(\"./check-locale.js\").assertIsLocale;\n\tisLocale: typeof import(\"./check-locale.js\").isLocale;\n\tlocalizeHref: typeof import(\"./localize-href.js\").localizeHref;\n\tdeLocalizeHref: typeof import(\"./localize-href.js\").deLocalizeHref;\n\tlocalizeUrl: typeof import(\"./localize-url.js\").localizeUrl;\n\tdeLocalizeUrl: typeof import(\"./localize-url.js\").deLocalizeUrl;\n\tshouldRedirect: typeof import(\"./should-redirect.js\").shouldRedirect;\n\textractLocaleFromUrl: typeof import(\"./extract-locale-from-url.js\").extractLocaleFromUrl;\n\textractLocaleFromRequest: typeof import(\"./extract-locale-from-request.js\").extractLocaleFromRequest;\n\textractLocaleFromRequestAsync: typeof import(\"./extract-locale-from-request-async.js\").extractLocaleFromRequestAsync;\n\textractLocaleFromCookie: typeof import(\"./extract-locale-from-cookie.js\").extractLocaleFromCookie;\n\textractLocaleFromHeader: typeof import(\"./extract-locale-from-header.js\").extractLocaleFromHeader;\n\textractLocaleFromNavigator: typeof import(\"./extract-locale-from-navigator.js\").extractLocaleFromNavigator;\n\tgenerateStaticLocalizedUrls: typeof import(\"./generate-static-localized-urls.js\").generateStaticLocalizedUrls;\n\ttrackMessageCall: typeof import(\"./track-message-call.js\").trackMessageCall;\n\tgetStrategyForUrl: typeof import(\"./route-strategy.js\").getStrategyForUrl;\n\tisExcludedByRouteStrategy: typeof import(\"./route-strategy.js\").isExcludedByRouteStrategy;\n\tdefineCustomServerStrategy: typeof import(\"./strategy.js\").defineCustomServerStrategy;\n\tdefineCustomClientStrategy: typeof import(\"./strategy.js\").defineCustomClientStrategy;\n};\n"]}
1
+ {"version":3,"file":"type.js","sourceRoot":"","sources":["../../../src/compiler/runtime/type.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * The Paraglide runtime API.\n */\nexport type Runtime = {\n\tbaseLocale: typeof import(\"./variables.js\").baseLocale;\n\tlocales: typeof import(\"./variables.js\").locales;\n\tstrategy: typeof import(\"./variables.js\").strategy;\n\trouteStrategies: typeof import(\"./variables.js\").routeStrategies;\n\tcookieName: typeof import(\"./variables.js\").cookieName;\n\tcookieMaxAge: typeof import(\"./variables.js\").cookieMaxAge;\n\turlPatterns: typeof import(\"./variables.js\").urlPatterns;\n\ttrailingSlash: typeof import(\"./variables.js\").trailingSlash;\n\tdisableAsyncLocalStorage: typeof import(\"./variables.js\").disableAsyncLocalStorage;\n\tserverAsyncLocalStorage: typeof import(\"./variables.js\").serverAsyncLocalStorage;\n\tgetServerAsyncLocalStorage: typeof import(\"./variables.js\").getServerAsyncLocalStorage;\n\texperimentalMiddlewareLocaleSplitting: typeof import(\"./variables.js\").experimentalMiddlewareLocaleSplitting;\n\tisServer: typeof import(\"./variables.js\").isServer;\n\texperimentalStaticLocale: typeof import(\"./variables.js\").experimentalStaticLocale;\n\tgetLocale: typeof import(\"./get-locale.js\").getLocale;\n\tgetTextDirection: typeof import(\"./get-text-direction.js\").getTextDirection;\n\tsetLocale: typeof import(\"./set-locale.js\").setLocale;\n\tgetUrlOrigin: typeof import(\"./get-url-origin.js\").getUrlOrigin;\n\toverwriteGetLocale: typeof import(\"./get-locale.js\").overwriteGetLocale;\n\toverwriteSetLocale: typeof import(\"./set-locale.js\").overwriteSetLocale;\n\toverwriteGetUrlOrigin: typeof import(\"./get-url-origin.js\").overwriteGetUrlOrigin;\n\toverwriteServerAsyncLocalStorage: typeof import(\"./variables.js\").overwriteServerAsyncLocalStorage;\n\ttoLocale: typeof import(\"./check-locale.js\").toLocale;\n\tassertIsLocale: typeof import(\"./check-locale.js\").assertIsLocale;\n\tisLocale: typeof import(\"./check-locale.js\").isLocale;\n\tlocalizeHref: typeof import(\"./localize-href.js\").localizeHref;\n\tdeLocalizeHref: typeof import(\"./localize-href.js\").deLocalizeHref;\n\tlocalizeUrl: typeof import(\"./localize-url.js\").localizeUrl;\n\tdeLocalizeUrl: typeof import(\"./localize-url.js\").deLocalizeUrl;\n\tshouldRedirect: typeof import(\"./should-redirect.js\").shouldRedirect;\n\textractLocaleFromUrl: typeof import(\"./extract-locale-from-url.js\").extractLocaleFromUrl;\n\textractLocaleFromRequest: typeof import(\"./extract-locale-from-request.js\").extractLocaleFromRequest;\n\textractLocaleFromRequestAsync: typeof import(\"./extract-locale-from-request-async.js\").extractLocaleFromRequestAsync;\n\textractLocaleFromCookie: typeof import(\"./extract-locale-from-cookie.js\").extractLocaleFromCookie;\n\textractLocaleFromHeader: typeof import(\"./extract-locale-from-header.js\").extractLocaleFromHeader;\n\textractLocaleFromNavigator: typeof import(\"./extract-locale-from-navigator.js\").extractLocaleFromNavigator;\n\tgenerateStaticLocalizedUrls: typeof import(\"./generate-static-localized-urls.js\").generateStaticLocalizedUrls;\n\ttrackMessageCall: typeof import(\"./track-message-call.js\").trackMessageCall;\n\tgetStrategyForUrl: typeof import(\"./route-strategy.js\").getStrategyForUrl;\n\tisExcludedByRouteStrategy: typeof import(\"./route-strategy.js\").isExcludedByRouteStrategy;\n\tdefineCustomServerStrategy: typeof import(\"./strategy.js\").defineCustomServerStrategy;\n\tdefineCustomClientStrategy: typeof import(\"./strategy.js\").defineCustomClientStrategy;\n};\n"]}
@@ -53,6 +53,12 @@ export declare const urlPatterns: Array<{
53
53
  pattern: string;
54
54
  localized: Array<[Locale, string]>;
55
55
  }>;
56
+ /**
57
+ * Controls trailing slash canonicalization for localized URLs.
58
+ *
59
+ * @type {"always" | "never" | undefined}
60
+ */
61
+ export declare const trailingSlash: "always" | "never" | undefined;
56
62
  export type ParaglideAsyncLocalStorage = {
57
63
  getStore(): {
58
64
  locale?: Locale;
@@ -46,6 +46,12 @@ export const routeStrategies = [];
46
46
  * @type {Array<{ pattern: string, localized: Array<[Locale, string]> }>}
47
47
  */
48
48
  export const urlPatterns = [];
49
+ /**
50
+ * Controls trailing slash canonicalization for localized URLs.
51
+ *
52
+ * @type {"always" | "never" | undefined}
53
+ */
54
+ export const trailingSlash = undefined;
49
55
  /**
50
56
  * @typedef {{
51
57
  * getStore(): {
@@ -1 +1 @@
1
- {"version":3,"file":"variables.js","sourceRoot":"","sources":["../../../src/compiler/runtime/variables.js"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC;AAE/B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,gCAAgC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAEvE,qBAAqB;AACrB,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,CAAC;AAE1C,qBAAqB;AACrB,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;AAE/C,qBAAqB;AACrB,MAAM,CAAC,MAAM,YAAY,GAAG,iBAAiB,CAAC;AAE9C,qBAAqB;AACrB,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAElD;;GAEG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAE9B;;;;;;;;;;GAUG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,IAAI,uBAAuB,GAAG,SAAS,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,UAAU,0BAA0B;IACzC,OAAO,uBAAuB,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAE9C,MAAM,CAAC,MAAM,qCAAqC,GAAG,KAAK,CAAC;AAE3D,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC;AAEtD,iCAAiC;AACjC,MAAM,CAAC,MAAM,wBAAwB,GAAG,SAAS,CAAC;AAElD;;;;;;;;;GASG;AACH,MAAM,UAAU,gCAAgC,CAAC,KAAK;IACrD,uBAAuB,GAAG,KAAK,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,MAAM,+BAA+B,GAAG,KAAK,CAAC;AAErD,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAElD,MAAM,CAAC,MAAM,wCAAwC,GAAG,KAAK,CAAC;AAE9D,MAAM,CAAC,MAAM,2CAA2C,GAAG,KAAK,CAAC;AAEjE,MAAM,CAAC,MAAM,mCAAmC,GAAG,KAAK,CAAC;AAEzD,MAAM,CAAC,MAAM,sCAAsC,GAAG,KAAK,CAAC","sourcesContent":["/**\n * The project's base locale.\n *\n * @example\n * if (locale === baseLocale) {\n * // do something\n * }\n */\nexport const baseLocale = \"en\";\n\n/**\n * The project's locales that have been specified in the settings.\n *\n * @example\n * if (locales.includes(userSelectedLocale) === false) {\n * throw new Error('Locale is not available');\n * }\n */\nexport const locales = /** @type {readonly string[]} */ ([\"en\", \"de\"]);\n\n/** @type {string} */\nexport const cookieName = \"<cookie-name>\";\n\n/** @type {number} */\nexport const cookieMaxAge = 60 * 60 * 24 * 400;\n\n/** @type {string} */\nexport const cookieDomain = \"<cookie-domain>\";\n\n/** @type {string} */\nexport const localStorageKey = \"PARAGLIDE_LOCALE\";\n\n/**\n * @type {Array<\"cookie\" | \"baseLocale\" | \"globalVariable\" | \"url\" | \"preferredLanguage\" | \"localStorage\" | `custom-${string}`>}\n */\nexport const strategy = [\"globalVariable\"];\n\n/**\n * Route-level strategy overrides.\n *\n * `match` uses URLPattern syntax.\n *\n * @type {Array<{\n * match: string;\n * strategy?: Array<\"cookie\" | \"baseLocale\" | \"globalVariable\" | \"url\" | \"preferredLanguage\" | \"localStorage\" | `custom-${string}`>;\n * exclude?: boolean;\n * }>}\n */\nexport const routeStrategies = [];\n\n/**\n * The used URL patterns.\n *\n * @type {Array<{ pattern: string, localized: Array<[Locale, string]> }>}\n */\nexport const urlPatterns = [];\n\n/**\n * @typedef {{\n * \t\tgetStore(): {\n * \t\tlocale?: Locale,\n * \t\t\torigin?: string,\n * \t\t\tmessageCalls?: Set<string>\n * \t} | undefined,\n * \t\trun: (store: { locale?: Locale, origin?: string, messageCalls?: Set<string>},\n * cb: any) => any\n * }} ParaglideAsyncLocalStorage\n */\n\n/**\n * Server side async local storage that is set by `serverMiddleware()`.\n *\n * The variable is used to retrieve the locale and origin in a server-side\n * rendering context without effecting other requests.\n *\n * @type {ParaglideAsyncLocalStorage | undefined}\n */\nexport let serverAsyncLocalStorage = undefined;\n\n/**\n * Returns the current server-side async local storage instance.\n *\n * Accessing the mutable value through a function keeps it observable when\n * module interceptors wrap exported bindings and snapshot their initial value.\n *\n * @returns {ParaglideAsyncLocalStorage | undefined}\n */\nexport function getServerAsyncLocalStorage() {\n\treturn serverAsyncLocalStorage;\n}\n\nexport const disableAsyncLocalStorage = false;\n\nexport const experimentalMiddlewareLocaleSplitting = false;\n\nexport const isServer = typeof window === \"undefined\";\n\n/** @type {Locale | undefined} */\nexport const experimentalStaticLocale = undefined;\n\n/**\n * Sets the server side async local storage.\n *\n * The function is needed because the `runtime.js` file\n * must define the `serverAsyncLocalStorage` variable to\n * avoid a circular import between `runtime.js` and\n * `server.js` files.\n *\n * @param {ParaglideAsyncLocalStorage | undefined} value\n */\nexport function overwriteServerAsyncLocalStorage(value) {\n\tserverAsyncLocalStorage = value;\n}\n\nexport const TREE_SHAKE_COOKIE_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_URL_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = false;\n\nexport const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = false;\n"]}
1
+ {"version":3,"file":"variables.js","sourceRoot":"","sources":["../../../src/compiler/runtime/variables.js"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC;AAE/B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,gCAAgC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAEvE,qBAAqB;AACrB,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,CAAC;AAE1C,qBAAqB;AACrB,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;AAE/C,qBAAqB;AACrB,MAAM,CAAC,MAAM,YAAY,GAAG,iBAAiB,CAAC;AAE9C,qBAAqB;AACrB,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAElD;;GAEG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAE9B;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC;AAEvC;;;;;;;;;;GAUG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,IAAI,uBAAuB,GAAG,SAAS,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,UAAU,0BAA0B;IACzC,OAAO,uBAAuB,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAE9C,MAAM,CAAC,MAAM,qCAAqC,GAAG,KAAK,CAAC;AAE3D,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC;AAEtD,iCAAiC;AACjC,MAAM,CAAC,MAAM,wBAAwB,GAAG,SAAS,CAAC;AAElD;;;;;;;;;GASG;AACH,MAAM,UAAU,gCAAgC,CAAC,KAAK;IACrD,uBAAuB,GAAG,KAAK,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,MAAM,+BAA+B,GAAG,KAAK,CAAC;AAErD,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAElD,MAAM,CAAC,MAAM,wCAAwC,GAAG,KAAK,CAAC;AAE9D,MAAM,CAAC,MAAM,2CAA2C,GAAG,KAAK,CAAC;AAEjE,MAAM,CAAC,MAAM,mCAAmC,GAAG,KAAK,CAAC;AAEzD,MAAM,CAAC,MAAM,sCAAsC,GAAG,KAAK,CAAC","sourcesContent":["/**\n * The project's base locale.\n *\n * @example\n * if (locale === baseLocale) {\n * // do something\n * }\n */\nexport const baseLocale = \"en\";\n\n/**\n * The project's locales that have been specified in the settings.\n *\n * @example\n * if (locales.includes(userSelectedLocale) === false) {\n * throw new Error('Locale is not available');\n * }\n */\nexport const locales = /** @type {readonly string[]} */ ([\"en\", \"de\"]);\n\n/** @type {string} */\nexport const cookieName = \"<cookie-name>\";\n\n/** @type {number} */\nexport const cookieMaxAge = 60 * 60 * 24 * 400;\n\n/** @type {string} */\nexport const cookieDomain = \"<cookie-domain>\";\n\n/** @type {string} */\nexport const localStorageKey = \"PARAGLIDE_LOCALE\";\n\n/**\n * @type {Array<\"cookie\" | \"baseLocale\" | \"globalVariable\" | \"url\" | \"preferredLanguage\" | \"localStorage\" | `custom-${string}`>}\n */\nexport const strategy = [\"globalVariable\"];\n\n/**\n * Route-level strategy overrides.\n *\n * `match` uses URLPattern syntax.\n *\n * @type {Array<{\n * match: string;\n * strategy?: Array<\"cookie\" | \"baseLocale\" | \"globalVariable\" | \"url\" | \"preferredLanguage\" | \"localStorage\" | `custom-${string}`>;\n * exclude?: boolean;\n * }>}\n */\nexport const routeStrategies = [];\n\n/**\n * The used URL patterns.\n *\n * @type {Array<{ pattern: string, localized: Array<[Locale, string]> }>}\n */\nexport const urlPatterns = [];\n\n/**\n * Controls trailing slash canonicalization for localized URLs.\n *\n * @type {\"always\" | \"never\" | undefined}\n */\nexport const trailingSlash = undefined;\n\n/**\n * @typedef {{\n * \t\tgetStore(): {\n * \t\tlocale?: Locale,\n * \t\t\torigin?: string,\n * \t\t\tmessageCalls?: Set<string>\n * \t} | undefined,\n * \t\trun: (store: { locale?: Locale, origin?: string, messageCalls?: Set<string>},\n * cb: any) => any\n * }} ParaglideAsyncLocalStorage\n */\n\n/**\n * Server side async local storage that is set by `serverMiddleware()`.\n *\n * The variable is used to retrieve the locale and origin in a server-side\n * rendering context without effecting other requests.\n *\n * @type {ParaglideAsyncLocalStorage | undefined}\n */\nexport let serverAsyncLocalStorage = undefined;\n\n/**\n * Returns the current server-side async local storage instance.\n *\n * Accessing the mutable value through a function keeps it observable when\n * module interceptors wrap exported bindings and snapshot their initial value.\n *\n * @returns {ParaglideAsyncLocalStorage | undefined}\n */\nexport function getServerAsyncLocalStorage() {\n\treturn serverAsyncLocalStorage;\n}\n\nexport const disableAsyncLocalStorage = false;\n\nexport const experimentalMiddlewareLocaleSplitting = false;\n\nexport const isServer = typeof window === \"undefined\";\n\n/** @type {Locale | undefined} */\nexport const experimentalStaticLocale = undefined;\n\n/**\n * Sets the server side async local storage.\n *\n * The function is needed because the `runtime.js` file\n * must define the `serverAsyncLocalStorage` variable to\n * avoid a circular import between `runtime.js` and\n * `server.js` files.\n *\n * @param {ParaglideAsyncLocalStorage | undefined} value\n */\nexport function overwriteServerAsyncLocalStorage(value) {\n\tserverAsyncLocalStorage = value;\n}\n\nexport const TREE_SHAKE_COOKIE_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_URL_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_GLOBAL_VARIABLE_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_PREFERRED_LANGUAGE_STRATEGY_USED = false;\n\nexport const TREE_SHAKE_DEFAULT_URL_PATTERN_USED = false;\n\nexport const TREE_SHAKE_LOCAL_STORAGE_STRATEGY_USED = false;\n"]}
@@ -103,7 +103,10 @@ export async function paraglideMiddleware(request, resolve, options) {
103
103
  return /** @type {Response} */ (await requestAsyncLocalStorage?.run({ locale, origin, messageCalls }, () => resolve({ locale, request: newRequest })));
104
104
  }
105
105
  const strategy = runtime.getStrategyForUrl(url.href);
106
- const decision = await runtime.shouldRedirect({ request, effectiveRequestUrl: url });
106
+ const decision = await runtime.shouldRedirect({
107
+ request,
108
+ effectiveRequestUrl: url,
109
+ });
107
110
  const locale = decision.locale;
108
111
  // if the client makes a request to a URL that doesn't match
109
112
  // the localizedUrl, redirect the client to the localized URL
@@ -188,7 +191,8 @@ function resolveMiddlewareUrl(request, effectiveRequestUrl) {
188
191
  if (typeof effectiveRequestUrl === "function") {
189
192
  return new URL(effectiveRequestUrl(request), request.url);
190
193
  }
191
- if (typeof effectiveRequestUrl === "string" || effectiveRequestUrl instanceof URL) {
194
+ if (typeof effectiveRequestUrl === "string" ||
195
+ effectiveRequestUrl instanceof URL) {
192
196
  return new URL(effectiveRequestUrl, request.url);
193
197
  }
194
198
  return new URL(request.url);
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/compiler/server/middleware.js"],"names":[],"mappings":"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","sourcesContent":["import * as runtime from \"./runtime.js\";\n\n/**\n * Server middleware that handles locale-based routing and request processing.\n *\n * Configure `disableAsyncLocalStorage` when generating Paraglide with\n * `paraglideVitePlugin()` or `compile()`, not when calling\n * `paraglideMiddleware()`. Keep AsyncLocalStorage enabled by default and\n * only disable it for runtimes that lack `AsyncLocalStorage` support and\n * guarantee request isolation.\n *\n * This middleware performs several key functions:\n *\n * 1. Determines the locale for the incoming request using configured strategies\n * 2. Handles URL localization and redirects (only for document requests)\n * 3. Maintains locale state using AsyncLocalStorage to prevent request interference\n *\n * When URL strategy is used:\n *\n * - The locale is extracted from the URL for all request types\n * - If URL doesn't match the determined locale, redirects to localized URL (only for document requests)\n * - De-localizes URLs before passing to server (e.g., `/fr/about` → `/about`)\n *\n * @see https://paraglidejs.com/middleware\n *\n * @template T - The return type of the resolve function\n *\n * @param {Request} request - The incoming request object\n * @param {(args: { request: Request, locale: import(\"./runtime.js\").Locale }) => T | Promise<T>} resolve - Function to handle the request. The callback receives:\n * - `request`: A modified request with a delocalized URL when the URL strategy is used (e.g., `/fr/about` → `/about`).\n * If your framework handles URL localization itself (e.g., TanStack Router's `rewrite` option), use the original\n * request instead to avoid redirect loops.\n * - `locale`: The determined locale for this request.\n * @param {{\n * effectiveRequestUrl?: string | URL | ((request: Request) => string | URL),\n * onRedirect?: (response: Response) => void\n * }} [options] - Options to control middleware behavior. `effectiveRequestUrl` sets the effective request URL used for route matching, URL-based locale detection, redirects, and `getUrlOrigin()`.\n * @returns {Promise<Response>}\n *\n * @example\n * ```typescript\n * // Basic usage in metaframeworks like NextJS, SvelteKit, Astro, Nuxt, etc.\n * export const handle = async ({ event, resolve }) => {\n * return paraglideMiddleware(event.request, ({ request, locale }) => {\n * // let the framework further resolve the request\n * return resolve(request);\n * });\n * };\n * ```\n *\n * @example\n * ```typescript\n * // Usage in a framework like Express JS or Hono\n * app.use(async (req, res, next) => {\n * const result = await paraglideMiddleware(req, ({ request, locale }) => {\n * // If a redirect happens this won't be called\n * return next(request);\n * });\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Usage with frameworks that handle URL localization/delocalization themselves\n * //\n * // Some frameworks like TanStack Router handle URL localization and delocalization\n * // themselves via their own rewrite APIs (e.g., `rewrite.input`/`rewrite.output`).\n * //\n * // When the framework handles this, the middleware's URL delocalization is not needed.\n * // Using the modified `request` from the callback would cause a redirect loop because\n * // both the middleware and the framework would attempt to delocalize the URL.\n * //\n * // Solution: Pass the original request to the handler instead of the modified one.\n * // The middleware still handles locale detection, cookies, and AsyncLocalStorage context.\n * //\n * // ❌ WRONG - causes redirect loop when framework handles URL rewriting:\n * // paraglideMiddleware(req, ({ request }) => handler.fetch(request))\n * //\n * // ✅ CORRECT - use original request when framework handles URL localization:\n * // paraglideMiddleware(req, () => handler.fetch(req))\n *\n * import { paraglideMiddleware } from './paraglide/server.js'\n * import handler from '@tanstack/react-start/server-entry'\n *\n * export default {\n * fetch(req: Request): Promise<Response> {\n * // TanStack Router handles URL rewriting via deLocalizeUrl/localizeUrl\n * // so we pass the original `req` instead of the modified `request`\n * return paraglideMiddleware(req, () => handler.fetch(req))\n * },\n * }\n * ```\n */\nexport async function paraglideMiddleware(request, resolve, options) {\n\tlet requestAsyncLocalStorage = runtime.serverAsyncLocalStorage;\n\t// %async-local-storage\n\tconst url = resolveMiddlewareUrl(request, options?.effectiveRequestUrl);\n\tconst origin = url.origin;\n\n\tif (runtime.isExcludedByRouteStrategy(url.href)) {\n\t\tconst locale = runtime.baseLocale;\n\t\tconst newRequest = cloneRequestWithFallback(request, url);\n\t\t/** @type {Set<string>} */\n\t\tconst messageCalls = new Set();\n\t\treturn /** @type {Response} */ (\n\t\t\tawait requestAsyncLocalStorage?.run(\n\t\t\t\t{ locale, origin, messageCalls },\n\t\t\t\t() => resolve({ locale, request: newRequest })\n\t\t\t)\n\t\t);\n\t}\n\n\tconst strategy = runtime.getStrategyForUrl(url.href);\n\tconst decision = await runtime.shouldRedirect({ request, effectiveRequestUrl: url });\n\tconst locale = decision.locale;\n\n\t// if the client makes a request to a URL that doesn't match\n\t// the localizedUrl, redirect the client to the localized URL\n\tif (\n\t\trequest.headers.get(\"Sec-Fetch-Dest\") === \"document\" &&\n\t\tdecision.shouldRedirect &&\n\t\tdecision.redirectUrl\n\t) {\n\t\t// Create headers object with Vary header if preferredLanguage strategy is used\n\t\t/** @type {Record<string, string>} */\n\t\tconst headers = {};\n\t\tif (strategy.includes(\"preferredLanguage\")) {\n\t\t\theaders[\"Vary\"] = \"Accept-Language\";\n\t\t}\n\n\t\tconst response = new Response(null, {\n\t\t\tstatus: 307,\n\t\t\theaders: {\n\t\t\t\tLocation: decision.redirectUrl.href,\n\t\t\t\t...headers,\n\t\t\t},\n\t\t});\n\n\t\toptions?.onRedirect?.(response);\n\t\treturn response;\n\t}\n\n\t// If the strategy includes \"url\", we need to de-localize the URL\n\t// before passing it to the server middleware.\n\t//\n\t// The middleware is responsible for mapping a localized URL to the\n\t// de-localized URL e.g. `/en/about` to `/about`. Otherwise,\n\t// the server can't render the correct page.\n\tlet newRequest;\n\tif (strategy.includes(\"url\")) {\n\t\tnewRequest = cloneRequestWithFallback(request, runtime.deLocalizeUrl(url));\n\t} else {\n\t\tnewRequest = cloneRequestWithFallback(request, url);\n\t}\n\n\t// the message functions that have been called in this request\n\t/** @type {Set<string>} */\n\tconst messageCalls = new Set();\n\n\tconst response = await requestAsyncLocalStorage?.run(\n\t\t{ locale, origin, messageCalls },\n\t\t() => resolve({ locale, request: newRequest })\n\t);\n\n\t// Only modify HTML responses\n\tif (\n\t\truntime.experimentalMiddlewareLocaleSplitting &&\n\t\tresponse.headers.get(\"Content-Type\")?.includes(\"html\")\n\t) {\n\t\tconst body = await response.text();\n\n\t\tconst messages = [];\n\n\t\t// using .values() to avoid polyfilling in older projects. else the following error is thrown\n\t\t// Type 'Set<string>' can only be iterated through when using the '--downlevelIteration' flag or with a '--target' of 'es2015' or higher.\n\t\tfor (const messageCall of Array.from(messageCalls)) {\n\t\t\tconst [id, locale] =\n\t\t\t\t/** @type {[string, import(\"./runtime.js\").Locale]} */ (\n\t\t\t\t\tmessageCall.split(\":\")\n\t\t\t\t);\n\t\t\tmessages.push(`${id}: ${compiledBundles[id]?.[locale]}`);\n\t\t}\n\n\t\t// Prevent translated content from terminating the inline script tag.\n\t\tconst escapedMessages = messages\n\t\t\t.join(\",\")\n\t\t\t.replace(/<\\/(script)/gi, \"<\\\\/$1\");\n\t\t// Reuse the request's CSP nonce (if any) so the injected script is allowed under a strict CSP\n\t\tconst nonce = response.headers\n\t\t\t.get(\"Content-Security-Policy\")\n\t\t\t?.match(/'nonce-([\\w+/=-]+)'/)?.[1];\n\t\tconst nonceAttr = nonce ? `nonce=\"${nonce}\"` : \"\";\n\t\tconst script = `<script ${nonceAttr}>globalThis.__paraglide = globalThis.__paraglide ?? {}; globalThis.__paraglide.ssr = { ${escapedMessages} }</script>`;\n\n\t\t// Insert the script before the closing head tag\n\t\tconst newBody = body.replace(\"</head>\", `${script}</head>`);\n\n\t\t// Create a new response with the modified body\n\t\t// Clone all headers except Content-Length which will be set automatically\n\t\tconst newHeaders = new Headers(response.headers);\n\t\tnewHeaders.delete(\"Content-Length\"); // Let the browser calculate the correct length\n\n\t\treturn new Response(newBody, {\n\t\t\tstatus: response.status,\n\t\t\tstatusText: response.statusText,\n\t\t\theaders: newHeaders,\n\t\t});\n\t}\n\n\treturn response;\n}\n\n/**\n * @param {Request} request\n * @param {string | URL | ((request: Request) => string | URL) | undefined} effectiveRequestUrl\n * @returns {URL}\n */\nfunction resolveMiddlewareUrl(request, effectiveRequestUrl) {\n\tif (typeof effectiveRequestUrl === \"function\") {\n\t\treturn new URL(effectiveRequestUrl(request), request.url);\n\t}\n\n\tif (typeof effectiveRequestUrl === \"string\" || effectiveRequestUrl instanceof URL) {\n\t\treturn new URL(effectiveRequestUrl, request.url);\n\t}\n\n\treturn new URL(request.url);\n}\n\n/**\n * Some metaframeworks (NextJS) require a new Request object.\n * https://github.com/opral/inlang-paraglide-js/issues/411\n *\n * However, some frameworks (TanStack Start 1.143+) use custom Request\n * implementations that cannot be cloned with `new Request(request)`.\n * https://github.com/opral/paraglide-js/issues/573\n *\n * Effective request URL overrides behind proxies:\n * https://github.com/opral/paraglide-js/issues/652\n *\n * @param {Request} request\n * @param {string | URL} [url]\n * @returns {Request}\n */\nfunction cloneRequestWithFallback(request, url = request.url) {\n\tconst targetUrl = typeof url === \"string\" ? url : url.href;\n\tif (targetUrl === request.url) {\n\t\ttry {\n\t\t\t// Clone first so building a new Request does not consume the original body stream.\n\t\t\treturn new Request(request.clone());\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\treturn new Request(request);\n\t\t\t} catch {\n\t\t\t\treturn request;\n\t\t\t}\n\t\t}\n\t}\n\n\ttry {\n\t\t// Clone first so building a new Request does not consume the original body stream.\n\t\treturn new Request(targetUrl, request.clone());\n\t} catch {\n\t\ttry {\n\t\t\treturn new Request(targetUrl, request);\n\t\t} catch {\n\t\t\treturn request;\n\t\t}\n\t}\n}\n\n/**\n * Creates a mock AsyncLocalStorage implementation for environments where\n * native AsyncLocalStorage is not available or disabled.\n *\n * This mock implementation mimics the behavior of the native AsyncLocalStorage\n * but doesn't require the async_hooks module. It's used as a fallback when\n * the runtime does not expose AsyncLocalStorage or when it has been disabled.\n *\n * @returns {import(\"./runtime.js\").ParaglideAsyncLocalStorage}\n */\nfunction createMockAsyncLocalStorage() {\n\t/** @type {any} */\n\tlet currentStore = undefined;\n\treturn {\n\t\tgetStore() {\n\t\t\treturn currentStore;\n\t\t},\n\t\tasync run(store, callback) {\n\t\t\tcurrentStore = store;\n\t\t\ttry {\n\t\t\t\treturn await callback();\n\t\t\t} finally {\n\t\t\t\tcurrentStore = undefined;\n\t\t\t}\n\t\t},\n\t};\n}\n\n// Used in generated server.js when async local storage is disabled.\nvoid createMockAsyncLocalStorage;\n\n/**\n * The compiled messages for the server middleware.\n *\n * Only populated if `enableMiddlewareOptimizations` is set to `true`.\n *\n * @type {Record<string, Record<import(\"./runtime.js\").Locale, string>>}\n */\nconst compiledBundles = {};\n"]}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/compiler/server/middleware.js"],"names":[],"mappings":"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","sourcesContent":["import * as runtime from \"./runtime.js\";\n\n/**\n * Server middleware that handles locale-based routing and request processing.\n *\n * Configure `disableAsyncLocalStorage` when generating Paraglide with\n * `paraglideVitePlugin()` or `compile()`, not when calling\n * `paraglideMiddleware()`. Keep AsyncLocalStorage enabled by default and\n * only disable it for runtimes that lack `AsyncLocalStorage` support and\n * guarantee request isolation.\n *\n * This middleware performs several key functions:\n *\n * 1. Determines the locale for the incoming request using configured strategies\n * 2. Handles URL localization and redirects (only for document requests)\n * 3. Maintains locale state using AsyncLocalStorage to prevent request interference\n *\n * When URL strategy is used:\n *\n * - The locale is extracted from the URL for all request types\n * - If URL doesn't match the determined locale, redirects to localized URL (only for document requests)\n * - De-localizes URLs before passing to server (e.g., `/fr/about` → `/about`)\n *\n * @see https://paraglidejs.com/middleware\n *\n * @template T - The return type of the resolve function\n *\n * @param {Request} request - The incoming request object\n * @param {(args: { request: Request, locale: import(\"./runtime.js\").Locale }) => T | Promise<T>} resolve - Function to handle the request. The callback receives:\n * - `request`: A modified request with a delocalized URL when the URL strategy is used (e.g., `/fr/about` → `/about`).\n * If your framework handles URL localization itself (e.g., TanStack Router's `rewrite` option), use the original\n * request instead to avoid redirect loops.\n * - `locale`: The determined locale for this request.\n * @param {{\n * effectiveRequestUrl?: string | URL | ((request: Request) => string | URL),\n * onRedirect?: (response: Response) => void\n * }} [options] - Options to control middleware behavior. `effectiveRequestUrl` sets the effective request URL used for route matching, URL-based locale detection, redirects, and `getUrlOrigin()`.\n * @returns {Promise<Response>}\n *\n * @example\n * ```typescript\n * // Basic usage in metaframeworks like NextJS, SvelteKit, Astro, Nuxt, etc.\n * export const handle = async ({ event, resolve }) => {\n * return paraglideMiddleware(event.request, ({ request, locale }) => {\n * // let the framework further resolve the request\n * return resolve(request);\n * });\n * };\n * ```\n *\n * @example\n * ```typescript\n * // Usage in a framework like Express JS or Hono\n * app.use(async (req, res, next) => {\n * const result = await paraglideMiddleware(req, ({ request, locale }) => {\n * // If a redirect happens this won't be called\n * return next(request);\n * });\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Usage with frameworks that handle URL localization/delocalization themselves\n * //\n * // Some frameworks like TanStack Router handle URL localization and delocalization\n * // themselves via their own rewrite APIs (e.g., `rewrite.input`/`rewrite.output`).\n * //\n * // When the framework handles this, the middleware's URL delocalization is not needed.\n * // Using the modified `request` from the callback would cause a redirect loop because\n * // both the middleware and the framework would attempt to delocalize the URL.\n * //\n * // Solution: Pass the original request to the handler instead of the modified one.\n * // The middleware still handles locale detection, cookies, and AsyncLocalStorage context.\n * //\n * // ❌ WRONG - causes redirect loop when framework handles URL rewriting:\n * // paraglideMiddleware(req, ({ request }) => handler.fetch(request))\n * //\n * // ✅ CORRECT - use original request when framework handles URL localization:\n * // paraglideMiddleware(req, () => handler.fetch(req))\n *\n * import { paraglideMiddleware } from './paraglide/server.js'\n * import handler from '@tanstack/react-start/server-entry'\n *\n * export default {\n * fetch(req: Request): Promise<Response> {\n * // TanStack Router handles URL rewriting via deLocalizeUrl/localizeUrl\n * // so we pass the original `req` instead of the modified `request`\n * return paraglideMiddleware(req, () => handler.fetch(req))\n * },\n * }\n * ```\n */\nexport async function paraglideMiddleware(request, resolve, options) {\n\tlet requestAsyncLocalStorage = runtime.serverAsyncLocalStorage;\n\t// %async-local-storage\n\tconst url = resolveMiddlewareUrl(request, options?.effectiveRequestUrl);\n\tconst origin = url.origin;\n\n\tif (runtime.isExcludedByRouteStrategy(url.href)) {\n\t\tconst locale = runtime.baseLocale;\n\t\tconst newRequest = cloneRequestWithFallback(request, url);\n\t\t/** @type {Set<string>} */\n\t\tconst messageCalls = new Set();\n\t\treturn /** @type {Response} */ (\n\t\t\tawait requestAsyncLocalStorage?.run(\n\t\t\t\t{ locale, origin, messageCalls },\n\t\t\t\t() => resolve({ locale, request: newRequest })\n\t\t\t)\n\t\t);\n\t}\n\n\tconst strategy = runtime.getStrategyForUrl(url.href);\n\tconst decision = await runtime.shouldRedirect({\n\t\trequest,\n\t\teffectiveRequestUrl: url,\n\t});\n\tconst locale = decision.locale;\n\n\t// if the client makes a request to a URL that doesn't match\n\t// the localizedUrl, redirect the client to the localized URL\n\tif (\n\t\trequest.headers.get(\"Sec-Fetch-Dest\") === \"document\" &&\n\t\tdecision.shouldRedirect &&\n\t\tdecision.redirectUrl\n\t) {\n\t\t// Create headers object with Vary header if preferredLanguage strategy is used\n\t\t/** @type {Record<string, string>} */\n\t\tconst headers = {};\n\t\tif (strategy.includes(\"preferredLanguage\")) {\n\t\t\theaders[\"Vary\"] = \"Accept-Language\";\n\t\t}\n\n\t\tconst response = new Response(null, {\n\t\t\tstatus: 307,\n\t\t\theaders: {\n\t\t\t\tLocation: decision.redirectUrl.href,\n\t\t\t\t...headers,\n\t\t\t},\n\t\t});\n\n\t\toptions?.onRedirect?.(response);\n\t\treturn response;\n\t}\n\n\t// If the strategy includes \"url\", we need to de-localize the URL\n\t// before passing it to the server middleware.\n\t//\n\t// The middleware is responsible for mapping a localized URL to the\n\t// de-localized URL e.g. `/en/about` to `/about`. Otherwise,\n\t// the server can't render the correct page.\n\tlet newRequest;\n\tif (strategy.includes(\"url\")) {\n\t\tnewRequest = cloneRequestWithFallback(request, runtime.deLocalizeUrl(url));\n\t} else {\n\t\tnewRequest = cloneRequestWithFallback(request, url);\n\t}\n\n\t// the message functions that have been called in this request\n\t/** @type {Set<string>} */\n\tconst messageCalls = new Set();\n\n\tconst response = await requestAsyncLocalStorage?.run(\n\t\t{ locale, origin, messageCalls },\n\t\t() => resolve({ locale, request: newRequest })\n\t);\n\n\t// Only modify HTML responses\n\tif (\n\t\truntime.experimentalMiddlewareLocaleSplitting &&\n\t\tresponse.headers.get(\"Content-Type\")?.includes(\"html\")\n\t) {\n\t\tconst body = await response.text();\n\n\t\tconst messages = [];\n\n\t\t// using .values() to avoid polyfilling in older projects. else the following error is thrown\n\t\t// Type 'Set<string>' can only be iterated through when using the '--downlevelIteration' flag or with a '--target' of 'es2015' or higher.\n\t\tfor (const messageCall of Array.from(messageCalls)) {\n\t\t\tconst [id, locale] =\n\t\t\t\t/** @type {[string, import(\"./runtime.js\").Locale]} */ (\n\t\t\t\t\tmessageCall.split(\":\")\n\t\t\t\t);\n\t\t\tmessages.push(`${id}: ${compiledBundles[id]?.[locale]}`);\n\t\t}\n\n\t\t// Prevent translated content from terminating the inline script tag.\n\t\tconst escapedMessages = messages\n\t\t\t.join(\",\")\n\t\t\t.replace(/<\\/(script)/gi, \"<\\\\/$1\");\n\t\t// Reuse the request's CSP nonce (if any) so the injected script is allowed under a strict CSP\n\t\tconst nonce = response.headers\n\t\t\t.get(\"Content-Security-Policy\")\n\t\t\t?.match(/'nonce-([\\w+/=-]+)'/)?.[1];\n\t\tconst nonceAttr = nonce ? `nonce=\"${nonce}\"` : \"\";\n\t\tconst script = `<script ${nonceAttr}>globalThis.__paraglide = globalThis.__paraglide ?? {}; globalThis.__paraglide.ssr = { ${escapedMessages} }</script>`;\n\n\t\t// Insert the script before the closing head tag\n\t\tconst newBody = body.replace(\"</head>\", `${script}</head>`);\n\n\t\t// Create a new response with the modified body\n\t\t// Clone all headers except Content-Length which will be set automatically\n\t\tconst newHeaders = new Headers(response.headers);\n\t\tnewHeaders.delete(\"Content-Length\"); // Let the browser calculate the correct length\n\n\t\treturn new Response(newBody, {\n\t\t\tstatus: response.status,\n\t\t\tstatusText: response.statusText,\n\t\t\theaders: newHeaders,\n\t\t});\n\t}\n\n\treturn response;\n}\n\n/**\n * @param {Request} request\n * @param {string | URL | ((request: Request) => string | URL) | undefined} effectiveRequestUrl\n * @returns {URL}\n */\nfunction resolveMiddlewareUrl(request, effectiveRequestUrl) {\n\tif (typeof effectiveRequestUrl === \"function\") {\n\t\treturn new URL(effectiveRequestUrl(request), request.url);\n\t}\n\n\tif (\n\t\ttypeof effectiveRequestUrl === \"string\" ||\n\t\teffectiveRequestUrl instanceof URL\n\t) {\n\t\treturn new URL(effectiveRequestUrl, request.url);\n\t}\n\n\treturn new URL(request.url);\n}\n\n/**\n * Some metaframeworks (NextJS) require a new Request object.\n * https://github.com/opral/inlang-paraglide-js/issues/411\n *\n * However, some frameworks (TanStack Start 1.143+) use custom Request\n * implementations that cannot be cloned with `new Request(request)`.\n * https://github.com/opral/paraglide-js/issues/573\n *\n * Effective request URL overrides behind proxies:\n * https://github.com/opral/paraglide-js/issues/652\n *\n * @param {Request} request\n * @param {string | URL} [url]\n * @returns {Request}\n */\nfunction cloneRequestWithFallback(request, url = request.url) {\n\tconst targetUrl = typeof url === \"string\" ? url : url.href;\n\tif (targetUrl === request.url) {\n\t\ttry {\n\t\t\t// Clone first so building a new Request does not consume the original body stream.\n\t\t\treturn new Request(request.clone());\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\treturn new Request(request);\n\t\t\t} catch {\n\t\t\t\treturn request;\n\t\t\t}\n\t\t}\n\t}\n\n\ttry {\n\t\t// Clone first so building a new Request does not consume the original body stream.\n\t\treturn new Request(targetUrl, request.clone());\n\t} catch {\n\t\ttry {\n\t\t\treturn new Request(targetUrl, request);\n\t\t} catch {\n\t\t\treturn request;\n\t\t}\n\t}\n}\n\n/**\n * Creates a mock AsyncLocalStorage implementation for environments where\n * native AsyncLocalStorage is not available or disabled.\n *\n * This mock implementation mimics the behavior of the native AsyncLocalStorage\n * but doesn't require the async_hooks module. It's used as a fallback when\n * the runtime does not expose AsyncLocalStorage or when it has been disabled.\n *\n * @returns {import(\"./runtime.js\").ParaglideAsyncLocalStorage}\n */\nfunction createMockAsyncLocalStorage() {\n\t/** @type {any} */\n\tlet currentStore = undefined;\n\treturn {\n\t\tgetStore() {\n\t\t\treturn currentStore;\n\t\t},\n\t\tasync run(store, callback) {\n\t\t\tcurrentStore = store;\n\t\t\ttry {\n\t\t\t\treturn await callback();\n\t\t\t} finally {\n\t\t\t\tcurrentStore = undefined;\n\t\t\t}\n\t\t},\n\t};\n}\n\n// Used in generated server.js when async local storage is disabled.\nvoid createMockAsyncLocalStorage;\n\n/**\n * The compiled messages for the server middleware.\n *\n * Only populated if `enableMiddlewareOptimizations` is set to `true`.\n *\n * @type {Record<string, Record<import(\"./runtime.js\").Locale, string>>}\n */\nconst compiledBundles = {};\n"]}
@@ -709,6 +709,40 @@ test("prevents redirect loops by normalizing URLs with trailing slashes in diffe
709
709
  expect(response.status).toBe(307);
710
710
  expect(response.headers.get("Location")).toBe("https://example.com/fr/different-page");
711
711
  });
712
+ // https://github.com/opral/paraglide-js/issues/473
713
+ test("canonicalizes translated pathnames with the configured trailing slash policy", async () => {
714
+ const runtime = await createParaglide({
715
+ blob: await newProject({
716
+ settings: {
717
+ baseLocale: "en",
718
+ locales: ["en", "de"],
719
+ },
720
+ }),
721
+ strategy: ["url", "baseLocale"],
722
+ trailingSlash: "never",
723
+ urlPatterns: [
724
+ {
725
+ pattern: "/about",
726
+ localized: [
727
+ ["en", "/about"],
728
+ ["de", "/de/ueber"],
729
+ ],
730
+ },
731
+ ],
732
+ });
733
+ const response = await runtime.paraglideMiddleware(new Request("https://example.com/de/ueber/", {
734
+ headers: { "Sec-Fetch-Dest": "document" },
735
+ }), () => {
736
+ throw new Error("canonical document requests should redirect first");
737
+ });
738
+ expect(response.status).toBe(307);
739
+ expect(response.headers.get("Location")).toBe("https://example.com/de/ueber");
740
+ await runtime.paraglideMiddleware(new Request("https://example.com/de/ueber/"), ({ locale, request }) => {
741
+ expect(locale).toBe("de");
742
+ expect(request.url).toBe("https://example.com/about");
743
+ return new Response();
744
+ });
745
+ });
712
746
  // not implemented because users should disable redirects by
713
747
  // making another strategy preceed the url strategy
714
748
  //