@rangojs/router 0.0.0-experimental.13 → 0.0.0-experimental.13221847

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 (298) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +884 -4
  3. package/dist/bin/rango.js +1531 -212
  4. package/dist/vite/index.js +3995 -2489
  5. package/package.json +57 -52
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +85 -23
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +12 -8
  11. package/skills/document-cache/SKILL.md +18 -16
  12. package/skills/fonts/SKILL.md +6 -4
  13. package/skills/hooks/SKILL.md +328 -70
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +131 -8
  16. package/skills/layout/SKILL.md +100 -3
  17. package/skills/links/SKILL.md +62 -15
  18. package/skills/loader/SKILL.md +368 -42
  19. package/skills/middleware/SKILL.md +171 -34
  20. package/skills/mime-routes/SKILL.md +14 -10
  21. package/skills/parallel/SKILL.md +137 -1
  22. package/skills/prerender/SKILL.md +366 -28
  23. package/skills/rango/SKILL.md +85 -21
  24. package/skills/response-routes/SKILL.md +136 -83
  25. package/skills/route/SKILL.md +195 -21
  26. package/skills/router-setup/SKILL.md +123 -30
  27. package/skills/theme/SKILL.md +9 -8
  28. package/skills/typesafety/SKILL.md +240 -102
  29. package/skills/use-cache/SKILL.md +324 -0
  30. package/src/__internal.ts +102 -4
  31. package/src/bin/rango.ts +312 -15
  32. package/src/browser/action-coordinator.ts +97 -0
  33. package/src/browser/action-response-classifier.ts +99 -0
  34. package/src/browser/event-controller.ts +92 -64
  35. package/src/browser/history-state.ts +80 -0
  36. package/src/browser/intercept-utils.ts +52 -0
  37. package/src/browser/link-interceptor.ts +24 -4
  38. package/src/browser/logging.ts +11 -0
  39. package/src/browser/merge-segment-loaders.ts +20 -12
  40. package/src/browser/navigation-bridge.ts +266 -558
  41. package/src/browser/navigation-client.ts +132 -75
  42. package/src/browser/navigation-store.ts +33 -50
  43. package/src/browser/navigation-transaction.ts +297 -0
  44. package/src/browser/network-error-handler.ts +61 -0
  45. package/src/browser/partial-update.ts +303 -309
  46. package/src/browser/prefetch/cache.ts +206 -0
  47. package/src/browser/prefetch/fetch.ts +144 -0
  48. package/src/browser/prefetch/observer.ts +65 -0
  49. package/src/browser/prefetch/policy.ts +48 -0
  50. package/src/browser/prefetch/queue.ts +128 -0
  51. package/src/browser/rango-state.ts +112 -0
  52. package/src/browser/react/Link.tsx +190 -70
  53. package/src/browser/react/NavigationProvider.tsx +78 -11
  54. package/src/browser/react/context.ts +6 -0
  55. package/src/browser/react/filter-segment-order.ts +11 -0
  56. package/src/browser/react/index.ts +12 -12
  57. package/src/browser/react/location-state-shared.ts +95 -53
  58. package/src/browser/react/location-state.ts +60 -15
  59. package/src/browser/react/mount-context.ts +6 -1
  60. package/src/browser/react/nonce-context.ts +23 -0
  61. package/src/browser/react/shallow-equal.ts +27 -0
  62. package/src/browser/react/use-action.ts +29 -51
  63. package/src/browser/react/use-client-cache.ts +5 -3
  64. package/src/browser/react/use-handle.ts +29 -70
  65. package/src/browser/react/use-link-status.ts +6 -5
  66. package/src/browser/react/use-navigation.ts +22 -63
  67. package/src/browser/react/use-params.ts +65 -0
  68. package/src/browser/react/use-pathname.ts +47 -0
  69. package/src/browser/react/use-router.ts +63 -0
  70. package/src/browser/react/use-search-params.ts +56 -0
  71. package/src/browser/react/use-segments.ts +80 -97
  72. package/src/browser/response-adapter.ts +73 -0
  73. package/src/browser/rsc-router.tsx +188 -57
  74. package/src/browser/scroll-restoration.ts +117 -44
  75. package/src/browser/segment-reconciler.ts +221 -0
  76. package/src/browser/segment-structure-assert.ts +16 -0
  77. package/src/browser/server-action-bridge.ts +488 -606
  78. package/src/browser/shallow.ts +6 -1
  79. package/src/browser/types.ts +116 -47
  80. package/src/browser/validate-redirect-origin.ts +29 -0
  81. package/src/build/generate-manifest.ts +63 -21
  82. package/src/build/generate-route-types.ts +36 -1038
  83. package/src/build/index.ts +2 -5
  84. package/src/build/route-trie.ts +38 -12
  85. package/src/build/route-types/ast-helpers.ts +25 -0
  86. package/src/build/route-types/ast-route-extraction.ts +98 -0
  87. package/src/build/route-types/codegen.ts +102 -0
  88. package/src/build/route-types/include-resolution.ts +411 -0
  89. package/src/build/route-types/param-extraction.ts +48 -0
  90. package/src/build/route-types/per-module-writer.ts +128 -0
  91. package/src/build/route-types/router-processing.ts +479 -0
  92. package/src/build/route-types/scan-filter.ts +78 -0
  93. package/src/build/runtime-discovery.ts +231 -0
  94. package/src/cache/background-task.ts +34 -0
  95. package/src/cache/cache-key-utils.ts +44 -0
  96. package/src/cache/cache-policy.ts +125 -0
  97. package/src/cache/cache-runtime.ts +342 -0
  98. package/src/cache/cache-scope.ts +122 -303
  99. package/src/cache/cf/cf-cache-store.ts +571 -17
  100. package/src/cache/cf/index.ts +13 -3
  101. package/src/cache/document-cache.ts +116 -77
  102. package/src/cache/handle-capture.ts +81 -0
  103. package/src/cache/handle-snapshot.ts +41 -0
  104. package/src/cache/index.ts +1 -15
  105. package/src/cache/memory-segment-store.ts +191 -13
  106. package/src/cache/profile-registry.ts +73 -0
  107. package/src/cache/read-through-swr.ts +134 -0
  108. package/src/cache/segment-codec.ts +256 -0
  109. package/src/cache/taint.ts +98 -0
  110. package/src/cache/types.ts +72 -122
  111. package/src/client.rsc.tsx +3 -1
  112. package/src/client.tsx +84 -126
  113. package/src/component-utils.ts +4 -4
  114. package/src/components/DefaultDocument.tsx +5 -1
  115. package/src/context-var.ts +86 -0
  116. package/src/debug.ts +19 -9
  117. package/src/errors.ts +77 -7
  118. package/src/handle.ts +12 -7
  119. package/src/handles/MetaTags.tsx +73 -20
  120. package/src/handles/breadcrumbs.ts +66 -0
  121. package/src/handles/index.ts +1 -0
  122. package/src/handles/meta.ts +30 -13
  123. package/src/host/cookie-handler.ts +21 -15
  124. package/src/host/errors.ts +8 -8
  125. package/src/host/index.ts +4 -7
  126. package/src/host/pattern-matcher.ts +27 -27
  127. package/src/host/router.ts +61 -39
  128. package/src/host/testing.ts +8 -8
  129. package/src/host/types.ts +15 -7
  130. package/src/host/utils.ts +1 -1
  131. package/src/href-client.ts +65 -45
  132. package/src/index.rsc.ts +104 -40
  133. package/src/index.ts +122 -67
  134. package/src/internal-debug.ts +9 -3
  135. package/src/loader.rsc.ts +18 -93
  136. package/src/loader.ts +26 -9
  137. package/src/network-error-thrower.tsx +3 -1
  138. package/src/outlet-provider.tsx +45 -0
  139. package/src/prerender/param-hash.ts +4 -2
  140. package/src/prerender/store.ts +121 -17
  141. package/src/prerender.ts +325 -20
  142. package/src/reverse.ts +144 -124
  143. package/src/root-error-boundary.tsx +41 -29
  144. package/src/route-content-wrapper.tsx +7 -4
  145. package/src/route-definition/dsl-helpers.ts +959 -0
  146. package/src/route-definition/helper-factories.ts +200 -0
  147. package/src/route-definition/helpers-types.ts +430 -0
  148. package/src/route-definition/index.ts +52 -0
  149. package/src/route-definition/redirect.ts +93 -0
  150. package/src/route-definition.ts +1 -1450
  151. package/src/route-map-builder.ts +87 -133
  152. package/src/route-name.ts +53 -0
  153. package/src/route-types.ts +41 -6
  154. package/src/router/content-negotiation.ts +116 -0
  155. package/src/router/debug-manifest.ts +72 -0
  156. package/src/router/error-handling.ts +9 -9
  157. package/src/router/find-match.ts +160 -0
  158. package/src/router/handler-context.ts +324 -116
  159. package/src/router/intercept-resolution.ts +11 -4
  160. package/src/router/lazy-includes.ts +237 -0
  161. package/src/router/loader-resolution.ts +179 -133
  162. package/src/router/logging.ts +112 -6
  163. package/src/router/manifest.ts +58 -19
  164. package/src/router/match-api.ts +89 -88
  165. package/src/router/match-context.ts +4 -2
  166. package/src/router/match-handlers.ts +440 -0
  167. package/src/router/match-middleware/background-revalidation.ts +86 -89
  168. package/src/router/match-middleware/cache-lookup.ts +295 -49
  169. package/src/router/match-middleware/cache-store.ts +56 -13
  170. package/src/router/match-middleware/intercept-resolution.ts +45 -22
  171. package/src/router/match-middleware/segment-resolution.ts +20 -9
  172. package/src/router/match-pipelines.ts +10 -45
  173. package/src/router/match-result.ts +44 -21
  174. package/src/router/metrics.ts +240 -15
  175. package/src/router/middleware-cookies.ts +55 -0
  176. package/src/router/middleware-types.ts +222 -0
  177. package/src/router/middleware.ts +327 -369
  178. package/src/router/pattern-matching.ts +169 -31
  179. package/src/router/prerender-match.ts +402 -0
  180. package/src/router/preview-match.ts +170 -0
  181. package/src/router/revalidation.ts +105 -14
  182. package/src/router/router-context.ts +40 -21
  183. package/src/router/router-interfaces.ts +452 -0
  184. package/src/router/router-options.ts +592 -0
  185. package/src/router/router-registry.ts +24 -0
  186. package/src/router/segment-resolution/fresh.ts +677 -0
  187. package/src/router/segment-resolution/helpers.ts +263 -0
  188. package/src/router/segment-resolution/loader-cache.ts +199 -0
  189. package/src/router/segment-resolution/revalidation.ts +1296 -0
  190. package/src/router/segment-resolution/static-store.ts +67 -0
  191. package/src/router/segment-resolution.ts +21 -1354
  192. package/src/router/segment-wrappers.ts +291 -0
  193. package/src/router/telemetry-otel.ts +299 -0
  194. package/src/router/telemetry.ts +300 -0
  195. package/src/router/timeout.ts +148 -0
  196. package/src/router/trie-matching.ts +96 -29
  197. package/src/router/types.ts +15 -9
  198. package/src/router.ts +642 -2366
  199. package/src/rsc/handler-context.ts +45 -0
  200. package/src/rsc/handler.ts +639 -1027
  201. package/src/rsc/helpers.ts +140 -6
  202. package/src/rsc/index.ts +0 -20
  203. package/src/rsc/loader-fetch.ts +209 -0
  204. package/src/rsc/manifest-init.ts +86 -0
  205. package/src/rsc/nonce.ts +14 -0
  206. package/src/rsc/origin-guard.ts +141 -0
  207. package/src/rsc/progressive-enhancement.ts +379 -0
  208. package/src/rsc/response-error.ts +37 -0
  209. package/src/rsc/response-route-handler.ts +347 -0
  210. package/src/rsc/rsc-rendering.ts +237 -0
  211. package/src/rsc/runtime-warnings.ts +42 -0
  212. package/src/rsc/server-action.ts +348 -0
  213. package/src/rsc/ssr-setup.ts +128 -0
  214. package/src/rsc/types.ts +38 -11
  215. package/src/search-params.ts +66 -54
  216. package/src/segment-system.tsx +165 -17
  217. package/src/server/context.ts +237 -54
  218. package/src/server/cookie-store.ts +190 -0
  219. package/src/server/fetchable-loader-store.ts +11 -6
  220. package/src/server/handle-store.ts +94 -15
  221. package/src/server/loader-registry.ts +15 -56
  222. package/src/server/request-context.ts +438 -71
  223. package/src/server.ts +26 -164
  224. package/src/ssr/index.tsx +101 -31
  225. package/src/static-handler.ts +22 -4
  226. package/src/theme/ThemeProvider.tsx +21 -15
  227. package/src/theme/ThemeScript.tsx +5 -5
  228. package/src/theme/constants.ts +5 -2
  229. package/src/theme/index.ts +4 -14
  230. package/src/theme/theme-context.ts +4 -30
  231. package/src/theme/theme-script.ts +21 -18
  232. package/src/types/boundaries.ts +158 -0
  233. package/src/types/cache-types.ts +198 -0
  234. package/src/types/error-types.ts +192 -0
  235. package/src/types/global-namespace.ts +100 -0
  236. package/src/types/handler-context.ts +773 -0
  237. package/src/types/index.ts +88 -0
  238. package/src/types/loader-types.ts +183 -0
  239. package/src/types/route-config.ts +170 -0
  240. package/src/types/route-entry.ts +109 -0
  241. package/src/types/segments.ts +150 -0
  242. package/src/types.ts +1 -1795
  243. package/src/urls/include-helper.ts +197 -0
  244. package/src/urls/index.ts +53 -0
  245. package/src/urls/path-helper-types.ts +339 -0
  246. package/src/urls/path-helper.ts +329 -0
  247. package/src/urls/pattern-types.ts +95 -0
  248. package/src/urls/response-types.ts +106 -0
  249. package/src/urls/type-extraction.ts +372 -0
  250. package/src/urls/urls-function.ts +98 -0
  251. package/src/urls.ts +1 -1323
  252. package/src/use-loader.tsx +85 -77
  253. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  254. package/src/vite/discovery/discover-routers.ts +344 -0
  255. package/src/vite/discovery/prerender-collection.ts +385 -0
  256. package/src/vite/discovery/route-types-writer.ts +258 -0
  257. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  258. package/src/vite/discovery/state.ts +108 -0
  259. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  260. package/src/vite/index.ts +11 -2259
  261. package/src/vite/plugin-types.ts +48 -0
  262. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  263. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  264. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  265. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -47
  266. package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +8 -43
  267. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  268. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  269. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  270. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  271. package/src/vite/plugins/expose-ids/types.ts +45 -0
  272. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  273. package/src/vite/plugins/refresh-cmd.ts +65 -0
  274. package/src/vite/plugins/use-cache-transform.ts +323 -0
  275. package/src/vite/plugins/version-injector.ts +83 -0
  276. package/src/vite/plugins/version-plugin.ts +266 -0
  277. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  278. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  279. package/src/vite/rango.ts +445 -0
  280. package/src/vite/router-discovery.ts +777 -0
  281. package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +181 -9
  282. package/src/vite/utils/banner.ts +36 -0
  283. package/src/vite/utils/bundle-analysis.ts +137 -0
  284. package/src/vite/utils/manifest-utils.ts +70 -0
  285. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  286. package/src/vite/utils/prerender-utils.ts +189 -0
  287. package/src/vite/utils/shared-utils.ts +169 -0
  288. package/CLAUDE.md +0 -43
  289. package/dist/vite/index.named-routes.gen.ts +0 -103
  290. package/src/browser/lru-cache.ts +0 -69
  291. package/src/browser/request-controller.ts +0 -164
  292. package/src/cache/memory-store.ts +0 -253
  293. package/src/href-context.ts +0 -33
  294. package/src/router.gen.ts +0 -6
  295. package/src/static-handler.gen.ts +0 -5
  296. package/src/urls.gen.ts +0 -8
  297. package/src/vite/expose-internal-ids.ts +0 -1167
  298. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -16,6 +16,7 @@ export interface ParsedSegment {
16
16
  value: string; // static text, param name, or "*"
17
17
  optional: boolean;
18
18
  constraint?: string[]; // enum values like ["en", "gb"]
19
+ suffix?: string; // literal text after param in same segment (e.g., ".html")
19
20
  }
