@rangojs/router 0.0.0-experimental.10 → 0.0.0-experimental.100

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 (329) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +1037 -4
  3. package/dist/bin/rango.js +1619 -157
  4. package/dist/vite/index.js +5762 -2301
  5. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  6. package/package.json +71 -63
  7. package/skills/breadcrumbs/SKILL.md +252 -0
  8. package/skills/cache-guide/SKILL.md +294 -0
  9. package/skills/caching/SKILL.md +93 -23
  10. package/skills/composability/SKILL.md +172 -0
  11. package/skills/debug-manifest/SKILL.md +12 -8
  12. package/skills/document-cache/SKILL.md +18 -16
  13. package/skills/fonts/SKILL.md +6 -4
  14. package/skills/handler-use/SKILL.md +364 -0
  15. package/skills/hooks/SKILL.md +367 -71
  16. package/skills/host-router/SKILL.md +218 -0
  17. package/skills/i18n/SKILL.md +276 -0
  18. package/skills/intercept/SKILL.md +176 -8
  19. package/skills/layout/SKILL.md +124 -3
  20. package/skills/links/SKILL.md +304 -25
  21. package/skills/loader/SKILL.md +474 -47
  22. package/skills/middleware/SKILL.md +207 -37
  23. package/skills/migrate-nextjs/SKILL.md +562 -0
  24. package/skills/migrate-react-router/SKILL.md +769 -0
  25. package/skills/mime-routes/SKILL.md +15 -11
  26. package/skills/parallel/SKILL.md +272 -1
  27. package/skills/prerender/SKILL.md +467 -65
  28. package/skills/rango/SKILL.md +89 -21
  29. package/skills/response-routes/SKILL.md +152 -91
  30. package/skills/route/SKILL.md +305 -14
  31. package/skills/router-setup/SKILL.md +210 -32
  32. package/skills/server-actions/SKILL.md +739 -0
  33. package/skills/streams-and-websockets/SKILL.md +283 -0
  34. package/skills/theme/SKILL.md +9 -8
  35. package/skills/typesafety/SKILL.md +333 -86
  36. package/skills/use-cache/SKILL.md +324 -0
  37. package/skills/view-transitions/SKILL.md +212 -0
  38. package/src/__internal.ts +102 -4
  39. package/src/bin/rango.ts +312 -15
  40. package/src/browser/action-coordinator.ts +97 -0
  41. package/src/browser/action-response-classifier.ts +99 -0
  42. package/src/browser/app-shell.ts +52 -0
  43. package/src/browser/app-version.ts +14 -0
  44. package/src/browser/event-controller.ts +136 -68
  45. package/src/browser/history-state.ts +80 -0
  46. package/src/browser/intercept-utils.ts +52 -0
  47. package/src/browser/link-interceptor.ts +24 -4
  48. package/src/browser/logging.ts +55 -0
  49. package/src/browser/merge-segment-loaders.ts +20 -12
  50. package/src/browser/navigation-bridge.ts +374 -561
  51. package/src/browser/navigation-client.ts +228 -70
  52. package/src/browser/navigation-store.ts +97 -55
  53. package/src/browser/navigation-transaction.ts +297 -0
  54. package/src/browser/network-error-handler.ts +61 -0
  55. package/src/browser/partial-update.ts +376 -315
  56. package/src/browser/prefetch/cache.ts +314 -0
  57. package/src/browser/prefetch/fetch.ts +282 -0
  58. package/src/browser/prefetch/observer.ts +65 -0
  59. package/src/browser/prefetch/policy.ts +48 -0
  60. package/src/browser/prefetch/queue.ts +191 -0
  61. package/src/browser/prefetch/resource-ready.ts +77 -0
  62. package/src/browser/rango-state.ts +152 -0
  63. package/src/browser/react/Link.tsx +255 -71
  64. package/src/browser/react/NavigationProvider.tsx +152 -24
  65. package/src/browser/react/context.ts +11 -0
  66. package/src/browser/react/filter-segment-order.ts +55 -0
  67. package/src/browser/react/index.ts +15 -12
  68. package/src/browser/react/location-state-shared.ts +95 -53
  69. package/src/browser/react/location-state.ts +60 -15
  70. package/src/browser/react/mount-context.ts +6 -1
  71. package/src/browser/react/nonce-context.ts +23 -0
  72. package/src/browser/react/shallow-equal.ts +27 -0
  73. package/src/browser/react/use-action.ts +29 -51
  74. package/src/browser/react/use-client-cache.ts +5 -3
  75. package/src/browser/react/use-handle.ts +30 -120
  76. package/src/browser/react/use-link-status.ts +6 -5
  77. package/src/browser/react/use-navigation.ts +44 -65
  78. package/src/browser/react/use-params.ts +78 -0
  79. package/src/browser/react/use-pathname.ts +47 -0
  80. package/src/browser/react/use-reverse.ts +99 -0
  81. package/src/browser/react/use-router.ts +83 -0
  82. package/src/browser/react/use-search-params.ts +56 -0
  83. package/src/browser/react/use-segments.ts +85 -99
  84. package/src/browser/response-adapter.ts +73 -0
  85. package/src/browser/rsc-router.tsx +246 -64
  86. package/src/browser/scroll-restoration.ts +127 -52
  87. package/src/browser/segment-reconciler.ts +243 -0
  88. package/src/browser/segment-structure-assert.ts +16 -0
  89. package/src/browser/server-action-bridge.ts +510 -603
  90. package/src/browser/shallow.ts +6 -1
  91. package/src/browser/types.ts +158 -48
  92. package/src/browser/validate-redirect-origin.ts +29 -0
  93. package/src/build/generate-manifest.ts +84 -23
  94. package/src/build/generate-route-types.ts +39 -828
  95. package/src/build/index.ts +4 -5
  96. package/src/build/route-trie.ts +85 -32
  97. package/src/build/route-types/ast-helpers.ts +25 -0
  98. package/src/build/route-types/ast-route-extraction.ts +98 -0
  99. package/src/build/route-types/codegen.ts +102 -0
  100. package/src/build/route-types/include-resolution.ts +418 -0
  101. package/src/build/route-types/param-extraction.ts +48 -0
  102. package/src/build/route-types/per-module-writer.ts +128 -0
  103. package/src/build/route-types/router-processing.ts +618 -0
  104. package/src/build/route-types/scan-filter.ts +85 -0
  105. package/src/build/runtime-discovery.ts +231 -0
  106. package/src/cache/background-task.ts +34 -0
  107. package/src/cache/cache-key-utils.ts +44 -0
  108. package/src/cache/cache-policy.ts +125 -0
  109. package/src/cache/cache-runtime.ts +342 -0
  110. package/src/cache/cache-scope.ts +167 -307
  111. package/src/cache/cf/cf-cache-store.ts +573 -21
  112. package/src/cache/cf/index.ts +13 -3
  113. package/src/cache/document-cache.ts +116 -77
  114. package/src/cache/handle-capture.ts +81 -0
  115. package/src/cache/handle-snapshot.ts +41 -0
  116. package/src/cache/index.ts +1 -15
  117. package/src/cache/memory-segment-store.ts +191 -13
  118. package/src/cache/profile-registry.ts +73 -0
  119. package/src/cache/read-through-swr.ts +134 -0
  120. package/src/cache/segment-codec.ts +256 -0
  121. package/src/cache/taint.ts +153 -0
  122. package/src/cache/types.ts +72 -122
  123. package/src/client.rsc.tsx +6 -1
  124. package/src/client.tsx +118 -302
  125. package/src/component-utils.ts +4 -4
  126. package/src/components/DefaultDocument.tsx +5 -1
  127. package/src/context-var.ts +156 -0
  128. package/src/debug.ts +19 -9
  129. package/src/errors.ts +77 -7
  130. package/src/handle.ts +55 -10
  131. package/src/handles/MetaTags.tsx +73 -20
  132. package/src/handles/breadcrumbs.ts +66 -0
  133. package/src/handles/index.ts +1 -0
  134. package/src/handles/meta.ts +30 -13
  135. package/src/host/cookie-handler.ts +21 -15
  136. package/src/host/errors.ts +8 -8
  137. package/src/host/index.ts +4 -7
  138. package/src/host/pattern-matcher.ts +27 -27
  139. package/src/host/router.ts +61 -39
  140. package/src/host/testing.ts +8 -8
  141. package/src/host/types.ts +15 -7
  142. package/src/host/utils.ts +1 -1
  143. package/src/href-client.ts +65 -45
  144. package/src/index.rsc.ts +138 -21
  145. package/src/index.ts +206 -51
  146. package/src/internal-debug.ts +11 -0
  147. package/src/loader.rsc.ts +25 -143
  148. package/src/loader.ts +27 -10
  149. package/src/network-error-thrower.tsx +3 -1
  150. package/src/outlet-context.ts +1 -1
  151. package/src/outlet-provider.tsx +45 -0
  152. package/src/prerender/param-hash.ts +4 -2
  153. package/src/prerender/store.ts +159 -13
  154. package/src/prerender.ts +397 -29
  155. package/src/response-utils.ts +28 -0
  156. package/src/reverse.ts +231 -121
  157. package/src/root-error-boundary.tsx +41 -29
  158. package/src/route-content-wrapper.tsx +7 -4
  159. package/src/route-definition/dsl-helpers.ts +1134 -0
  160. package/src/route-definition/helper-factories.ts +200 -0
  161. package/src/route-definition/helpers-types.ts +483 -0
  162. package/src/route-definition/index.ts +55 -0
  163. package/src/route-definition/redirect.ts +101 -0
  164. package/src/route-definition/resolve-handler-use.ts +155 -0
  165. package/src/route-definition.ts +1 -1431
  166. package/src/route-map-builder.ts +162 -123
  167. package/src/route-name.ts +53 -0
  168. package/src/route-types.ts +66 -9
  169. package/src/router/content-negotiation.ts +215 -0
  170. package/src/router/debug-manifest.ts +72 -0
  171. package/src/router/error-handling.ts +9 -9
  172. package/src/router/find-match.ts +160 -0
  173. package/src/router/handler-context.ts +418 -86
  174. package/src/router/intercept-resolution.ts +35 -20
  175. package/src/router/lazy-includes.ts +237 -0
  176. package/src/router/loader-resolution.ts +359 -128
  177. package/src/router/logging.ts +251 -0
  178. package/src/router/manifest.ts +98 -32
  179. package/src/router/match-api.ts +196 -261
  180. package/src/router/match-context.ts +4 -2
  181. package/src/router/match-handlers.ts +441 -0
  182. package/src/router/match-middleware/background-revalidation.ts +108 -93
  183. package/src/router/match-middleware/cache-lookup.ts +415 -86
  184. package/src/router/match-middleware/cache-store.ts +91 -29
  185. package/src/router/match-middleware/intercept-resolution.ts +48 -21
  186. package/src/router/match-middleware/segment-resolution.ts +73 -9
  187. package/src/router/match-pipelines.ts +10 -45
  188. package/src/router/match-result.ts +154 -35
  189. package/src/router/metrics.ts +240 -15
  190. package/src/router/middleware-cookies.ts +55 -0
  191. package/src/router/middleware-types.ts +209 -0
  192. package/src/router/middleware.ts +373 -371
  193. package/src/router/navigation-snapshot.ts +182 -0
  194. package/src/router/pattern-matching.ts +292 -52
  195. package/src/router/prerender-match.ts +502 -0
  196. package/src/router/preview-match.ts +98 -0
  197. package/src/router/request-classification.ts +310 -0
  198. package/src/router/revalidation.ts +152 -39
  199. package/src/router/route-snapshot.ts +245 -0
  200. package/src/router/router-context.ts +41 -21
  201. package/src/router/router-interfaces.ts +484 -0
  202. package/src/router/router-options.ts +618 -0
  203. package/src/router/router-registry.ts +24 -0
  204. package/src/router/segment-resolution/fresh.ts +756 -0
  205. package/src/router/segment-resolution/helpers.ts +268 -0
  206. package/src/router/segment-resolution/loader-cache.ts +199 -0
  207. package/src/router/segment-resolution/revalidation.ts +1407 -0
  208. package/src/router/segment-resolution/static-store.ts +67 -0
  209. package/src/router/segment-resolution.ts +21 -1315
  210. package/src/router/segment-wrappers.ts +291 -0
  211. package/src/router/substitute-pattern-params.ts +56 -0
  212. package/src/router/telemetry-otel.ts +299 -0
  213. package/src/router/telemetry.ts +300 -0
  214. package/src/router/timeout.ts +148 -0
  215. package/src/router/trie-matching.ts +111 -39
  216. package/src/router/types.ts +17 -9
  217. package/src/router/url-params.ts +49 -0
  218. package/src/router.ts +642 -2011
  219. package/src/rsc/handler-context.ts +45 -0
  220. package/src/rsc/handler.ts +864 -1114
  221. package/src/rsc/helpers.ts +181 -19
  222. package/src/rsc/index.ts +0 -20
  223. package/src/rsc/loader-fetch.ts +229 -0
  224. package/src/rsc/manifest-init.ts +90 -0
  225. package/src/rsc/nonce.ts +14 -0
  226. package/src/rsc/origin-guard.ts +141 -0
  227. package/src/rsc/progressive-enhancement.ts +395 -0
  228. package/src/rsc/response-error.ts +37 -0
  229. package/src/rsc/response-route-handler.ts +360 -0
  230. package/src/rsc/rsc-rendering.ts +256 -0
  231. package/src/rsc/runtime-warnings.ts +42 -0
  232. package/src/rsc/server-action.ts +360 -0
  233. package/src/rsc/ssr-setup.ts +128 -0
  234. package/src/rsc/types.ts +52 -11
  235. package/src/search-params.ts +230 -0
  236. package/src/segment-content-promise.ts +67 -0
  237. package/src/segment-loader-promise.ts +122 -0
  238. package/src/segment-system.tsx +187 -38
  239. package/src/server/context.ts +333 -59
  240. package/src/server/cookie-store.ts +190 -0
  241. package/src/server/fetchable-loader-store.ts +37 -0
  242. package/src/server/handle-store.ts +113 -15
  243. package/src/server/loader-registry.ts +24 -64
  244. package/src/server/request-context.ts +603 -109
  245. package/src/server.ts +35 -155
  246. package/src/ssr/index.tsx +107 -30
  247. package/src/static-handler.ts +126 -0
  248. package/src/theme/ThemeProvider.tsx +21 -15
  249. package/src/theme/ThemeScript.tsx +5 -5
  250. package/src/theme/constants.ts +5 -2
  251. package/src/theme/index.ts +4 -14
  252. package/src/theme/theme-context.ts +4 -30
  253. package/src/theme/theme-script.ts +21 -18
  254. package/src/types/boundaries.ts +158 -0
  255. package/src/types/cache-types.ts +198 -0
  256. package/src/types/error-types.ts +192 -0
  257. package/src/types/global-namespace.ts +100 -0
  258. package/src/types/handler-context.ts +764 -0
  259. package/src/types/index.ts +88 -0
  260. package/src/types/loader-types.ts +209 -0
  261. package/src/types/request-scope.ts +126 -0
  262. package/src/types/route-config.ts +170 -0
  263. package/src/types/route-entry.ts +120 -0
  264. package/src/types/segments.ts +167 -0
  265. package/src/types.ts +1 -1757
  266. package/src/urls/include-helper.ts +207 -0
  267. package/src/urls/index.ts +53 -0
  268. package/src/urls/path-helper-types.ts +372 -0
  269. package/src/urls/path-helper.ts +364 -0
  270. package/src/urls/pattern-types.ts +107 -0
  271. package/src/urls/response-types.ts +108 -0
  272. package/src/urls/type-extraction.ts +372 -0
  273. package/src/urls/urls-function.ts +98 -0
  274. package/src/urls.ts +1 -1282
  275. package/src/use-loader.tsx +161 -81
  276. package/src/vite/debug.ts +184 -0
  277. package/src/vite/discovery/bundle-postprocess.ts +181 -0
  278. package/src/vite/discovery/discover-routers.ts +376 -0
  279. package/src/vite/discovery/gate-state.ts +171 -0
  280. package/src/vite/discovery/prerender-collection.ts +486 -0
  281. package/src/vite/discovery/route-types-writer.ts +258 -0
  282. package/src/vite/discovery/self-gen-tracking.ts +73 -0
  283. package/src/vite/discovery/state.ts +117 -0
  284. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  285. package/src/vite/index.ts +15 -2063
  286. package/src/vite/plugin-types.ts +103 -0
  287. package/src/vite/plugins/cjs-to-esm.ts +98 -0
  288. package/src/vite/plugins/client-ref-dedup.ts +131 -0
  289. package/src/vite/plugins/client-ref-hashing.ts +117 -0
  290. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  291. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  292. package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
  293. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +107 -64
  294. package/src/vite/plugins/expose-id-utils.ts +299 -0
  295. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  296. package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
  297. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  298. package/src/vite/plugins/expose-ids/router-transform.ts +127 -0
  299. package/src/vite/plugins/expose-ids/types.ts +45 -0
  300. package/src/vite/plugins/expose-internal-ids.ts +816 -0
  301. package/src/vite/plugins/performance-tracks.ts +96 -0
  302. package/src/vite/plugins/refresh-cmd.ts +127 -0
  303. package/src/vite/plugins/use-cache-transform.ts +336 -0
  304. package/src/vite/plugins/version-injector.ts +109 -0
  305. package/src/vite/plugins/version-plugin.ts +266 -0
  306. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  307. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  308. package/src/vite/rango.ts +497 -0
  309. package/src/vite/router-discovery.ts +1423 -0
  310. package/src/vite/utils/ast-handler-extract.ts +517 -0
  311. package/src/vite/utils/banner.ts +36 -0
  312. package/src/vite/utils/bundle-analysis.ts +137 -0
  313. package/src/vite/utils/manifest-utils.ts +70 -0
  314. package/src/vite/utils/package-resolution.ts +161 -0
  315. package/src/vite/utils/prerender-utils.ts +222 -0
  316. package/src/vite/utils/shared-utils.ts +170 -0
  317. package/CLAUDE.md +0 -43
  318. package/src/browser/lru-cache.ts +0 -69
  319. package/src/browser/request-controller.ts +0 -164
  320. package/src/cache/memory-store.ts +0 -253
  321. package/src/href-context.ts +0 -33
  322. package/src/router.gen.ts +0 -6
  323. package/src/urls.gen.ts +0 -8
  324. package/src/vite/expose-handle-id.ts +0 -209
  325. package/src/vite/expose-loader-id.ts +0 -426
  326. package/src/vite/expose-location-state-id.ts +0 -177
  327. package/src/vite/expose-prerender-handler-id.ts +0 -429
  328. package/src/vite/package-resolution.ts +0 -125
  329. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -14,22 +14,23 @@ import type {
14
14
  HostPattern,
15
15
  RouteEntry,
16
16
  HostMatchResult,
17
- } from './types.js';
17
+ } from "./types.js";
18
+ import type { RouterRequestInput } from "../router/router-interfaces.js";
18
19
  import {
19
20
  matchPattern,
20
21
  parseRequest,
21
22
  normalizePattern,
22
23
  validatePattern,
23
- } from './pattern-matcher.js';
24
+ } from "./pattern-matcher.js";
24
25
  import {
25
26
  handleCookieOverride,
26
27
  createCookieErrorResponse,
27
- } from './cookie-handler.js';
28
+ } from "./cookie-handler.js";
28
29
  import {
29
30
  HostRouterError,
30
31
  NoRouteMatchError,
31
32
  InvalidHandlerError,
32
- } from './errors.js';
33
+ } from "./errors.js";
33
34
 
