@rangojs/router 0.0.0-experimental.10

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 (172) hide show
  1. package/CLAUDE.md +43 -0
  2. package/README.md +19 -0
  3. package/dist/bin/rango.js +227 -0
  4. package/dist/vite/index.js +3039 -0
  5. package/package.json +171 -0
  6. package/skills/caching/SKILL.md +191 -0
  7. package/skills/debug-manifest/SKILL.md +108 -0
  8. package/skills/document-cache/SKILL.md +180 -0
  9. package/skills/fonts/SKILL.md +165 -0
  10. package/skills/hooks/SKILL.md +442 -0
  11. package/skills/intercept/SKILL.md +190 -0
  12. package/skills/layout/SKILL.md +213 -0
  13. package/skills/links/SKILL.md +180 -0
  14. package/skills/loader/SKILL.md +246 -0
  15. package/skills/middleware/SKILL.md +202 -0
  16. package/skills/mime-routes/SKILL.md +124 -0
  17. package/skills/parallel/SKILL.md +228 -0
  18. package/skills/prerender/SKILL.md +283 -0
  19. package/skills/rango/SKILL.md +54 -0
  20. package/skills/response-routes/SKILL.md +358 -0
  21. package/skills/route/SKILL.md +173 -0
  22. package/skills/router-setup/SKILL.md +346 -0
  23. package/skills/tailwind/SKILL.md +129 -0
  24. package/skills/theme/SKILL.md +78 -0
  25. package/skills/typesafety/SKILL.md +394 -0
  26. package/src/__internal.ts +175 -0
  27. package/src/bin/rango.ts +24 -0
  28. package/src/browser/event-controller.ts +876 -0
  29. package/src/browser/index.ts +18 -0
  30. package/src/browser/link-interceptor.ts +121 -0
  31. package/src/browser/lru-cache.ts +69 -0
  32. package/src/browser/merge-segment-loaders.ts +126 -0
  33. package/src/browser/navigation-bridge.ts +913 -0
  34. package/src/browser/navigation-client.ts +165 -0
  35. package/src/browser/navigation-store.ts +823 -0
  36. package/src/browser/partial-update.ts +600 -0
  37. package/src/browser/react/Link.tsx +248 -0
  38. package/src/browser/react/NavigationProvider.tsx +346 -0
  39. package/src/browser/react/ScrollRestoration.tsx +94 -0
  40. package/src/browser/react/context.ts +53 -0
  41. package/src/browser/react/index.ts +52 -0
  42. package/src/browser/react/location-state-shared.ts +120 -0
  43. package/src/browser/react/location-state.ts +62 -0
  44. package/src/browser/react/mount-context.ts +32 -0
  45. package/src/browser/react/use-action.ts +240 -0
  46. package/src/browser/react/use-client-cache.ts +56 -0
  47. package/src/browser/react/use-handle.ts +203 -0
  48. package/src/browser/react/use-href.tsx +40 -0
  49. package/src/browser/react/use-link-status.ts +134 -0
  50. package/src/browser/react/use-mount.ts +31 -0
  51. package/src/browser/react/use-navigation.ts +140 -0
  52. package/src/browser/react/use-segments.ts +188 -0
  53. package/src/browser/request-controller.ts +164 -0
  54. package/src/browser/rsc-router.tsx +352 -0
  55. package/src/browser/scroll-restoration.ts +324 -0
  56. package/src/browser/segment-structure-assert.ts +67 -0
  57. package/src/browser/server-action-bridge.ts +762 -0
  58. package/src/browser/shallow.ts +35 -0
  59. package/src/browser/types.ts +478 -0
  60. package/src/build/generate-manifest.ts +377 -0
  61. package/src/build/generate-route-types.ts +828 -0
  62. package/src/build/index.ts +36 -0
  63. package/src/build/route-trie.ts +239 -0
  64. package/src/cache/cache-scope.ts +563 -0
  65. package/src/cache/cf/cf-cache-store.ts +428 -0
  66. package/src/cache/cf/index.ts +19 -0
  67. package/src/cache/document-cache.ts +340 -0
  68. package/src/cache/index.ts +58 -0
  69. package/src/cache/memory-segment-store.ts +150 -0
  70. package/src/cache/memory-store.ts +253 -0
  71. package/src/cache/types.ts +392 -0
  72. package/src/client.rsc.tsx +83 -0
  73. package/src/client.tsx +643 -0
  74. package/src/component-utils.ts +76 -0
  75. package/src/components/DefaultDocument.tsx +23 -0
  76. package/src/debug.ts +233 -0
  77. package/src/default-error-boundary.tsx +88 -0
  78. package/src/deps/browser.ts +8 -0
  79. package/src/deps/html-stream-client.ts +2 -0
  80. package/src/deps/html-stream-server.ts +2 -0
  81. package/src/deps/rsc.ts +10 -0
  82. package/src/deps/ssr.ts +2 -0
  83. package/src/errors.ts +295 -0
  84. package/src/handle.ts +130 -0
  85. package/src/handles/MetaTags.tsx +193 -0
  86. package/src/handles/index.ts +6 -0
  87. package/src/handles/meta.ts +247 -0
  88. package/src/host/cookie-handler.ts +159 -0
  89. package/src/host/errors.ts +97 -0
  90. package/src/host/index.ts +56 -0
  91. package/src/host/pattern-matcher.ts +214 -0
  92. package/src/host/router.ts +330 -0
  93. package/src/host/testing.ts +79 -0
  94. package/src/host/types.ts +138 -0
  95. package/src/host/utils.ts +25 -0
  96. package/src/href-client.ts +202 -0
  97. package/src/href-context.ts +33 -0
  98. package/src/index.rsc.ts +121 -0
  99. package/src/index.ts +165 -0
  100. package/src/loader.rsc.ts +207 -0
  101. package/src/loader.ts +47 -0
  102. package/src/network-error-thrower.tsx +21 -0
  103. package/src/outlet-context.ts +15 -0
  104. package/src/prerender/param-hash.ts +35 -0
  105. package/src/prerender/store.ts +40 -0
  106. package/src/prerender.ts +156 -0
  107. package/src/reverse.ts +267 -0
  108. package/src/root-error-boundary.tsx +277 -0
  109. package/src/route-content-wrapper.tsx +193 -0
  110. package/src/route-definition.ts +1431 -0
  111. package/src/route-map-builder.ts +242 -0
  112. package/src/route-types.ts +220 -0
  113. package/src/router/error-handling.ts +287 -0
  114. package/src/router/handler-context.ts +158 -0
  115. package/src/router/intercept-resolution.ts +387 -0
  116. package/src/router/loader-resolution.ts +327 -0
  117. package/src/router/manifest.ts +216 -0
  118. package/src/router/match-api.ts +621 -0
  119. package/src/router/match-context.ts +264 -0
  120. package/src/router/match-middleware/background-revalidation.ts +236 -0
  121. package/src/router/match-middleware/cache-lookup.ts +382 -0
  122. package/src/router/match-middleware/cache-store.ts +276 -0
  123. package/src/router/match-middleware/index.ts +81 -0
  124. package/src/router/match-middleware/intercept-resolution.ts +281 -0
  125. package/src/router/match-middleware/segment-resolution.ts +184 -0
  126. package/src/router/match-pipelines.ts +214 -0
  127. package/src/router/match-result.ts +213 -0
  128. package/src/router/metrics.ts +62 -0
  129. package/src/router/middleware.ts +791 -0
  130. package/src/router/pattern-matching.ts +407 -0
  131. package/src/router/revalidation.ts +190 -0
  132. package/src/router/router-context.ts +301 -0
  133. package/src/router/segment-resolution.ts +1315 -0
  134. package/src/router/trie-matching.ts +172 -0
  135. package/src/router/types.ts +163 -0
  136. package/src/router.gen.ts +6 -0
  137. package/src/router.ts +2423 -0
  138. package/src/rsc/handler.ts +1443 -0
  139. package/src/rsc/helpers.ts +64 -0
  140. package/src/rsc/index.ts +56 -0
  141. package/src/rsc/nonce.ts +18 -0
  142. package/src/rsc/types.ts +236 -0
  143. package/src/segment-system.tsx +442 -0
  144. package/src/server/context.ts +466 -0
  145. package/src/server/handle-store.ts +229 -0
  146. package/src/server/loader-registry.ts +174 -0
  147. package/src/server/request-context.ts +554 -0
  148. package/src/server/root-layout.tsx +10 -0
  149. package/src/server/tsconfig.json +14 -0
  150. package/src/server.ts +171 -0
  151. package/src/ssr/index.tsx +296 -0
  152. package/src/theme/ThemeProvider.tsx +291 -0
  153. package/src/theme/ThemeScript.tsx +61 -0
  154. package/src/theme/constants.ts +59 -0
  155. package/src/theme/index.ts +58 -0
  156. package/src/theme/theme-context.ts +70 -0
  157. package/src/theme/theme-script.ts +152 -0
  158. package/src/theme/types.ts +182 -0
  159. package/src/theme/use-theme.ts +44 -0
  160. package/src/types.ts +1757 -0
  161. package/src/urls.gen.ts +8 -0
  162. package/src/urls.ts +1282 -0
  163. package/src/use-loader.tsx +346 -0
  164. package/src/vite/expose-action-id.ts +344 -0
  165. package/src/vite/expose-handle-id.ts +209 -0
  166. package/src/vite/expose-loader-id.ts +426 -0
  167. package/src/vite/expose-location-state-id.ts +177 -0
  168. package/src/vite/expose-prerender-handler-id.ts +429 -0
  169. package/src/vite/index.ts +2068 -0
  170. package/src/vite/package-resolution.ts +125 -0
  171. package/src/vite/version.d.ts +12 -0
  172. package/src/vite/virtual-entries.ts +114 -0
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Host Router Implementation
3
+ *
4
+ * Main router that handles host-based routing with middleware and cookie override.
5
+ */
6
+
7
+ import type {
8
+ HostRouter,
9
+ HostRouteBuilder,
10
+ HostRouterOptions,
11
+ Handler,
12
+ LazyHandler,
13
+ Middleware,
14
+ HostPattern,
15
+ RouteEntry,
16
+ HostMatchResult,
17
+ } from './types.js';
18
+ import {
19
+ matchPattern,
20
+ parseRequest,
21
+ normalizePattern,
22
+ validatePattern,
23
+ } from './pattern-matcher.js';
24
+ import {
25
+ handleCookieOverride,
26
+ createCookieErrorResponse,
27
+ } from './cookie-handler.js';
28
+ import {
29
+ HostRouterError,
30
+ NoRouteMatchError,
31
+ InvalidHandlerError,
32
+ } from './errors.js';
33
+
34
+ /**
35
+ * Registry entry for a host router instance.
36
+ * Stores references to the live routes array and fallback, so the discovery
37
+ * plugin can iterate handlers registered after createHostRouter() returns.
38
+ */
39
+ export interface HostRouterRegistryEntry {
40
+ routes: RouteEntry[];
41
+ fallback: RouteEntry | null;
42
+ }
43
+
44
+ /**
45
+ * Global registry for host routers (parallel to RouterRegistry for RSC routers).
46
+ * Populated by createHostRouter() so the build-time discovery plugin can find
47
+ * host routers and resolve their lazy handlers to trigger sub-app createRouter() calls.
48
+ */
49
+ export const HostRouterRegistry: Map<string, HostRouterRegistryEntry> = new Map();
50
+
51
+ let hostRouterAutoId = 0;
52
+
53
+ /**
54
+ * Create a host router
55
+ */
56
+ export function createHostRouter(options: HostRouterOptions = {}): HostRouter {
57
+ const routes: RouteEntry[] = [];
58
+ const globalMiddleware: Middleware[] = [];
59
+ let fallbackRoute: RouteEntry | null = null;
60
+
61
+ const { debug = false, hostOverride } = options;
62
+
63
+ function log(message: string, ...args: any[]): void {
64
+ if (debug) {
65
+ console.log(`[HostRouter] ${message}`, ...args);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Create a route builder for chaining
71
+ */
72
+ function createRouteBuilder(
73
+ patterns: string[],
74
+ isFallback = false
75
+ ): HostRouteBuilder {
76
+ const middleware: Middleware[] = [];
77
+
78
+ return {
79
+ use(...mw: Middleware[]): HostRouteBuilder {
80
+ middleware.push(...mw);
81
+ return this;
82
+ },
83
+
84
+ map(handler: Handler | LazyHandler): HostRouter {
85
+ const entry: RouteEntry = {
86
+ patterns,
87
+ middleware,
88
+ handler,
89
+ isFallback,
90
+ };
91
+
92
+ if (isFallback) {
93
+ fallbackRoute = entry;
94
+ } else {
95
+ routes.push(entry);
96
+ }
97
+
98
+ log(
99
+ `Registered ${isFallback ? 'fallback' : 'route'}:`,
100
+ patterns.join(', ')
101
+ );
102
+
103
+ return router;
104
+ },
105
+ };
106
+ }
107
+
108
+ /**
109
+ * Find matching route for hostname and path
110
+ */
111
+ function findMatchingRoute(
112
+ hostname: string,
113
+ pathname: string
114
+ ): RouteEntry | null {
115
+ const parts = hostname.split('.');
116
+
117
+ for (const route of routes) {
118
+ for (const pattern of route.patterns) {
119
+ if (matchPattern(pattern, hostname, pathname, parts)) {
120
+ log(`Matched pattern: "${pattern}"`);
121
+ return route;
122
+ }
123
+ }
124
+ }
125
+
126
+ return null;
127
+ }
128
+
129
+ /**
130
+ * Execute middleware chain
131
+ */
132
+ async function executeMiddleware(
133
+ middleware: Middleware[],
134
+ request: Request,
135
+ context: any,
136
+ finalHandler: () => Promise<Response>
137
+ ): Promise<Response> {
138
+ let index = 0;
139
+
140
+ async function next(): Promise<Response> {
141
+ if (index >= middleware.length) {
142
+ return finalHandler();
143
+ }
144
+
145
+ const mw = middleware[index++];
146
+ if (!mw) {
147
+ return finalHandler();
148
+ }
149
+
150
+ return mw(request, context, next);
151
+ }
152
+
153
+ return next();
154
+ }
155
+
156
+ /**
157
+ * Execute handler (lazy or direct)
158
+ */
159
+ async function executeHandler(
160
+ handler: Handler | LazyHandler,
161
+ request: Request,
162
+ context: any
163
+ ): Promise<Response> {
164
+ // Check if it's a lazy handler (function that returns promise)
165
+ if (typeof handler === 'function') {
166
+ const result = handler(request, context);
167
+
168
+ // If it returns a promise with default export
169
+ if (result && typeof result === 'object' && 'then' in result) {
170
+ const module = await result;
171
+ if (
172
+ typeof module === 'object' &&
173
+ module !== null &&
174
+ 'default' in module
175
+ ) {
176
+ const defaultExport = (module as { default: Handler | HostRouter })
177
+ .default;
178
+
179
+ // If default export is a router with match method
180
+ if (
181
+ typeof defaultExport === 'object' &&
182
+ defaultExport !== null &&
183
+ 'match' in defaultExport
184
+ ) {
185
+ return (defaultExport as HostRouter).match(request, context);
186
+ }
187
+
188
+ // Otherwise treat as handler
189
+ return (defaultExport as Handler)(request, context);
190
+ }
191
+ // If promise resolves to Response
192
+ return result as Promise<Response>;
193
+ }
194
+
195
+ // Direct handler
196
+ return result as Response | Promise<Response>;
197
+ }
198
+
199
+ throw new InvalidHandlerError(handler, {
200
+ cause: { handlerType: typeof handler },
201
+ });
202
+ }
203
+
204
+ /**
205
+ * Router instance
206
+ */
207
+ const router: HostRouter = {
208
+ host(patterns: HostPattern): HostRouteBuilder {
209
+ const patternsArray = Array.isArray(patterns) ? patterns : [patterns];
210
+
211
+ // Validate and normalize patterns
212
+ const normalized = patternsArray.map((p) => {
213
+ validatePattern(p);
214
+ return normalizePattern(p);
215
+ });
216
+
217
+ return createRouteBuilder(normalized, false);
218
+ },
219
+
220
+ use(...middleware: Middleware[]): HostRouter {
221
+ globalMiddleware.push(...middleware);
222
+ log(`Registered global middleware (${middleware.length})`);
223
+ return router;
224
+ },
225
+
226
+ fallback(): HostRouteBuilder {
227
+ return createRouteBuilder([], true);
228
+ },
229
+
230
+ test(hostname: string): HostMatchResult | null {
231
+ const parts = hostname.split('.');
232
+ const pathname = '/';
233
+
234
+ for (const route of routes) {
235
+ for (const pattern of route.patterns) {
236
+ if (matchPattern(pattern, hostname, pathname, parts)) {
237
+ return {
238
+ pattern,
239
+ handler: route.handler,
240
+ };
241
+ }
242
+ }
243
+ }
244
+
245
+ return null;
246
+ },
247
+
248
+ async match(request: Request, context: any = {}): Promise<Response> {
249
+ log(`Request: ${request.url}`);
250
+
251
+ let effectiveHostname: string;
252
+
253
+ try {
254
+ // Handle cookie override (may throw HostRouterError)
255
+ effectiveHostname = handleCookieOverride(
256
+ request,
257
+ hostOverride,
258
+ context
259
+ );
260
+ } catch (error) {
261
+ // If it's a HostRouterError from cookie override
262
+ if (error instanceof HostRouterError) {
263
+ log(`Cookie override error: ${error.message}`);
264
+
265
+ // If fallback exists, use it
266
+ if (fallbackRoute) {
267
+ context.error = error;
268
+ const allMiddleware = [
269
+ ...globalMiddleware,
270
+ ...fallbackRoute.middleware,
271
+ ];
272
+
273
+ return executeMiddleware(allMiddleware, request, context, () =>
274
+ executeHandler(fallbackRoute!.handler, request, context)
275
+ );
276
+ }
277
+
278
+ // Otherwise return error response with cookie deletion
279
+ if (hostOverride) {
280
+ return createCookieErrorResponse(
281
+ hostOverride.cookieName,
282
+ error.message
283
+ );
284
+ }
285
+ }
286
+
287
+ // Re-throw non-HostRouterErrors
288
+ throw error;
289
+ }
290
+
291
+ const { pathname } = parseRequest(request);
292
+
293
+ if (effectiveHostname !== parseRequest(request).hostname) {
294
+ log(`Cookie override: ${effectiveHostname}`);
295
+ }
296
+
297
+ // Find matching route
298
+ const matchedRoute = findMatchingRoute(effectiveHostname, pathname);
299
+
300
+ if (!matchedRoute) {
301
+ log(`No route matched`);
302
+ throw new NoRouteMatchError(effectiveHostname, pathname, {
303
+ cause: {
304
+ hostname: effectiveHostname,
305
+ pathname,
306
+ },
307
+ });
308
+ }
309
+
310
+ // Combine global and route-specific middleware
311
+ const allMiddleware = [...globalMiddleware, ...matchedRoute.middleware];
312
+
313
+ // Execute middleware chain and handler
314
+ return executeMiddleware(allMiddleware, request, context, () =>
315
+ executeHandler(matchedRoute.handler, request, context)
316
+ );
317
+ },
318
+ };
319
+
320
+ // Register in the global HostRouterRegistry for build-time discovery.
321
+ // The routes array and fallbackRoute ref are live - they reflect routes
322
+ // added via .host().map() after this point.
323
+ const registryId = `host-router-${hostRouterAutoId++}`;
324
+ HostRouterRegistry.set(registryId, {
325
+ get routes() { return routes; },
326
+ get fallback() { return fallbackRoute; },
327
+ });
328
+
329
+ return router;
330
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Testing Utilities for Host Router
3
+ *
4
+ * Helper functions for testing host routing.
5
+ */
6
+
7
+ import { matchPattern } from './pattern-matcher.js';
8
+
9
+ export interface CreateTestRequestOptions {
10
+ host: string;
11
+ path?: string;
12
+ method?: string;
13
+ cookies?: Record<string, string>;
14
+ headers?: Record<string, string>;
15
+ }
16
+
17
+ /**
18
+ * Create a test request with specific host and cookies
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const request = createTestRequest({
23
+ * host: 'admin.example.com',
24
+ * path: '/dashboard',
25
+ * cookies: { 'x-requested-host': 'api.example.com' }
26
+ * });
27
+ * ```
28
+ */
29
+ export function createTestRequest(options: CreateTestRequestOptions): Request {
30
+ const {
31
+ host,
32
+ path = '/',
33
+ method = 'GET',
34
+ cookies = {},
35
+ headers = {},
36
+ } = options;
37
+
38
+ const url = `http://${host}${path}`;
39
+ const requestHeaders = new Headers(headers);
40
+
41
+ // Add cookies if provided
42
+ if (Object.keys(cookies).length > 0) {
43
+ const cookieString = Object.entries(cookies)
44
+ .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
45
+ .join('; ');
46
+ requestHeaders.set('cookie', cookieString);
47
+ }
48
+
49
+ return new Request(url, {
50
+ method,
51
+ headers: requestHeaders,
52
+ });
53
+ }
54
+
55
+ /**
56
+ * Test if a pattern matches a hostname
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * expect(testPattern('admin.*', 'admin.example.com')).toBe(true);
61
+ * expect(testPattern(['*', 'www.*'], 'example.com')).toBe(true);
62
+ * ```
63
+ */
64
+ export function testPattern(
65
+ pattern: string | string[],
66
+ hostname: string
67
+ ): boolean {
68
+ const patterns = Array.isArray(pattern) ? pattern : [pattern];
69
+ const parts = hostname.split('.');
70
+ const pathname = '/';
71
+
72
+ for (const p of patterns) {
73
+ if (matchPattern(p, hostname, pathname, parts)) {
74
+ return true;
75
+ }
76
+ }
77
+
78
+ return false;
79
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Host Router Types
3
+ *
4
+ * Type definitions for the host-based routing system.
5
+ */
6
+
7
+ /**
8
+ * Handler function that processes a request and returns a response
9
+ */
10
+ export type Handler = (
11
+ request: Request,
12
+ context: any
13
+ ) => Response | Promise<Response>;
14
+
15
+ /**
16
+ * Lazy handler that dynamically imports a module with a default handler or router
17
+ */
18
+ export type LazyHandler = () => Promise<{ default: Handler | HostRouter }>;
19
+
20
+ /**
21
+ * Middleware function that can intercept and modify requests/responses
22
+ */
23
+ export type Middleware = (
24
+ request: Request,
25
+ context: any,
26
+ next: () => Promise<Response>
27
+ ) => Promise<Response>;
28
+
29
+ /**
30
+ * Host pattern - can be a string or array of strings
31
+ */
32
+ export type HostPattern = string | string[];
33
+
34
+ /**
35
+ * Result from testing a hostname against patterns
36
+ */
37
+ export interface HostMatchResult {
38
+ pattern: string;
39
+ handler: Handler | LazyHandler;
40
+ }
41
+
42
+ /**
43
+ * Host route builder for chaining middleware and handler
44
+ */
45
+ export interface HostRouteBuilder {
46
+ /**
47
+ * Add middleware to this host pattern
48
+ */
49
+ use(...middleware: Middleware[]): HostRouteBuilder;
50
+
51
+ /**
52
+ * Map to a handler or lazy import
53
+ */
54
+ map(handler: Handler | LazyHandler): HostRouter;
55
+ }
56
+
57
+ /**
58
+ * Main host router interface
59
+ */
60
+ export interface HostRouter {
61
+ /**
62
+ * Register a host pattern
63
+ */
64
+ host(patterns: HostPattern): HostRouteBuilder;
65
+
66
+ /**
67
+ * Register global middleware
68
+ */
69
+ use(...middleware: Middleware[]): HostRouter;
70
+
71
+ /**
72
+ * Match an incoming request
73
+ */
74
+ match(request: Request, context?: any): Promise<Response>;
75
+
76
+ /**
77
+ * Register fallback handler for allowed hosts without valid cookie
78
+ */
79
+ fallback(): HostRouteBuilder;
80
+
81
+ /**
82
+ * Test which handler would match a hostname
83
+ */
84
+ test(hostname: string): HostMatchResult | null;
85
+ }
86
+
87
+ /**
88
+ * Host override configuration
89
+ */
90
+ export interface HostOverrideConfig {
91
+ /**
92
+ * Cookie name to read for host override
93
+ */
94
+ cookieName: string;
95
+
96
+ /**
97
+ * Hosts that are allowed to use override
98
+ */
99
+ allowedHosts: string[];
100
+
101
+ /**
102
+ * Optional validation function
103
+ */
104
+ validate?: (request: Request, cookieValue: string, context: any) => string;
105
+ }
106
+
107
+ /**
108
+ * Host router options
109
+ */
110
+ export interface HostRouterOptions {
111
+ /**
112
+ * Enable debug logging
113
+ */
114
+ debug?: boolean;
115
+
116
+ /**
117
+ * Cookie-based host override configuration
118
+ */
119
+ hostOverride?: HostOverrideConfig;
120
+ }
121
+
122
+ /**
123
+ * Internal route entry
124
+ */
125
+ export interface RouteEntry {
126
+ patterns: string[];
127
+ middleware: Middleware[];
128
+ handler: Handler | LazyHandler;
129
+ isFallback?: boolean;
130
+ }
131
+
132
+ /**
133
+ * Pattern match result (internal)
134
+ */
135
+ export interface PatternMatchResult {
136
+ matched: boolean;
137
+ routeEntry?: RouteEntry;
138
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Host Router Utilities
3
+ *
4
+ * Helper functions for type-safe pattern definitions.
5
+ */
6
+
7
+ /**
8
+ * Define hosts with type safety
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const hosts = defineHosts({
13
+ * admin: 'admin.*',
14
+ * api: 'api.*',
15
+ * app: ['*', 'www.*']
16
+ * });
17
+ *
18
+ * router.host(hosts.admin).map(...); // Type-safe!
19
+ * ```
20
+ */
21
+ export function defineHosts<T extends Record<string, string | string[]>>(
22
+ hosts: T
23
+ ): Readonly<T> {
24
+ return Object.freeze(hosts);
25
+ }