20
21
 
21
22
  /**
@@ -38,11 +39,22 @@ export function parsePattern(pattern: string): ParsedSegment[] {
38
39
  // - :param(a|b)
39
40
  // - :param(a|b)?
40
41
  // - *
41
- const segmentRegex = /\/(:([a-zA-Z_][a-zA-Z0-9_]*)(\(([^)]+)\))?(\?)?|(\*)|([^/]+))/g;
42
+ const segmentRegex =
43
+ /\/(:([a-zA-Z_][a-zA-Z0-9_]*)(\(([^)]+)\))?(\?)?([^/]*)|(\*)|([^/]+))/g;
42
44
 
43
45
  let match;
44
46
  while ((match = segmentRegex.exec(pattern)) !== null) {
45
- const [, , paramName, , constraint, optional, wildcard, staticText] = match;
47
+ const [
48
+ ,
49
+ ,
50
+ paramName,
51
+ ,
52
+ constraint,
53
+ optional,
54
+ suffix,
55
+ wildcard,
56
+ staticText,
57
+ ] = match;
46
58
 
47
59
  if (wildcard) {
48
60
  segments.push({ type: "wildcard", value: "*", optional: false });
@@ -52,6 +64,7 @@ export function parsePattern(pattern: string): ParsedSegment[] {
52
64
  value: paramName,
53
65
  optional: optional === "?",
54
66
  constraint: constraint ? constraint.split("|") : undefined,
67
+ suffix: suffix || undefined,
55
68
  });
56
69
  } else if (staticText) {
57
70
  segments.push({ type: "static", value: staticText, optional: false });
@@ -61,6 +74,48 @@ export function parsePattern(pattern: string): ParsedSegment[] {
61
74
  return segments;
62
75
  }
63
76
 
77
+ /**
78
+ * Compiled pattern result containing regex, param metadata, and trailing slash info.
79
+ */
80
+ export interface CompiledPattern {
81
+ regex: RegExp;
82
+ paramNames: string[];
83
+ optionalParams: Set<string>;
84
+ hasTrailingSlash: boolean;
85
+ }
86
+
87
+ // Module-level cache for compiled patterns. Route patterns are a finite set
88
+ // defined at build time, so this map is bounded by the number of routes.
89
+ const compiledPatternCache = new Map<string, CompiledPattern>();
90
+
91
+ /**
92
+ * Get a compiled pattern from cache or compile and cache it.
93
+ * Avoids O(routes) regex compilations per request in the fallback path.
94
+ */
95
+ export function getCompiledPattern(pattern: string): CompiledPattern {
96
+ let compiled = compiledPatternCache.get(pattern);
97
+ if (compiled) return compiled;
98
+ compiled = compilePattern(pattern);
99
+ compiledPatternCache.set(pattern, compiled);
100
+ return compiled;
101
+ }
102
+
103
+ /**
104
+ * Return the current size of the compiled pattern cache.
105
+ * Exposed for testing.
106
+ */
107
+ export function getPatternCacheSize(): number {
108
+ return compiledPatternCache.size;
109
+ }
110
+
111
+ /**
112
+ * Clear the compiled pattern cache.
113
+ * Exposed for testing.
114
+ */
115
+ export function clearPatternCache(): void {
116
+ compiledPatternCache.clear();
117
+ }
118
+
64
119
  /**
65
120
  * Compile a route pattern to regex
66
121
  *
@@ -78,12 +133,7 @@ export function parsePattern(pattern: string): ParsedSegment[] {
78
133
  * compilePattern("/:locale(en|gb)/blog") // matches /en/blog or /gb/blog
79
134
  * compilePattern("/:locale(en|gb)?/blog") // matches /blog, /en/blog, or /gb/blog
80
135
  */