34
35
  /**
35
36
  * Registry entry for a host router instance.
@@ -46,7 +47,8 @@ export interface HostRouterRegistryEntry {
46
47
  * Populated by createHostRouter() so the build-time discovery plugin can find
47
48
  * host routers and resolve their lazy handlers to trigger sub-app createRouter() calls.
48
49
  */
49
- export const HostRouterRegistry: Map<string, HostRouterRegistryEntry> = new Map();
50
+ export const HostRouterRegistry: Map<string, HostRouterRegistryEntry> =
51
+ new Map();
50
52
 
51
53
  let hostRouterAutoId = 0;
52
54
 
@@ -71,7 +73,7 @@ export function createHostRouter(options: HostRouterOptions = {}): HostRouter {
71
73
  */
72
74
  function createRouteBuilder(
73
75
  patterns: string[],
74
- isFallback = false
76
+ isFallback = false,
75
77
  ): HostRouteBuilder {
76
78
  const middleware: Middleware[] = [];
77
79
 
@@ -96,8 +98,8 @@ export function createHostRouter(options: HostRouterOptions = {}): HostRouter {
96
98
  }
97
99
 
98
100
  log(
99
- `Registered ${isFallback ? 'fallback' : 'route'}:`,
100
- patterns.join(', ')
101
+ `Registered ${isFallback ? "fallback" : "route"}:`,
102
+ patterns.join(", "),
101
103
  );
102
104
 
103
105
  return router;
@@ -110,9 +112,9 @@ export function createHostRouter(options: HostRouterOptions = {}): HostRouter {
110
112
  */
111
113
  function findMatchingRoute(
112
114
  hostname: string,
113
- pathname: string
115
+ pathname: string,
114
116
  ): RouteEntry | null {
115
- const parts = hostname.split('.');
117
+ const parts = hostname.split(".");
116
118
 
117
119
  for (const route of routes) {
118
120
  for (const pattern of route.patterns) {
@@ -132,8 +134,8 @@ export function createHostRouter(options: HostRouterOptions = {}): HostRouter {
132
134
  async function executeMiddleware(
133
135
  middleware: Middleware[],
134
136
  request: Request,
135
- context: any,
136
- finalHandler: () => Promise<Response>
137
+ input: RouterRequestInput<any>,
138
+ finalHandler: () => Promise<Response>,
137
139
  ): Promise<Response> {
138
140
  let index = 0;
139
141
 
@@ -147,7 +149,20 @@ export function createHostRouter(options: HostRouterOptions = {}): HostRouter {
147
149
  return finalHandler();
148
150
  }
149
151
 
150
- return mw(request, context, next);
152
+ // Guard against double next() calls — a second call would
153
+ // re-enter the downstream chain and run handlers/side-effects twice.
154
+ let nextCalled = false;
155
+ const guardedNext = (): Promise<Response> => {
156
+ if (nextCalled) {
157
+ throw new Error(
158
+ `[HostRouter] Middleware called next() more than once.`,
159
+ );
160
+ }
161
+ nextCalled = true;
162
+ return next();
163
+ };
164
+
165
+ return mw(request, input, guardedNext);
151
166
  }
152
167
 
153
168
  return next();
@@ -159,34 +174,34 @@ export function createHostRouter(options: HostRouterOptions = {}): HostRouter {
159
174
  async function executeHandler(
160
175
  handler: Handler | LazyHandler,
161
176
  request: Request,
162
- context: any
177
+ input: RouterRequestInput<any>,
163
178
  ): Promise<Response> {
164
179
  // Check if it's a lazy handler (function that returns promise)
165
- if (typeof handler === 'function') {
166
- const result = handler(request, context);
180
+ if (typeof handler === "function") {
181
+ const result = handler(request, input);
167
182
 
168
183
  // If it returns a promise with default export
169
- if (result && typeof result === 'object' && 'then' in result) {
184
+ if (result && typeof result === "object" && "then" in result) {
170
185
  const module = await result;
171
186
  if (
172
- typeof module === 'object' &&
187
+ typeof module === "object" &&
173
188
  module !== null &&
174
- 'default' in module
189
+ "default" in module
175
190
  ) {
176
191
  const defaultExport = (module as { default: Handler | HostRouter })
177
192
  .default;
178
193
 
179
194
  // If default export is a router with match method
180
195
  if (
181
- typeof defaultExport === 'object' &&
196
+ typeof defaultExport === "object" &&
182
197
  defaultExport !== null &&
183
- 'match' in defaultExport
198
+ "match" in defaultExport
184
199
  ) {
185
- return (defaultExport as HostRouter).match(request, context);
200
+ return (defaultExport as HostRouter).match(request, input);
186
201
  }
187
202
 
188
203
  // Otherwise treat as handler
189
- return (defaultExport as Handler)(request, context);
204
+ return (defaultExport as Handler)(request, input);
190
205
  }
191
206
  // If promise resolves to Response
192
207
  return result as Promise<Response>;
@@ -228,8 +243,8 @@ export function createHostRouter(options: HostRouterOptions = {}): HostRouter {
228
243
  },
229
244
 
230
245
  test(hostname: string): HostMatchResult | null {
231
- const parts = hostname.split('.');
232
- const pathname = '/';
246
+ const parts = hostname.split(".");
247
+ const pathname = "/";
233
248
 
234
249
  for (const route of routes) {
235
250
  for (const pattern of route.patterns) {
@@ -245,18 +260,17 @@ export function createHostRouter(options: HostRouterOptions = {}): HostRouter {
245
260
  return null;
246
261
  },
247
262
 
248
- async match(request: Request, context: any = {}): Promise<Response> {
263
+ async match(
264
+ request: Request,
265
+ input: RouterRequestInput<any> = {},
266
+ ): Promise<Response> {
249
267
  log(`Request: ${request.url}`);
250
268
 
251
269
  let effectiveHostname: string;
252
270
 
253
271
  try {
254
272
  // Handle cookie override (may throw HostRouterError)
255
- effectiveHostname = handleCookieOverride(
256
- request,
257
- hostOverride,
258
- context
259
- );
273
+ effectiveHostname = handleCookieOverride(request, hostOverride, input);
260
274
  } catch (error) {
261
275
  // If it's a HostRouterError from cookie override
262
276
  if (error instanceof HostRouterError) {
@@ -264,14 +278,18 @@ export function createHostRouter(options: HostRouterOptions = {}): HostRouter {
264
278
 
265
279
  // If fallback exists, use it
266
280
  if (fallbackRoute) {
267
- context.error = error;
281
+ const fallbackInput = { ...input, error };
268
282
  const allMiddleware = [
269
283
  ...globalMiddleware,
270
284
  ...fallbackRoute.middleware,
271
285
  ];
272
286
 
273
- return executeMiddleware(allMiddleware, request, context, () =>
274
- executeHandler(fallbackRoute!.handler, request, context)
287
+ return executeMiddleware(
288
+ allMiddleware,
289
+ request,
290
+ fallbackInput,
291
+ () =>
292
+ executeHandler(fallbackRoute!.handler, request, fallbackInput),
275
293
  );
276
294
  }
277
295
 
@@ -279,7 +297,7 @@ export function createHostRouter(options: HostRouterOptions = {}): HostRouter {
279
297
  if (hostOverride) {
280
298
  return createCookieErrorResponse(
281
299
  hostOverride.cookieName,
282
- error.message
300
+ error.message,
283
301
  );
284
302
  }
285
303
  }
@@ -311,8 +329,8 @@ export function createHostRouter(options: HostRouterOptions = {}): HostRouter {
311
329
  const allMiddleware = [...globalMiddleware, ...matchedRoute.middleware];
312
330
 
313
331
  // Execute middleware chain and handler
314
- return executeMiddleware(allMiddleware, request, context, () =>
315
- executeHandler(matchedRoute.handler, request, context)
332
+ return executeMiddleware(allMiddleware, request, input, () =>
333
+ executeHandler(matchedRoute.handler, request, input),
316
334
  );
317
335
  },
318
336
  };
@@ -322,8 +340,12 @@ export function createHostRouter(options: HostRouterOptions = {}): HostRouter {
322
340
  // added via .host().map() after this point.
323
341
  const registryId = `host-router-${hostRouterAutoId++}`;
324
342
  HostRouterRegistry.set(registryId, {
325
- get routes() { return routes; },
326
- get fallback() { return fallbackRoute; },
343
+ get routes() {
344
+ return routes;
345
+ },
346
+ get fallback() {
347
+ return fallbackRoute;
348
+ },
327
349
  });
328
350
 
329
351
  return router;
@@ -4,7 +4,7 @@
4
4
  * Helper functions for testing host routing.
5
5
  */
6
6
 
7
- import { matchPattern } from './pattern-matcher.js';
7
+ import { matchPattern } from "./pattern-matcher.js";
8
8
 
9
9
  export interface CreateTestRequestOptions {
10
10
  host: string;
@@ -29,8 +29,8 @@ export interface CreateTestRequestOptions {
29
29
  export function createTestRequest(options: CreateTestRequestOptions): Request {
30
30
  const {
31
31
  host,
32
- path = '/',
33
- method = 'GET',
32
+ path = "/",
33
+ method = "GET",
34
34
  cookies = {},
35
35
  headers = {},
36
36
  } = options;
@@ -42,8 +42,8 @@ export function createTestRequest(options: CreateTestRequestOptions): Request {
42
42
  if (Object.keys(cookies).length > 0) {
43
43
  const cookieString = Object.entries(cookies)
44
44
  .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
45
- .join('; ');
46
- requestHeaders.set('cookie', cookieString);
45
+ .join("; ");
46
+ requestHeaders.set("cookie", cookieString);
47
47
  }
48
48
 
49
49
  return new Request(url, {
@@ -63,11 +63,11 @@ export function createTestRequest(options: CreateTestRequestOptions): Request {
63
63
  */
64
64
  export function testPattern(
65
65
  pattern: string | string[],
66
- hostname: string
66
+ hostname: string,
67
67
  ): boolean {
68
68
  const patterns = Array.isArray(pattern) ? pattern : [pattern];
69
- const parts = hostname.split('.');
70
- const pathname = '/';
69
+ const parts = hostname.split(".");
70
+ const pathname = "/";
71
71
 
72
72
  for (const p of patterns) {
73
73
  if (matchPattern(p, hostname, pathname, parts)) {
package/src/host/types.ts CHANGED
@@ -4,12 +4,15 @@
4
4
  * Type definitions for the host-based routing system.
5
5
  */
6
6
 
7
+ import type { RouterRequestInput } from "../router/router-interfaces.js";
8
+
7
9
  /**
8
- * Handler function that processes a request and returns a response
10
+ * Handler function that processes a request and returns a response.
11
+ * The input parameter receives the same RouterRequestInput passed to match().
9
12
  */
10
13
  export type Handler = (
11
14
  request: Request,
12
- context: any
15
+ input: RouterRequestInput<any>,
13
16
  ) => Response | Promise<Response>;
14
17
 
15
18
  /**
@@ -18,12 +21,13 @@ export type Handler = (
18
21
  export type LazyHandler = () => Promise<{ default: Handler | HostRouter }>;
19
22
 
20
23
  /**
21
- * Middleware function that can intercept and modify requests/responses
24
+ * Middleware function that can intercept and modify requests/responses.
25
+ * The input parameter receives the same RouterRequestInput passed to match().
22
26
  */
23
27
  export type Middleware = (
24
28
  request: Request,
25
- context: any,
26
- next: () => Promise<Response>
29
+ input: RouterRequestInput<any>,
30
+ next: () => Promise<Response>,
27
31
  ) => Promise<Response>;
28
32
 
29
33
  /**
@@ -71,7 +75,7 @@ export interface HostRouter {
71
75
  /**
72
76
  * Match an incoming request
73
77
  */
74
- match(request: Request, context?: any): Promise<Response>;
78
+ match(request: Request, input?: RouterRequestInput<any>): Promise<Response>;
75
79
 
76
80
  /**
77
81
  * Register fallback handler for allowed hosts without valid cookie
@@ -101,7 +105,11 @@ export interface HostOverrideConfig {
101
105
  /**
102
106
  * Optional validation function
103
107
  */
104
- validate?: (request: Request, cookieValue: string, context: any) => string;
108
+ validate?: (
109
+ request: Request,
110
+ cookieValue: string,
111
+ input: RouterRequestInput<any>,
112
+ ) => string;
105
113
  }
106
114
 
107
115
  /**
package/src/host/utils.ts CHANGED
@@ -19,7 +19,7 @@
19
19
  * ```
20
20
  */
21
21
  export function defineHosts<T extends Record<string, string | string[]>>(
22
- hosts: T
22
+ hosts: T,
23
23
  ): Readonly<T> {
24
24
  return Object.freeze(hosts);
25
25
  }
@@ -45,30 +45,34 @@ type ParseConstraintPath<T extends string> =
45
45
  export type PatternToPath<T extends string> =
46
46
  // Optional + constrained param in middle: /:param(a|b)?/rest
47
47
  T extends `${infer Before}:${infer _Name}(${infer Constraint})?/${infer After}`
48
- ? PatternToPath<`${Before}${After}`> | `${Before}${ParseConstraintPath<Constraint>}/${PatternToPath<After>}`
49
- // Optional + constrained param at end: /path/:param(a|b)?
50
- : T extends `${infer Before}:${infer _Name}(${infer Constraint})?`
51
- ? Before | `${Before}${ParseConstraintPath<Constraint>}`
52
- // Constrained param in middle: /:param(a|b)/rest
53
- : T extends `${infer Before}:${infer _Name}(${infer Constraint})/${infer After}`
54
- ? `${Before}${ParseConstraintPath<Constraint>}/${PatternToPath<After>}`
55
- // Constrained param at end: /path/:param(a|b)
56
- : T extends `${infer Before}:${infer _Name}(${infer Constraint})`
57
- ? `${Before}${ParseConstraintPath<Constraint>}`
58
- // Optional param in middle: /:param?/rest
59
- : T extends `${infer Before}:${infer _Param}?/${infer After}`
60
- ? PatternToPath<`${Before}${After}`> | `${Before}${string}/${PatternToPath<After>}`
61
- // Optional param at end: /path/:param?
62
- : T extends `${infer Before}:${infer _Param}?`
63
- ? Before | `${Before}${string}`
64
- // Required param in middle: /:param/rest
65
- : T extends `${infer Before}:${infer _Param}/${infer After}`
66
- ? `${Before}${string}/${PatternToPath<After>}`
67
- // Required param at end: /path/:param
68
- : T extends `${infer Before}:${infer _Param}`
69
- ? `${Before}${string}`
70
- // Static path
71
- : T;
48
+ ?
49
+ | PatternToPath<`${Before}${After}`>
50
+ | `${Before}${ParseConstraintPath<Constraint>}/${PatternToPath<After>}`
51
+ : // Optional + constrained param at end: /path/:param(a|b)?
52
+ T extends `${infer Before}:${infer _Name}(${infer Constraint})?`
53
+ ? Before | `${Before}${ParseConstraintPath<Constraint>}`
54
+ : // Constrained param in middle: /:param(a|b)/rest
55
+ T extends `${infer Before}:${infer _Name}(${infer Constraint})/${infer After}`
56
+ ? `${Before}${ParseConstraintPath<Constraint>}/${PatternToPath<After>}`
57
+ : // Constrained param at end: /path/:param(a|b)
58
+ T extends `${infer Before}:${infer _Name}(${infer Constraint})`
59
+ ? `${Before}${ParseConstraintPath<Constraint>}`
60
+ : // Optional param in middle: /:param?/rest
61
+ T extends `${infer Before}:${infer _Param}?/${infer After}`
62
+ ?
63
+ | PatternToPath<`${Before}${After}`>
64
+ | `${Before}${string}/${PatternToPath<After>}`
65
+ : // Optional param at end: /path/:param?
66
+ T extends `${infer Before}:${infer _Param}?`
67
+ ? Before | `${Before}${string}`
68
+ : // Required param in middle: /:param/rest
69
+ T extends `${infer Before}:${infer _Param}/${infer After}`
70
+ ? `${Before}${string}/${PatternToPath<After>}`
71
+ : // Required param at end: /path/:param
72
+ T extends `${infer Before}:${infer _Param}`
73
+ ? `${Before}${string}`
74
+ : // Static path
75
+ T;
72
76
 
73
77
  /**
74
78
  * Allow optional query string (?...) and/or hash fragment (#...) suffix
@@ -85,19 +89,17 @@ type WithSuffix<T extends string> =
85
89
  /**
86
90
  * Helper type to get pattern from routes, handling string values and { path, response } objects
87
91
  */
88
- type RoutePattern<TRoutes, K extends keyof TRoutes> =
89
- TRoutes[K] extends string ? TRoutes[K]
90
- : TRoutes[K] extends { readonly path: infer P extends string } ? P
91
- : string;
92
+ type RoutePattern<TRoutes, K extends keyof TRoutes> = TRoutes[K] extends string
93
+ ? TRoutes[K]
94
+ : TRoutes[K] extends { readonly path: infer P extends string }
95
+ ? P
96
+ : string;
92
97
 
93
98
  /**
94
99
  * Reverse lookup: find route name where the pattern matches TPattern
95
100
  */
96
- type NameForPattern<
97
- TPattern extends string,
98
- TRoutes = GetRegisteredRoutes
99
- > = {
100
- [K in keyof TRoutes]: RoutePattern<TRoutes, K> extends TPattern ? K : never
101
+ type NameForPattern<TPattern extends string, TRoutes = GetRegisteredRoutes> = {
102
+ [K in keyof TRoutes]: RoutePattern<TRoutes, K> extends TPattern ? K : never;
101
103
  }[keyof TRoutes];
102
104
 
103
105
  /**
@@ -112,19 +114,28 @@ type NameForPattern<
112
114
  * For dynamic routes, use the pattern:
113
115
  * PathResponse<"/api/products/:id"> → Product
114
116
  */
115
- export type PathResponse<TPattern extends string, TRoutes = GetRegisteredRoutes> =
116
- ResponseEnvelope<{
117
+ export type PathResponse<
118
+ TPattern extends string,
119
+ TRoutes = GetRegisteredRoutes,
120
+ > = ResponseEnvelope<
121
+ {
117
122
  [K in keyof TRoutes]: RoutePattern<TRoutes, K> extends TPattern
118
- ? TRoutes[K] extends { readonly response: infer R } ? Exclude<R, Response> : never
119
- : never
120
- }[keyof TRoutes]>;
123
+ ? TRoutes[K] extends { readonly response: infer R }
124
+ ? Exclude<R, Response>
125
+ : never
126
+ : never;
127
+ }[keyof TRoutes]
128
+ >;
121
129
 
122
130
  /**
123
131
  * Strip trailing slash from a path (e.g., "/blog/" -> "/blog" | "/blog/")
124
132
  * Allows navigation to include() prefixes without requiring trailing slash
125
133
  */
126
- type OptionalTrailingSlash<T extends string> =
127
- T extends `${infer Base}/` ? (Base extends "" ? T : Base | T) : T;
134
+ type OptionalTrailingSlash<T extends string> = T extends `${infer Base}/`
135
+ ? Base extends ""
136
+ ? T
137
+ : Base | T
138
+ : T;
128
139
 
129
140
  /**
130
141
  * Union of all valid paths from registered routes
@@ -135,9 +146,13 @@ type OptionalTrailingSlash<T extends string> =
135
146
  export type ValidPaths<TRoutes = GetRegisteredRoutes> =
136
147
  keyof TRoutes extends never
137
148
  ? `/${string}` // Fallback when no routes are registered
138
- : WithSuffix<{
139
- [K in keyof TRoutes]: OptionalTrailingSlash<PatternToPath<RoutePattern<TRoutes, K>>>
140
- }[keyof TRoutes]>;
149
+ : WithSuffix<
150
+ {
151
+ [K in keyof TRoutes]: OptionalTrailingSlash<
152
+ PatternToPath<RoutePattern<TRoutes, K>>
153
+ >;
154
+ }[keyof TRoutes]
155
+ >;
141
156
 
142
157
  /**
143
158
  * Type-safe href function for client-side use
@@ -167,7 +182,9 @@ export type ValidPaths<TRoutes = GetRegisteredRoutes> =
167
182
  */
168
183
  export function href<T extends ValidPaths>(path: T, mount?: string): string {
169
184
  if (mount && mount !== "/") {
170
- return mount + path;
185
+ // Strip trailing slash from mount to avoid double-slash when joining
186
+ const normalizedMount = mount.endsWith("/") ? mount.slice(0, -1) : mount;
187
+ return normalizedMount + path;
171
188
  }
172
189
  return path;
173
190
  }
@@ -181,7 +198,10 @@ export interface ResponseHrefProps {
181
198
  "data-external": "";
182
199
  }
183
200
 
184
- type ResponseHrefFn = <T extends ValidPaths>(path: T, mount?: string) => ResponseHrefProps;
201
+ type ResponseHrefFn = <T extends ValidPaths>(
202
+ path: T,
203
+ mount?: string,
204
+ ) => ResponseHrefProps;
185
205
 
186
206
  function createResponseHrefTag(): ResponseHrefFn {
187
207
  return (path, mount) => ({