@rangojs/router 0.0.0-experimental.3 → 0.0.0-experimental.30
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.
- package/AGENTS.md +5 -0
- package/README.md +883 -4
- package/dist/bin/rango.js +1601 -0
- package/dist/vite/index.js +4655 -747
- package/package.json +78 -50
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +54 -25
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +12 -8
- package/skills/document-cache/SKILL.md +23 -21
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +390 -63
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +133 -10
- package/skills/layout/SKILL.md +102 -5
- package/skills/links/SKILL.md +239 -0
- package/skills/loader/SKILL.md +366 -29
- package/skills/middleware/SKILL.md +173 -36
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +80 -3
- package/skills/prerender/SKILL.md +643 -0
- package/skills/rango/SKILL.md +86 -16
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +227 -14
- package/skills/router-setup/SKILL.md +225 -32
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +12 -11
- package/skills/typesafety/SKILL.md +401 -75
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +10 -4
- package/src/bin/rango.ts +321 -0
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +87 -64
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +20 -4
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +201 -553
- package/src/browser/navigation-client.ts +124 -71
- package/src/browser/navigation-store.ts +33 -50
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +267 -317
- package/src/browser/prefetch/cache.ts +146 -0
- package/src/browser/prefetch/fetch.ts +135 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +173 -73
- package/src/browser/react/NavigationProvider.tsx +138 -27
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +12 -12
- package/src/browser/react/location-state-shared.ts +95 -53
- package/src/browser/react/location-state.ts +60 -15
- package/src/browser/react/mount-context.ts +37 -0
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +29 -51
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +49 -65
- package/src/browser/react/use-href.tsx +20 -188
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-mount.ts +31 -0
- package/src/browser/react/use-navigation.ts +27 -78
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +63 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +80 -97
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +111 -26
- package/src/browser/scroll-restoration.ts +92 -16
- package/src/browser/segment-reconciler.ts +216 -0
- package/src/browser/segment-structure-assert.ts +83 -0
- package/src/browser/server-action-bridge.ts +504 -584
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +92 -57
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +438 -0
- package/src/build/generate-route-types.ts +36 -0
- package/src/build/index.ts +35 -0
- package/src/build/route-trie.ts +265 -0
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +411 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +469 -0
- package/src/build/route-types/scan-filter.ts +78 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +338 -0
- package/src/cache/cache-scope.ts +120 -303
- package/src/cache/cf/cf-cache-store.ts +119 -7
- package/src/cache/cf/index.ts +8 -2
- package/src/cache/document-cache.ts +101 -72
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +0 -15
- package/src/cache/memory-segment-store.ts +191 -13
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +98 -0
- package/src/cache/types.ts +72 -122
- package/src/client.rsc.tsx +10 -15
- package/src/client.tsx +114 -135
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +86 -0
- package/src/debug.ts +17 -7
- package/src/errors.ts +108 -2
- package/src/handle.ts +34 -19
- package/src/handles/MetaTags.tsx +73 -20
- package/src/handles/meta.ts +30 -13
- package/src/host/cookie-handler.ts +165 -0
- package/src/host/errors.ts +97 -0
- package/src/host/index.ts +53 -0
- package/src/host/pattern-matcher.ts +214 -0
- package/src/host/router.ts +352 -0
- package/src/host/testing.ts +79 -0
- package/src/host/types.ts +146 -0
- package/src/host/utils.ts +25 -0
- package/src/href-client.ts +135 -49
- package/src/index.rsc.ts +182 -17
- package/src/index.ts +238 -24
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +27 -142
- package/src/loader.ts +27 -10
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +37 -0
- package/src/prerender/store.ts +185 -0
- package/src/prerender.ts +463 -0
- package/src/reverse.ts +330 -0
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +9 -11
- package/src/route-definition/dsl-helpers.ts +934 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +430 -0
- package/src/route-definition/index.ts +52 -0
- package/src/route-definition/redirect.ts +93 -0
- package/src/route-definition.ts +1 -1388
- package/src/route-map-builder.ts +241 -112
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +70 -9
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +9 -9
- package/src/router/find-match.ts +158 -0
- package/src/router/handler-context.ts +371 -81
- package/src/router/intercept-resolution.ts +395 -0
- package/src/router/lazy-includes.ts +234 -0
- package/src/router/loader-resolution.ts +215 -122
- package/src/router/logging.ts +248 -0
- package/src/router/manifest.ts +155 -32
- package/src/router/match-api.ts +620 -0
- package/src/router/match-context.ts +5 -3
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +80 -93
- package/src/router/match-middleware/cache-lookup.ts +382 -9
- package/src/router/match-middleware/cache-store.ts +51 -22
- package/src/router/match-middleware/intercept-resolution.ts +55 -17
- package/src/router/match-middleware/segment-resolution.ts +24 -6
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +34 -29
- package/src/router/metrics.ts +235 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +324 -367
- package/src/router/pattern-matching.ts +321 -30
- package/src/router/prerender-match.ts +400 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +137 -38
- package/src/router/router-context.ts +36 -21
- package/src/router/router-interfaces.ts +452 -0
- package/src/router/router-options.ts +592 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +570 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +198 -0
- package/src/router/segment-resolution/revalidation.ts +1241 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -0
- package/src/router/segment-wrappers.ts +289 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +239 -0
- package/src/router/types.ts +77 -3
- package/src/router.ts +688 -3656
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +786 -760
- package/src/rsc/helpers.ts +140 -6
- package/src/rsc/index.ts +5 -25
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +379 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +235 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +348 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +40 -14
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +57 -61
- package/src/server/context.ts +202 -51
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +94 -15
- package/src/server/loader-registry.ts +15 -56
- package/src/server/request-context.ts +422 -70
- package/src/server.ts +36 -120
- package/src/ssr/index.tsx +157 -26
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +21 -15
- package/src/theme/ThemeScript.tsx +5 -5
- package/src/theme/constants.ts +5 -2
- package/src/theme/index.ts +4 -14
- package/src/theme/theme-context.ts +4 -30
- package/src/theme/theme-script.ts +21 -18
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +687 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +183 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +102 -0
- package/src/types/segments.ts +148 -0
- package/src/types.ts +1 -1577
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +339 -0
- package/src/urls/path-helper.ts +329 -0
- package/src/urls/pattern-types.ts +95 -0
- package/src/urls/response-types.ts +106 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -726
- package/src/use-loader.tsx +85 -77
- package/src/vite/discovery/bundle-postprocess.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +344 -0
- package/src/vite/discovery/prerender-collection.ts +385 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +110 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +11 -782
- package/src/vite/plugin-types.ts +131 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -51
- package/src/vite/plugins/expose-id-utils.ts +287 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +569 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +254 -0
- package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +29 -15
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +510 -0
- package/src/vite/router-discovery.ts +785 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
- package/CLAUDE.md +0 -3
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/href.ts +0 -255
- package/src/vite/expose-handle-id.ts +0 -209
- package/src/vite/expose-loader-id.ts +0 -357
- package/src/vite/expose-location-state-id.ts +0 -177
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
|
@@ -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,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host Router Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the host-based routing system.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { RouterRequestInput } from "../router/router-interfaces.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Handler function that processes a request and returns a response.
|
|
11
|
+
* The input parameter receives the same RouterRequestInput passed to match().
|
|
12
|
+
*/
|
|
13
|
+
export type Handler = (
|
|
14
|
+
request: Request,
|
|
15
|
+
input: RouterRequestInput<any>,
|
|
16
|
+
) => Response | Promise<Response>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Lazy handler that dynamically imports a module with a default handler or router
|
|
20
|
+
*/
|
|
21
|
+
export type LazyHandler = () => Promise<{ default: Handler | HostRouter }>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Middleware function that can intercept and modify requests/responses.
|
|
25
|
+
* The input parameter receives the same RouterRequestInput passed to match().
|
|
26
|
+
*/
|
|
27
|
+
export type Middleware = (
|
|
28
|
+
request: Request,
|
|
29
|
+
input: RouterRequestInput<any>,
|
|
30
|
+
next: () => Promise<Response>,
|
|
31
|
+
) => Promise<Response>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Host pattern - can be a string or array of strings
|
|
35
|
+
*/
|
|
36
|
+
export type HostPattern = string | string[];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Result from testing a hostname against patterns
|
|
40
|
+
*/
|
|
41
|
+
export interface HostMatchResult {
|
|
42
|
+
pattern: string;
|
|
43
|
+
handler: Handler | LazyHandler;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Host route builder for chaining middleware and handler
|
|
48
|
+
*/
|
|
49
|
+
export interface HostRouteBuilder {
|
|
50
|
+
/**
|
|
51
|
+
* Add middleware to this host pattern
|
|
52
|
+
*/
|
|
53
|
+
use(...middleware: Middleware[]): HostRouteBuilder;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Map to a handler or lazy import
|
|
57
|
+
*/
|
|
58
|
+
map(handler: Handler | LazyHandler): HostRouter;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Main host router interface
|
|
63
|
+
*/
|
|
64
|
+
export interface HostRouter {
|
|
65
|
+
/**
|
|
66
|
+
* Register a host pattern
|
|
67
|
+
*/
|
|
68
|
+
host(patterns: HostPattern): HostRouteBuilder;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Register global middleware
|
|
72
|
+
*/
|
|
73
|
+
use(...middleware: Middleware[]): HostRouter;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Match an incoming request
|
|
77
|
+
*/
|
|
78
|
+
match(request: Request, input?: RouterRequestInput<any>): Promise<Response>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Register fallback handler for allowed hosts without valid cookie
|
|
82
|
+
*/
|
|
83
|
+
fallback(): HostRouteBuilder;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Test which handler would match a hostname
|
|
87
|
+
*/
|
|
88
|
+
test(hostname: string): HostMatchResult | null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Host override configuration
|
|
93
|
+
*/
|
|
94
|
+
export interface HostOverrideConfig {
|
|
95
|
+
/**
|
|
96
|
+
* Cookie name to read for host override
|
|
97
|
+
*/
|
|
98
|
+
cookieName: string;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Hosts that are allowed to use override
|
|
102
|
+
*/
|
|
103
|
+
allowedHosts: string[];
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Optional validation function
|
|
107
|
+
*/
|
|
108
|
+
validate?: (
|
|
109
|
+
request: Request,
|
|
110
|
+
cookieValue: string,
|
|
111
|
+
input: RouterRequestInput<any>,
|
|
112
|
+
) => string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Host router options
|
|
117
|
+
*/
|
|
118
|
+
export interface HostRouterOptions {
|
|
119
|
+
/**
|
|
120
|
+
* Enable debug logging
|
|
121
|
+
*/
|
|
122
|
+
debug?: boolean;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Cookie-based host override configuration
|
|
126
|
+
*/
|
|
127
|
+
hostOverride?: HostOverrideConfig;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Internal route entry
|
|
132
|
+
*/
|
|
133
|
+
export interface RouteEntry {
|
|
134
|
+
patterns: string[];
|
|
135
|
+
middleware: Middleware[];
|
|
136
|
+
handler: Handler | LazyHandler;
|
|
137
|
+
isFallback?: boolean;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Pattern match result (internal)
|
|
142
|
+
*/
|
|
143
|
+
export interface PatternMatchResult {
|
|
144
|
+
matched: boolean;
|
|
145
|
+
routeEntry?: RouteEntry;
|
|
146
|
+
}
|
|
@@ -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
|
+
}
|
package/src/href-client.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import type { GetRegisteredRoutes } from "./types.js";
|
|
18
|
+
import type { ResponseEnvelope } from "./urls.js";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Parse constraint values into a union type for paths
|
|
@@ -44,30 +45,34 @@ type ParseConstraintPath<T extends string> =
|
|
|
44
45
|
export type PatternToPath<T extends string> =
|
|
45
46
|
// Optional + constrained param in middle: /:param(a|b)?/rest
|
|
46
47
|
T extends `${infer Before}:${infer _Name}(${infer Constraint})?/${infer After}`
|
|
47
|
-
?
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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;
|
|
71
76
|
|
|
72
77
|
/**
|
|
73
78
|
* Allow optional query string (?...) and/or hash fragment (#...) suffix
|
|
@@ -82,10 +87,55 @@ type WithSuffix<T extends string> =
|
|
|
82
87
|
| `${T}?${string}#${string}`;
|
|
83
88
|
|
|
84
89
|
/**
|
|
85
|
-
* Helper type to get pattern from routes, handling
|
|
90
|
+
* Helper type to get pattern from routes, handling string values and { path, response } objects
|
|
91
|
+
*/
|
|
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;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Reverse lookup: find route name where the pattern matches TPattern
|
|
100
|
+
*/
|
|
101
|
+
type NameForPattern<TPattern extends string, TRoutes = GetRegisteredRoutes> = {
|
|
102
|
+
[K in keyof TRoutes]: RoutePattern<TRoutes, K> extends TPattern ? K : never;
|
|
103
|
+
}[keyof TRoutes];
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Look up the response data type for a route pattern from RegisteredRoutes.
|
|
107
|
+
*
|
|
108
|
+
* Works by reverse-looking up the route name for the given pattern,
|
|
109
|
+
* then extracting the response type from the route entry.
|
|
110
|
+
*
|
|
111
|
+
* For static routes (no params), pattern === path:
|
|
112
|
+
* PathResponse<"/api/health"> → { status: string; timestamp: number }
|
|
113
|
+
*
|
|
114
|
+
* For dynamic routes, use the pattern:
|
|
115
|
+
* PathResponse<"/api/products/:id"> → Product
|
|
86
116
|
*/
|
|
87
|
-
type
|
|
88
|
-
|
|
117
|
+
export type PathResponse<
|
|
118
|
+
TPattern extends string,
|
|
119
|
+
TRoutes = GetRegisteredRoutes,
|
|
120
|
+
> = ResponseEnvelope<
|
|
121
|
+
{
|
|
122
|
+
[K in keyof TRoutes]: RoutePattern<TRoutes, K> extends TPattern
|
|
123
|
+
? TRoutes[K] extends { readonly response: infer R }
|
|
124
|
+
? Exclude<R, Response>
|
|
125
|
+
: never
|
|
126
|
+
: never;
|
|
127
|
+
}[keyof TRoutes]
|
|
128
|
+
>;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Strip trailing slash from a path (e.g., "/blog/" -> "/blog" | "/blog/")
|
|
132
|
+
* Allows navigation to include() prefixes without requiring trailing slash
|
|
133
|
+
*/
|
|
134
|
+
type OptionalTrailingSlash<T extends string> = T extends `${infer Base}/`
|
|
135
|
+
? Base extends ""
|
|
136
|
+
? T
|
|
137
|
+
: Base | T
|
|
138
|
+
: T;
|
|
89
139
|
|
|
90
140
|
/**
|
|
91
141
|
* Union of all valid paths from registered routes
|
|
@@ -96,41 +146,77 @@ type RoutePattern<TRoutes, K extends keyof TRoutes> =
|
|
|
96
146
|
export type ValidPaths<TRoutes = GetRegisteredRoutes> =
|
|
97
147
|
keyof TRoutes extends never
|
|
98
148
|
? `/${string}` // Fallback when no routes are registered
|
|
99
|
-
: WithSuffix<
|
|
149
|
+
: WithSuffix<
|
|
150
|
+
{
|
|
151
|
+
[K in keyof TRoutes]: OptionalTrailingSlash<
|
|
152
|
+
PatternToPath<RoutePattern<TRoutes, K>>
|
|
153
|
+
>;
|
|
154
|
+
}[keyof TRoutes]
|
|
155
|
+
>;
|
|
100
156
|
|
|
101
157
|
/**
|
|
102
158
|
* Type-safe href function for client-side use
|
|
103
159
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* Works with:
|
|
108
|
-
* - Static paths: href("/about")
|
|
109
|
-
* - Dynamic segments: href("/blog/my-post")
|
|
110
|
-
* - Multiple segments: href("/shop/product/widget/reviews/123")
|
|
111
|
-
*
|
|
112
|
-
* Does NOT validate:
|
|
113
|
-
* - Query strings (passed through as-is)
|
|
114
|
-
* - Hash fragments (passed through as-is)
|
|
160
|
+
* Without mount: identity function, validates absolute paths at compile time.
|
|
161
|
+
* With mount: prepends mount path, for use with useMount() inside include() scopes.
|
|
115
162
|
*
|
|
116
163
|
* @param path - A valid path matching one of the registered route patterns
|
|
117
|
-
* @
|
|
164
|
+
* @param mount - Optional mount prefix from useMount() for include-scoped paths
|
|
165
|
+
* @returns The resolved path
|
|
118
166
|
*
|
|
119
167
|
* @example
|
|
120
168
|
* ```typescript
|
|
121
|
-
* //
|
|
122
|
-
* href("/blog/hello");
|
|
123
|
-
* href("/shop/product/widget");
|
|
124
|
-
* href("/shop/product/widget/reviews"); // matches /shop/product/:slug/reviews
|
|
169
|
+
* // Absolute paths (type-safe)
|
|
170
|
+
* href("/blog/hello"); // "/blog/hello"
|
|
171
|
+
* href("/shop/product/widget"); // "/shop/product/widget"
|
|
125
172
|
*
|
|
126
|
-
* //
|
|
173
|
+
* // With mount (inside an include)
|
|
174
|
+
* const mount = useMount(); // "/articles"
|
|
175
|
+
* href("/", mount); // "/articles/"
|
|
176
|
+
* href("/my-post", mount); // "/articles/my-post"
|
|
177
|
+
*
|
|
178
|
+
* // Query strings and hashes pass through
|
|
127
179
|
* href("/blog/hello?page=1");
|
|
128
180
|
* href("/about#contact");
|
|
129
|
-
*
|
|
130
|
-
* // Invalid paths (TypeScript error)
|
|
131
|
-
* href("/nonexistent"); // Error: not assignable to ValidPaths
|
|
132
181
|
* ```
|
|
133
182
|
*/
|
|
134
|
-
export function href<T extends ValidPaths>(path: T):
|
|
183
|
+
export function href<T extends ValidPaths>(path: T, mount?: string): string {
|
|
184
|
+
if (mount && mount !== "/") {
|
|
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;
|
|
188
|
+
}
|
|
135
189
|
return path;
|
|
136
190
|
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Props shape returned by href.json() etc. for spreading on <Link>.
|
|
194
|
+
* Sets data-external to trigger hard navigation (skips RSC fetch).
|
|
195
|
+
*/
|
|
196
|
+
export interface ResponseHrefProps {
|
|
197
|
+
to: string;
|
|
198
|
+
"data-external": "";
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
type ResponseHrefFn = <T extends ValidPaths>(
|
|
202
|
+
path: T,
|
|
203
|
+
mount?: string,
|
|
204
|
+
) => ResponseHrefProps;
|
|
205
|
+
|
|
206
|
+
function createResponseHrefTag(): ResponseHrefFn {
|
|
207
|
+
return (path, mount) => ({
|
|
208
|
+
to: href(path, mount),
|
|
209
|
+
"data-external": "" as const,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export namespace href {
|
|
214
|
+
export const json: ResponseHrefFn = createResponseHrefTag();
|
|
215
|
+
export const text: ResponseHrefFn = createResponseHrefTag();
|
|
216
|
+
export const html: ResponseHrefFn = createResponseHrefTag();
|
|
217
|
+
export const xml: ResponseHrefFn = createResponseHrefTag();
|
|
218
|
+
export const md: ResponseHrefFn = createResponseHrefTag();
|
|
219
|
+
export const image: ResponseHrefFn = createResponseHrefTag();
|
|
220
|
+
export const stream: ResponseHrefFn = createResponseHrefTag();
|
|
221
|
+
export const any: ResponseHrefFn = createResponseHrefTag();
|
|
222
|
+
}
|