@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.
- package/AGENTS.md +9 -0
- package/README.md +1037 -4
- package/dist/bin/rango.js +1619 -157
- package/dist/vite/index.js +5762 -2301
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +71 -63
- package/skills/breadcrumbs/SKILL.md +252 -0
- package/skills/cache-guide/SKILL.md +294 -0
- package/skills/caching/SKILL.md +93 -23
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +12 -8
- package/skills/document-cache/SKILL.md +18 -16
- package/skills/fonts/SKILL.md +6 -4
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +367 -71
- package/skills/host-router/SKILL.md +218 -0
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +176 -8
- package/skills/layout/SKILL.md +124 -3
- package/skills/links/SKILL.md +304 -25
- package/skills/loader/SKILL.md +474 -47
- package/skills/middleware/SKILL.md +207 -37
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +15 -11
- package/skills/parallel/SKILL.md +272 -1
- package/skills/prerender/SKILL.md +467 -65
- package/skills/rango/SKILL.md +89 -21
- package/skills/response-routes/SKILL.md +152 -91
- package/skills/route/SKILL.md +305 -14
- package/skills/router-setup/SKILL.md +210 -32
- package/skills/server-actions/SKILL.md +739 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +333 -86
- package/skills/use-cache/SKILL.md +324 -0
- package/skills/view-transitions/SKILL.md +212 -0
- package/src/__internal.ts +102 -4
- package/src/bin/rango.ts +312 -15
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +136 -68
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +374 -561
- package/src/browser/navigation-client.ts +228 -70
- package/src/browser/navigation-store.ts +97 -55
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +376 -315
- package/src/browser/prefetch/cache.ts +314 -0
- package/src/browser/prefetch/fetch.ts +282 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +191 -0
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +152 -0
- package/src/browser/react/Link.tsx +255 -71
- package/src/browser/react/NavigationProvider.tsx +152 -24
- package/src/browser/react/context.ts +11 -0
- package/src/browser/react/filter-segment-order.ts +55 -0
- package/src/browser/react/index.ts +15 -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 +6 -1
- 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 +30 -120
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +44 -65
- package/src/browser/react/use-params.ts +78 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-reverse.ts +99 -0
- package/src/browser/react/use-router.ts +83 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +85 -99
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +246 -64
- package/src/browser/scroll-restoration.ts +127 -52
- package/src/browser/segment-reconciler.ts +243 -0
- package/src/browser/segment-structure-assert.ts +16 -0
- package/src/browser/server-action-bridge.ts +510 -603
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +158 -48
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +84 -23
- package/src/build/generate-route-types.ts +39 -828
- package/src/build/index.ts +4 -5
- package/src/build/route-trie.ts +85 -32
- 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 +418 -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 +618 -0
- package/src/build/route-types/scan-filter.ts +85 -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 +342 -0
- package/src/cache/cache-scope.ts +167 -307
- package/src/cache/cf/cf-cache-store.ts +573 -21
- package/src/cache/cf/index.ts +13 -3
- package/src/cache/document-cache.ts +116 -77
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +1 -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 +153 -0
- package/src/cache/types.ts +72 -122
- package/src/client.rsc.tsx +6 -1
- package/src/client.tsx +118 -302
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +156 -0
- package/src/debug.ts +19 -9
- package/src/errors.ts +77 -7
- package/src/handle.ts +55 -10
- package/src/handles/MetaTags.tsx +73 -20
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/handles/meta.ts +30 -13
- package/src/host/cookie-handler.ts +21 -15
- package/src/host/errors.ts +8 -8
- package/src/host/index.ts +4 -7
- package/src/host/pattern-matcher.ts +27 -27
- package/src/host/router.ts +61 -39
- package/src/host/testing.ts +8 -8
- package/src/host/types.ts +15 -7
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +65 -45
- package/src/index.rsc.ts +138 -21
- package/src/index.ts +206 -51
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +25 -143
- package/src/loader.ts +27 -10
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +4 -2
- package/src/prerender/store.ts +159 -13
- package/src/prerender.ts +397 -29
- package/src/response-utils.ts +28 -0
- package/src/reverse.ts +231 -121
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +1134 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +483 -0
- package/src/route-definition/index.ts +55 -0
- package/src/route-definition/redirect.ts +101 -0
- package/src/route-definition/resolve-handler-use.ts +155 -0
- package/src/route-definition.ts +1 -1431
- package/src/route-map-builder.ts +162 -123
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +66 -9
- package/src/router/content-negotiation.ts +215 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +9 -9
- package/src/router/find-match.ts +160 -0
- package/src/router/handler-context.ts +418 -86
- package/src/router/intercept-resolution.ts +35 -20
- package/src/router/lazy-includes.ts +237 -0
- package/src/router/loader-resolution.ts +359 -128
- package/src/router/logging.ts +251 -0
- package/src/router/manifest.ts +98 -32
- package/src/router/match-api.ts +196 -261
- package/src/router/match-context.ts +4 -2
- package/src/router/match-handlers.ts +441 -0
- package/src/router/match-middleware/background-revalidation.ts +108 -93
- package/src/router/match-middleware/cache-lookup.ts +415 -86
- package/src/router/match-middleware/cache-store.ts +91 -29
- package/src/router/match-middleware/intercept-resolution.ts +48 -21
- package/src/router/match-middleware/segment-resolution.ts +73 -9
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +154 -35
- package/src/router/metrics.ts +240 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +209 -0
- package/src/router/middleware.ts +373 -371
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +292 -52
- package/src/router/prerender-match.ts +502 -0
- package/src/router/preview-match.ts +98 -0
- package/src/router/request-classification.ts +310 -0
- package/src/router/revalidation.ts +152 -39
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +41 -21
- package/src/router/router-interfaces.ts +484 -0
- package/src/router/router-options.ts +618 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +756 -0
- package/src/router/segment-resolution/helpers.ts +268 -0
- package/src/router/segment-resolution/loader-cache.ts +199 -0
- package/src/router/segment-resolution/revalidation.ts +1407 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -1315
- package/src/router/segment-wrappers.ts +291 -0
- package/src/router/substitute-pattern-params.ts +56 -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 +111 -39
- package/src/router/types.ts +17 -9
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +642 -2011
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +864 -1114
- package/src/rsc/helpers.ts +181 -19
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +229 -0
- package/src/rsc/manifest-init.ts +90 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +395 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +360 -0
- package/src/rsc/rsc-rendering.ts +256 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +360 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +52 -11
- package/src/search-params.ts +230 -0
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +187 -38
- package/src/server/context.ts +333 -59
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +113 -15
- package/src/server/loader-registry.ts +24 -64
- package/src/server/request-context.ts +603 -109
- package/src/server.ts +35 -155
- package/src/ssr/index.tsx +107 -30
- package/src/static-handler.ts +126 -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 +764 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +209 -0
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +120 -0
- package/src/types/segments.ts +167 -0
- package/src/types.ts +1 -1757
- package/src/urls/include-helper.ts +207 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +372 -0
- package/src/urls/path-helper.ts +364 -0
- package/src/urls/pattern-types.ts +107 -0
- package/src/urls/response-types.ts +108 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -1282
- package/src/use-loader.tsx +161 -81
- package/src/vite/debug.ts +184 -0
- package/src/vite/discovery/bundle-postprocess.ts +181 -0
- package/src/vite/discovery/discover-routers.ts +376 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +486 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +73 -0
- package/src/vite/discovery/state.ts +117 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +15 -2063
- package/src/vite/plugin-types.ts +103 -0
- package/src/vite/plugins/cjs-to-esm.ts +98 -0
- package/src/vite/plugins/client-ref-dedup.ts +131 -0
- package/src/vite/plugins/client-ref-hashing.ts +117 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +107 -64
- package/src/vite/plugins/expose-id-utils.ts +299 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +127 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +816 -0
- package/src/vite/plugins/performance-tracks.ts +96 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/use-cache-transform.ts +336 -0
- package/src/vite/plugins/version-injector.ts +109 -0
- package/src/vite/plugins/version-plugin.ts +266 -0
- package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +497 -0
- package/src/vite/router-discovery.ts +1423 -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/utils/package-resolution.ts +161 -0
- package/src/vite/utils/prerender-utils.ts +222 -0
- package/src/vite/utils/shared-utils.ts +170 -0
- package/CLAUDE.md +0 -43
- 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/router.gen.ts +0 -6
- package/src/urls.gen.ts +0 -8
- package/src/vite/expose-handle-id.ts +0 -209
- package/src/vite/expose-loader-id.ts +0 -426
- package/src/vite/expose-location-state-id.ts +0 -177
- package/src/vite/expose-prerender-handler-id.ts +0 -429
- package/src/vite/package-resolution.ts +0 -125
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
package/src/host/router.ts
CHANGED
|
@@ -14,22 +14,23 @@ import type {
|
|
|
14
14
|
HostPattern,
|
|
15
15
|
RouteEntry,
|
|
16
16
|
HostMatchResult,
|
|
17
|
-
} from
|
|
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
|
|
24
|
+
} from "./pattern-matcher.js";
|
|
24
25
|
import {
|
|
25
26
|
handleCookieOverride,
|
|
26
27
|
createCookieErrorResponse,
|
|
27
|
-
} from
|
|
28
|
+
} from "./cookie-handler.js";
|
|
28
29
|
import {
|
|
29
30
|
HostRouterError,
|
|
30
31
|
NoRouteMatchError,
|
|
31
32
|
InvalidHandlerError,
|
|
32
|
-
} from
|
|
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> =
|
|
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 ?
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 ===
|
|
166
|
-
const result = handler(request,
|
|
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 ===
|
|
184
|
+
if (result && typeof result === "object" && "then" in result) {
|
|
170
185
|
const module = await result;
|
|
171
186
|
if (
|
|
172
|
-
typeof module ===
|
|
187
|
+
typeof module === "object" &&
|
|
173
188
|
module !== null &&
|
|
174
|
-
|
|
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 ===
|
|
196
|
+
typeof defaultExport === "object" &&
|
|
182
197
|
defaultExport !== null &&
|
|
183
|
-
|
|
198
|
+
"match" in defaultExport
|
|
184
199
|
) {
|
|
185
|
-
return (defaultExport as HostRouter).match(request,
|
|
200
|
+
return (defaultExport as HostRouter).match(request, input);
|
|
186
201
|
}
|
|
187
202
|
|
|
188
203
|
// Otherwise treat as handler
|
|
189
|
-
return (defaultExport as Handler)(request,
|
|
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(
|
|
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
|
-
|
|
281
|
+
const fallbackInput = { ...input, error };
|
|
268
282
|
const allMiddleware = [
|
|
269
283
|
...globalMiddleware,
|
|
270
284
|
...fallbackRoute.middleware,
|
|
271
285
|
];
|
|
272
286
|
|
|
273
|
-
return executeMiddleware(
|
|
274
|
-
|
|
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,
|
|
315
|
-
executeHandler(matchedRoute.handler, request,
|
|
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() {
|
|
326
|
-
|
|
343
|
+
get routes() {
|
|
344
|
+
return routes;
|
|
345
|
+
},
|
|
346
|
+
get fallback() {
|
|
347
|
+
return fallbackRoute;
|
|
348
|
+
},
|
|
327
349
|
});
|
|
328
350
|
|
|
329
351
|
return router;
|
package/src/host/testing.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Helper functions for testing host routing.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { matchPattern } from
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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?: (
|
|
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
package/src/href-client.ts
CHANGED
|
@@ -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
|
-
?
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
90
|
-
: TRoutes[K] extends { readonly path: infer P extends string }
|
|
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;
|
|
92
97
|
|
|
93
98
|
/**
|
|
94
99
|
* Reverse lookup: find route name where the pattern matches TPattern
|
|
95
100
|
*/
|
|
96
|
-
type NameForPattern<
|
|
97
|
-
|
|
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<
|
|
116
|
-
|
|
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 }
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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>(
|
|
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) => ({
|