81
- export function compilePattern(pattern: string): {
82
- regex: RegExp;
83
- paramNames: string[];
84
- optionalParams: Set<string>;
85
- hasTrailingSlash: boolean;
86
- } {
136
+ export function compilePattern(pattern: string): CompiledPattern {
87
137
  // Detect if pattern has trailing slash (but not just "/")
88
138
  const hasTrailingSlash = pattern.length > 1 && pattern.endsWith("/");
89
139
  // Remove trailing slash for parsing (we'll add it back to regex if needed)
@@ -101,16 +151,19 @@ export function compilePattern(pattern: string): {
101
151
  regexPattern += "/(.*)";
102
152
  } else if (segment.type === "param") {
103
153
  paramNames.push(segment.value);
154
+ const suffixPattern = segment.suffix ? escapeRegex(segment.suffix) : "";
104
155
  const valuePattern = segment.constraint
105
- ? `(${segment.constraint.join("|")})`
106
- : "([^/]+)";
156
+ ? `(${segment.constraint.map(escapeRegex).join("|")})`
157
+ : segment.suffix
158
+ ? "([^/]+?)"
159
+ : "([^/]+)";
107
160
 
108
161
  if (segment.optional) {
109
162
  optionalParams.add(segment.value);
110
163
  // Optional: make the whole /segment optional
111
- regexPattern += `(?:/${valuePattern})?`;
164
+ regexPattern += `(?:/${valuePattern}${suffixPattern})?`;
112
165
  } else {
113
- regexPattern += `/${valuePattern}`;
166
+ regexPattern += `/${valuePattern}${suffixPattern}`;
114
167
  }
115
168
  } else {
116
169
  // Static segment
@@ -240,7 +293,7 @@ export interface LazyEvaluationNeeded<TEnv = any> {
240
293
  * Type guard to check if result is a lazy evaluation needed response
241
294
  */
242
295
  export function isLazyEvaluationNeeded<TEnv>(
243
- result: RouteMatchResult<TEnv> | LazyEvaluationNeeded<TEnv> | null
296
+ result: RouteMatchResult<TEnv> | LazyEvaluationNeeded<TEnv> | null,
244
297
  ): result is LazyEvaluationNeeded<TEnv> {
245
298
  return result !== null && "lazyEntry" in result;
246
299
  }
@@ -261,12 +314,16 @@ export function enableMatchDebug(enabled: boolean): void {
261
314
  }
262
315
 
263
316
  export function getMatchDebugStats(): MatchDebugStats {
264
- return { entriesChecked: debugStats.entriesChecked, entriesSkipped: debugStats.entriesSkipped, routesChecked: debugStats.routesChecked };
317
+ return {
318
+ entriesChecked: debugStats.entriesChecked,
319
+ entriesSkipped: debugStats.entriesSkipped,
320
+ routesChecked: debugStats.routesChecked,
321
+ };
265
322
  }
266
323
 
267
324
  export function findMatch<TEnv>(
268
325
  pathname: string,
269
- routesEntries: RouteEntry<TEnv>[]
326
+ routesEntries: RouteEntry<TEnv>[],
270
327
  ): RouteMatchResult<TEnv> | LazyEvaluationNeeded<TEnv> | null {
271
328
  const effectiveDebug = debugEnabled || isRouterDebugEnabled();
272
329
 
@@ -282,7 +339,8 @@ export function findMatch<TEnv>(
282
339
  }
283
340
  }
284
341
 
285
- const pathnameHasTrailingSlash = pathname.length > 1 && pathname.endsWith("/");
342
+ const pathnameHasTrailingSlash =
343
+ pathname.length > 1 && pathname.endsWith("/");
286
344
  // Try alternate pathname for redirect matching
287
345
  const alternatePathname = pathnameHasTrailingSlash
288
346
  ? pathname.slice(0, -1)
@@ -334,14 +392,20 @@ export function findMatch<TEnv>(
334
392
  fullPattern = entry.prefix + pattern;
335
393
  }
336
394
 
337
- const { regex, paramNames, optionalParams, hasTrailingSlash } = compilePattern(fullPattern);
395
+ const { regex, paramNames, optionalParams, hasTrailingSlash } =
396
+ getCompiledPattern(fullPattern);
338
397
 
339
398
  // Get trailing slash mode for this route (per-route config or pattern-based)
340
- const trailingSlashMode: TrailingSlashMode | undefined = entry.trailingSlash?.[routeKey];
341
-
399
+ const trailingSlashMode: TrailingSlashMode | undefined =
400
+ entry.trailingSlash?.[routeKey];
342
401
 
343
402
  // Prerender flag from entry metadata (set by urls() for prerender handlers)
344
- const prFlag = entry.prerenderRouteKeys?.has(routeKey) ? { pr: true as const } : {};
403
+ const prFlag = entry.prerenderRouteKeys?.has(routeKey)
404
+ ? { pr: true as const }
405
+ : {};
406
+ const ptFlag = entry.passthroughRouteKeys?.has(routeKey)
407
+ ? { pt: true as const }
408
+ : {};
345
409
 
346
410
  // Try exact match first
347
411
  const match = regex.exec(pathname);
@@ -360,15 +424,42 @@ export function findMatch<TEnv>(
360
424
  }
361
425
 
362
426
  // Check if trailing slash mode requires redirect even on exact match
363
- if (trailingSlashMode === "always" && !pathnameHasTrailingSlash && pathname !== "/") {
427
+ if (
428
+ trailingSlashMode === "always" &&
429
+ !pathnameHasTrailingSlash &&
430
+ pathname !== "/"
431
+ ) {
364
432
  // Mode says always have trailing slash, but pathname doesn't have it
365
- return { entry, routeKey, params, optionalParams, redirectTo: pathname + "/", ...prFlag };
433
+ return {
434
+ entry,
435
+ routeKey,
436
+ params,
437
+ optionalParams,
438
+ redirectTo: pathname + "/",
439
+ ...prFlag,
440
+ ...ptFlag,
441
+ };
366
442
  } else if (trailingSlashMode === "never" && pathnameHasTrailingSlash) {
367
443
  // Mode says never have trailing slash, but pathname has it
368
- return { entry, routeKey, params, optionalParams, redirectTo: pathname.slice(0, -1), ...prFlag };
444
+ return {
445
+ entry,
446
+ routeKey,
447
+ params,
448
+ optionalParams,
449
+ redirectTo: pathname.slice(0, -1),
450
+ ...prFlag,
451
+ ...ptFlag,
452
+ };
369
453
  }
370
454
 
371
- return { entry, routeKey, params, optionalParams, ...prFlag };
455
+ return {
456
+ entry,
457
+ routeKey,
458
+ params,
459
+ optionalParams,
460
+ ...prFlag,
461
+ ...ptFlag,
462
+ };
372
463
  }
373
464
 
374
465
  // Try alternate pathname (opposite trailing slash)
@@ -382,24 +473,71 @@ export function findMatch<TEnv>(
382
473
  // Determine redirect behavior based on mode
383
474
  if (trailingSlashMode === "ignore") {
384
475
  // Match without redirect
385
- return { entry, routeKey, params, optionalParams, ...prFlag };
476
+ return {
477
+ entry,
478
+ routeKey,
479
+ params,
480
+ optionalParams,
481
+ ...prFlag,
482
+ ...ptFlag,
483
+ };
386
484
  } else if (trailingSlashMode === "never") {
387
485
  // Redirect to no trailing slash
388
486
  if (pathnameHasTrailingSlash) {
389
- return { entry, routeKey, params, optionalParams, redirectTo: alternatePathname, ...prFlag };
487
+ return {
488
+ entry,
489
+ routeKey,
490
+ params,
491
+ optionalParams,
492
+ redirectTo: alternatePathname,
493
+ ...prFlag,
494
+ ...ptFlag,
495
+ };
390
496
  }
391
- return { entry, routeKey, params, optionalParams, ...prFlag };
497
+ return {
498
+ entry,
499
+ routeKey,
500
+ params,
501
+ optionalParams,
502
+ ...prFlag,
503
+ ...ptFlag,
504
+ };
392
505
  } else if (trailingSlashMode === "always") {
393
506
  // Redirect to with trailing slash
394
507
  if (!pathnameHasTrailingSlash) {
395
- return { entry, routeKey, params, optionalParams, redirectTo: alternatePathname, ...prFlag };
508
+ return {
509
+ entry,
510
+ routeKey,
511
+ params,
512
+ optionalParams,
513
+ redirectTo: alternatePathname,
514
+ ...prFlag,
515
+ ...ptFlag,
516
+ };
396
517
  }
397
- return { entry, routeKey, params, optionalParams, ...prFlag };
518
+ return {
519
+ entry,
520
+ routeKey,
521
+ params,
522
+ optionalParams,
523
+ ...prFlag,
524
+ ...ptFlag,
525
+ };
398
526
  } else {
399
527
  // No explicit mode - use pattern-based detection
400
528
  // Redirect to canonical form (what the pattern defines)
401
- const canonicalPath = hasTrailingSlash ? alternatePathname : pathname.slice(0, -1);
402
- return { entry, routeKey, params, optionalParams, redirectTo: canonicalPath, ...prFlag };
529
+ const canonicalPath = hasTrailingSlash
530
+ ? alternatePathname
531
+ : pathname.slice(0, -1);
532
+ return {
533
+ entry,
534
+ routeKey,
535
+ params,
536
+ optionalParams,
537
+ redirectTo: canonicalPath,
538
+ ...prFlag,
539
+ ...ptFlag,
540
+ };
403
541
  }
404
542
  }
405
543
  }