@rangojs/router 0.0.0-experimental.002d056c
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 +899 -0
- package/dist/bin/rango.js +1606 -0
- package/dist/vite/index.js +5153 -0
- package/package.json +177 -0
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +253 -0
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +112 -0
- package/skills/document-cache/SKILL.md +182 -0
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +704 -0
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +313 -0
- package/skills/layout/SKILL.md +310 -0
- package/skills/links/SKILL.md +239 -0
- package/skills/loader/SKILL.md +596 -0
- package/skills/middleware/SKILL.md +339 -0
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +305 -0
- package/skills/prerender/SKILL.md +643 -0
- package/skills/rango/SKILL.md +118 -0
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +385 -0
- package/skills/router-setup/SKILL.md +439 -0
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +79 -0
- package/skills/typesafety/SKILL.md +623 -0
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +273 -0
- 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 +899 -0
- package/src/browser/history-state.ts +80 -0
- package/src/browser/index.ts +18 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +141 -0
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +134 -0
- package/src/browser/navigation-bridge.ts +638 -0
- package/src/browser/navigation-client.ts +261 -0
- package/src/browser/navigation-store.ts +806 -0
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +582 -0
- package/src/browser/prefetch/cache.ts +206 -0
- package/src/browser/prefetch/fetch.ts +145 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +128 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +368 -0
- package/src/browser/react/NavigationProvider.tsx +413 -0
- package/src/browser/react/ScrollRestoration.tsx +94 -0
- package/src/browser/react/context.ts +59 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +52 -0
- package/src/browser/react/location-state-shared.ts +162 -0
- package/src/browser/react/location-state.ts +107 -0
- 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 +218 -0
- package/src/browser/react/use-client-cache.ts +58 -0
- package/src/browser/react/use-handle.ts +162 -0
- package/src/browser/react/use-href.tsx +40 -0
- package/src/browser/react/use-link-status.ts +135 -0
- package/src/browser/react/use-mount.ts +31 -0
- package/src/browser/react/use-navigation.ts +99 -0
- 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 +171 -0
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +464 -0
- package/src/browser/scroll-restoration.ts +397 -0
- package/src/browser/segment-reconciler.ts +216 -0
- package/src/browser/segment-structure-assert.ts +83 -0
- package/src/browser/server-action-bridge.ts +667 -0
- package/src/browser/shallow.ts +40 -0
- package/src/browser/types.ts +547 -0
- 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 +479 -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 +382 -0
- package/src/cache/cf/cf-cache-store.ts +982 -0
- package/src/cache/cf/index.ts +29 -0
- package/src/cache/document-cache.ts +369 -0
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +44 -0
- package/src/cache/memory-segment-store.ts +328 -0
- 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 +342 -0
- package/src/client.rsc.tsx +85 -0
- package/src/client.tsx +601 -0
- package/src/component-utils.ts +76 -0
- package/src/components/DefaultDocument.tsx +27 -0
- package/src/context-var.ts +86 -0
- package/src/debug.ts +243 -0
- package/src/default-error-boundary.tsx +88 -0
- package/src/deps/browser.ts +8 -0
- package/src/deps/html-stream-client.ts +2 -0
- package/src/deps/html-stream-server.ts +2 -0
- package/src/deps/rsc.ts +10 -0
- package/src/deps/ssr.ts +2 -0
- package/src/errors.ts +365 -0
- package/src/handle.ts +135 -0
- package/src/handles/MetaTags.tsx +246 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +7 -0
- package/src/handles/meta.ts +264 -0
- 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 +222 -0
- package/src/index.rsc.ts +233 -0
- package/src/index.ts +277 -0
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +89 -0
- package/src/loader.ts +64 -0
- package/src/network-error-thrower.tsx +23 -0
- package/src/outlet-context.ts +15 -0
- 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 +289 -0
- package/src/route-content-wrapper.tsx +196 -0
- 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 -0
- package/src/route-map-builder.ts +281 -0
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +259 -0
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +287 -0
- package/src/router/find-match.ts +160 -0
- package/src/router/handler-context.ts +451 -0
- package/src/router/intercept-resolution.ts +397 -0
- package/src/router/lazy-includes.ts +236 -0
- package/src/router/loader-resolution.ts +420 -0
- package/src/router/logging.ts +251 -0
- package/src/router/manifest.ts +269 -0
- package/src/router/match-api.ts +620 -0
- package/src/router/match-context.ts +266 -0
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +223 -0
- package/src/router/match-middleware/cache-lookup.ts +634 -0
- package/src/router/match-middleware/cache-store.ts +295 -0
- package/src/router/match-middleware/index.ts +81 -0
- package/src/router/match-middleware/intercept-resolution.ts +306 -0
- package/src/router/match-middleware/segment-resolution.ts +193 -0
- package/src/router/match-pipelines.ts +179 -0
- package/src/router/match-result.ts +219 -0
- package/src/router/metrics.ts +282 -0
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +749 -0
- package/src/router/pattern-matching.ts +563 -0
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +289 -0
- package/src/router/router-context.ts +320 -0
- 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 +1242 -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 +291 -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 +170 -0
- package/src/router.ts +1006 -0
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +1089 -0
- package/src/rsc/helpers.ts +198 -0
- package/src/rsc/index.ts +36 -0
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +32 -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 +237 -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 +263 -0
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +454 -0
- package/src/server/context.ts +591 -0
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +308 -0
- package/src/server/loader-registry.ts +133 -0
- package/src/server/request-context.ts +920 -0
- package/src/server/root-layout.tsx +10 -0
- package/src/server/tsconfig.json +14 -0
- package/src/server.ts +51 -0
- package/src/ssr/index.tsx +365 -0
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +297 -0
- package/src/theme/ThemeScript.tsx +61 -0
- package/src/theme/constants.ts +62 -0
- package/src/theme/index.ts +48 -0
- package/src/theme/theme-context.ts +44 -0
- package/src/theme/theme-script.ts +155 -0
- package/src/theme/types.ts +182 -0
- package/src/theme/use-theme.ts +44 -0
- 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 +109 -0
- package/src/types/segments.ts +148 -0
- package/src/types.ts +1 -0
- 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 -0
- package/src/use-loader.tsx +354 -0
- 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 +108 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +16 -0
- package/src/vite/plugin-types.ts +48 -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/plugins/expose-action-id.ts +363 -0
- 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 +266 -0
- package/src/vite/plugins/version.d.ts +12 -0
- package/src/vite/plugins/virtual-entries.ts +123 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +445 -0
- package/src/vite/router-discovery.ts +777 -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 +121 -0
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router Handler Context
|
|
3
|
+
*
|
|
4
|
+
* Creates the handler context object passed to route handlers, middleware, and loaders.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { HandlerContext, InternalHandlerContext } from "../types";
|
|
8
|
+
import { _getRequestContext } from "../server/request-context.js";
|
|
9
|
+
import { getSearchSchema, isRouteRootScoped } from "../route-map-builder.js";
|
|
10
|
+
import { parseSearchParams, serializeSearchParams } from "../search-params.js";
|
|
11
|
+
import { contextGet, contextSet } from "../context-var.js";
|
|
12
|
+
import { NOCACHE_SYMBOL, assertNotInsideCacheExec } from "../cache/taint.js";
|
|
13
|
+
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
14
|
+
import { PRERENDER_PASSTHROUGH } from "../prerender.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Strip internal _rsc* query params from a URL.
|
|
18
|
+
* Returns a new URL with only user-facing params.
|
|
19
|
+
*/
|
|
20
|
+
export function stripInternalParams(url: URL): URL {
|
|
21
|
+
const clean = new URL(url);
|
|
22
|
+
for (const key of [...clean.searchParams.keys()]) {
|
|
23
|
+
if (key.startsWith("_rsc")) {
|
|
24
|
+
clean.searchParams.delete(key);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return clean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Resolve route name with namespace prefix support.
|
|
32
|
+
* Supports local names (dot-prefixed) and absolute names (global lookup).
|
|
33
|
+
*
|
|
34
|
+
* @param rootScoped - Explicit override for root-scope check. When undefined,
|
|
35
|
+
* falls back to the global scope registry, then to a heuristic.
|
|
36
|
+
*/
|
|
37
|
+
function resolveRouteName(
|
|
38
|
+
name: string,
|
|
39
|
+
routeMap: Record<string, string>,
|
|
40
|
+
currentRoutePrefix?: string,
|
|
41
|
+
rootScoped?: boolean,
|
|
42
|
+
): string | undefined {
|
|
43
|
+
// 1. Dot-prefixed (".article", ".author.posts") — local resolution only.
|
|
44
|
+
// Resolves within the current include() scope using the mount prefix.
|
|
45
|
+
if (name.startsWith(".")) {
|
|
46
|
+
const lookupName = name.slice(1);
|
|
47
|
+
if (!currentRoutePrefix) return undefined;
|
|
48
|
+
|
|
49
|
+
// Extract the include prefix from current route name
|
|
50
|
+
// e.g., "magazine.author" -> prefix is "magazine"
|
|
51
|
+
const lastDot = currentRoutePrefix.lastIndexOf(".");
|
|
52
|
+
const prefix =
|
|
53
|
+
lastDot > 0
|
|
54
|
+
? currentRoutePrefix.substring(0, lastDot)
|
|
55
|
+
: currentRoutePrefix;
|
|
56
|
+
|
|
57
|
+
// Try prefixed name at current level
|
|
58
|
+
const prefixedName = `${prefix}.${lookupName}`;
|
|
59
|
+
if (routeMap[prefixedName] !== undefined) {
|
|
60
|
+
return routeMap[prefixedName];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Walk up parent prefixes for nested includes
|
|
64
|
+
let currentPrefix = prefix;
|
|
65
|
+
while (currentPrefix.includes(".")) {
|
|
66
|
+
const parentDot = currentPrefix.lastIndexOf(".");
|
|
67
|
+
currentPrefix = currentPrefix.substring(0, parentDot);
|
|
68
|
+
const parentPrefixedName = `${currentPrefix}.${lookupName}`;
|
|
69
|
+
if (routeMap[parentPrefixedName] !== undefined) {
|
|
70
|
+
return routeMap[parentPrefixedName];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Fallback: try bare name at root scope only.
|
|
75
|
+
// Routes inside { name: "" } mounts are at root scope — their dot-local
|
|
76
|
+
// names can fall back to bare names (e.g., "sub.detail" reaching "flatIndex").
|
|
77
|
+
// Routes inside named mounts (e.g., { name: "magazine" }) are NOT at root
|
|
78
|
+
// scope — dot-local must not leak into unrelated global names.
|
|
79
|
+
//
|
|
80
|
+
// Resolution order: explicit param > scope registry > heuristic.
|
|
81
|
+
const isRootScoped =
|
|
82
|
+
rootScoped ??
|
|
83
|
+
isRouteRootScoped(currentRoutePrefix) ??
|
|
84
|
+
!currentRoutePrefix.includes(".");
|
|
85
|
+
if (isRootScoped) {
|
|
86
|
+
if (routeMap[lookupName] !== undefined) {
|
|
87
|
+
return routeMap[lookupName];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 2. Unprefixed ("magazine.index", "blog.post") — global resolution only.
|
|
95
|
+
// Direct lookup in the full named-routes map.
|
|
96
|
+
return routeMap[name];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function createPrerenderPassthroughFn(
|
|
100
|
+
build: boolean,
|
|
101
|
+
isPassthroughRoute: boolean,
|
|
102
|
+
): () => typeof PRERENDER_PASSTHROUGH {
|
|
103
|
+
return () => {
|
|
104
|
+
if (!build) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
"ctx.passthrough() can only be called during build-time prerendering.",
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (!isPassthroughRoute) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
"ctx.passthrough() is only available on routes declared with " +
|
|
112
|
+
"{ passthrough: true }. Remove the passthrough() call or add " +
|
|
113
|
+
"{ passthrough: true } to the Prerender options.",
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return PRERENDER_PASSTHROUGH;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Create a reverse function for URL generation from route names.
|
|
122
|
+
* Used by both HandlerContext and MiddlewareContext.
|
|
123
|
+
*
|
|
124
|
+
* When currentParams is provided, those params are used as defaults for URL
|
|
125
|
+
* generation. This enables auto-filling mount params from include() prefixes:
|
|
126
|
+
* inner handlers can call ctx.reverse(".sibling") without explicitly passing
|
|
127
|
+
* params that are already known from the current URL match.
|
|
128
|
+
* Explicitly passed hrefParams take priority over currentParams.
|
|
129
|
+
*/
|
|
130
|
+
export function createReverseFunction(
|
|
131
|
+
routeMap: Record<string, string>,
|
|
132
|
+
currentRoutePrefix?: string,
|
|
133
|
+
currentParams?: Record<string, string>,
|
|
134
|
+
rootScoped?: boolean,
|
|
135
|
+
): (
|
|
136
|
+
name: string,
|
|
137
|
+
hrefParams?: Record<string, string>,
|
|
138
|
+
search?: Record<string, unknown>,
|
|
139
|
+
) => string {
|
|
140
|
+
return (name, hrefParams, search) => {
|
|
141
|
+
// Resolve route name with namespace support
|
|
142
|
+
const pattern = resolveRouteName(
|
|
143
|
+
name,
|
|
144
|
+
routeMap,
|
|
145
|
+
currentRoutePrefix,
|
|
146
|
+
rootScoped,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
if (pattern === undefined) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Unknown route: "${name}"${currentRoutePrefix ? ` (current route: ${currentRoutePrefix})` : ""}`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let result = pattern;
|
|
156
|
+
|
|
157
|
+
// Merge current request params as defaults, explicit params override
|
|
158
|
+
const effectiveParams = currentParams
|
|
159
|
+
? { ...currentParams, ...hrefParams }
|
|
160
|
+
: hrefParams;
|
|
161
|
+
|
|
162
|
+
// Substitute params (strip constraint and optional syntax: :param(a|b)? -> value)
|
|
163
|
+
if (effectiveParams) {
|
|
164
|
+
result = result.replace(
|
|
165
|
+
/:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?\??/g,
|
|
166
|
+
(_, key) => {
|
|
167
|
+
const value = effectiveParams[key];
|
|
168
|
+
if (value === undefined) {
|
|
169
|
+
throw new Error(`Missing param "${key}" for route "${name}"`);
|
|
170
|
+
}
|
|
171
|
+
return encodeURIComponent(value);
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Append search params as query string
|
|
177
|
+
if (search) {
|
|
178
|
+
const qs = serializeSearchParams(search);
|
|
179
|
+
if (qs) result += `?${qs}`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return result;
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Create HandlerContext with typed env/var/get/set
|
|
188
|
+
*/
|
|
189
|
+
export function createHandlerContext<TEnv>(
|
|
190
|
+
params: Record<string, string>,
|
|
191
|
+
request: Request,
|
|
192
|
+
searchParams: URLSearchParams,
|
|
193
|
+
pathname: string,
|
|
194
|
+
url: URL,
|
|
195
|
+
bindings: TEnv = {} as TEnv,
|
|
196
|
+
routeMap: Record<string, string> = {},
|
|
197
|
+
routeName?: string,
|
|
198
|
+
responseType?: string,
|
|
199
|
+
isPassthroughRoute: boolean = false,
|
|
200
|
+
): InternalHandlerContext<any, TEnv> {
|
|
201
|
+
// Get variables from request context - this is the unified context
|
|
202
|
+
// shared between middleware and route handlers
|
|
203
|
+
const requestContext = _getRequestContext();
|
|
204
|
+
const variables: any = requestContext?.var ?? {};
|
|
205
|
+
|
|
206
|
+
// If route has a search schema, parse URLSearchParams into typed object
|
|
207
|
+
const searchSchema = routeName ? getSearchSchema(routeName) : undefined;
|
|
208
|
+
const resolvedSearchParams = searchSchema
|
|
209
|
+
? parseSearchParams(searchParams, searchSchema)
|
|
210
|
+
: searchParams;
|
|
211
|
+
|
|
212
|
+
// Get stub response from request context for setting headers
|
|
213
|
+
const stubResponse =
|
|
214
|
+
requestContext?.res ?? new Response(null, { status: 200 });
|
|
215
|
+
|
|
216
|
+
// Guard mutating Headers methods so they throw inside "use cache" functions.
|
|
217
|
+
// Uses lazy `ctx` reference (assigned below) — only the specific handler ctx
|
|
218
|
+
// is stamped by cache-runtime, not the shared request context.
|
|
219
|
+
const MUTATING_HEADERS_METHODS = new Set(["set", "append", "delete"]);
|
|
220
|
+
let ctx: InternalHandlerContext<any, TEnv>;
|
|
221
|
+
const guardedHeaders = new Proxy(stubResponse.headers, {
|
|
222
|
+
get(target, prop, receiver) {
|
|
223
|
+
const value = Reflect.get(target, prop, receiver);
|
|
224
|
+
if (typeof value === "function") {
|
|
225
|
+
if (MUTATING_HEADERS_METHODS.has(prop as string)) {
|
|
226
|
+
return (...args: any[]) => {
|
|
227
|
+
assertNotInsideCacheExec(ctx, "headers");
|
|
228
|
+
return value.apply(target, args);
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return value.bind(target);
|
|
232
|
+
}
|
|
233
|
+
return value;
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
ctx = {
|
|
238
|
+
params,
|
|
239
|
+
build: false,
|
|
240
|
+
request,
|
|
241
|
+
searchParams,
|
|
242
|
+
search: searchSchema ? resolvedSearchParams : {},
|
|
243
|
+
pathname,
|
|
244
|
+
url,
|
|
245
|
+
originalUrl: new URL(request.url),
|
|
246
|
+
env: bindings,
|
|
247
|
+
var: variables,
|
|
248
|
+
get: ((keyOrVar: any) => contextGet(variables, keyOrVar)) as HandlerContext<
|
|
249
|
+
any,
|
|
250
|
+
TEnv
|
|
251
|
+
>["get"],
|
|
252
|
+
set: ((keyOrVar: any, value: any) => {
|
|
253
|
+
assertNotInsideCacheExec(ctx, "set");
|
|
254
|
+
contextSet(variables, keyOrVar, value);
|
|
255
|
+
}) as HandlerContext<any, TEnv>["set"],
|
|
256
|
+
res: stubResponse, // Stub response for setting headers
|
|
257
|
+
headers: guardedHeaders, // Guarded shorthand for res.headers
|
|
258
|
+
// Placeholder use() - will be replaced with actual implementation during request
|
|
259
|
+
use: () => {
|
|
260
|
+
throw new Error("ctx.use() called before loaders were initialized");
|
|
261
|
+
},
|
|
262
|
+
// Theme support (when enabled via router config)
|
|
263
|
+
theme: requestContext?.theme,
|
|
264
|
+
setTheme: requestContext?.setTheme,
|
|
265
|
+
// Location state support (delegates to request context)
|
|
266
|
+
setLocationState(entries) {
|
|
267
|
+
if (!requestContext) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
"setLocationState() is not available outside a request context",
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
requestContext.setLocationState(entries);
|
|
273
|
+
},
|
|
274
|
+
routeName: (routeName && !isAutoGeneratedRouteName(routeName)
|
|
275
|
+
? routeName
|
|
276
|
+
: undefined) as HandlerContext["routeName"],
|
|
277
|
+
// Scoped reverse for URL generation (auto-fills current request params).
|
|
278
|
+
// Resolve rootScoped eagerly so the reverse function is self-contained
|
|
279
|
+
// and does not depend on the global rootScopeRoutes registry at call time.
|
|
280
|
+
reverse: createReverseFunction(
|
|
281
|
+
routeMap,
|
|
282
|
+
routeName,
|
|
283
|
+
params,
|
|
284
|
+
routeName ? isRouteRootScoped(routeName) : undefined,
|
|
285
|
+
),
|
|
286
|
+
passthrough: createPrerenderPassthroughFn(false, isPassthroughRoute),
|
|
287
|
+
_responseType: responseType,
|
|
288
|
+
_routeName: routeName,
|
|
289
|
+
};
|
|
290
|
+
// Brand with taint symbol so "use cache" excludes ctx from cache keys
|
|
291
|
+
(ctx as any)[NOCACHE_SYMBOL] = true;
|
|
292
|
+
return ctx;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Create a PrerenderContext for Prerender() handlers at build time.
|
|
297
|
+
*
|
|
298
|
+
* Returns an InternalHandlerContext where params, pathname, url, searchParams,
|
|
299
|
+
* search, reverse, and use(handle) work. Request-time properties
|
|
300
|
+
* (request, env, headers, cookies, var, get, set, res) throw with a clear error.
|
|
301
|
+
*/
|
|
302
|
+
export function createPrerenderContext<TEnv>(
|
|
303
|
+
params: Record<string, string>,
|
|
304
|
+
pathname: string,
|
|
305
|
+
routeMap: Record<string, string>,
|
|
306
|
+
routeName?: string,
|
|
307
|
+
buildVars?: Record<string, any>,
|
|
308
|
+
isPassthroughRoute?: boolean,
|
|
309
|
+
): InternalHandlerContext<any, TEnv> {
|
|
310
|
+
const syntheticUrl = new URL(`http://prerender${pathname}`);
|
|
311
|
+
const variables = buildVars ?? {};
|
|
312
|
+
|
|
313
|
+
function throwUnavailable(prop: string): never {
|
|
314
|
+
throw new Error(
|
|
315
|
+
`Property "${prop}" is not available during pre-rendering. ` +
|
|
316
|
+
`Fetch data directly in the handler or use a passthrough prerender handler.`,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
params,
|
|
322
|
+
build: true,
|
|
323
|
+
get request(): Request {
|
|
324
|
+
return throwUnavailable("request");
|
|
325
|
+
},
|
|
326
|
+
searchParams: syntheticUrl.searchParams,
|
|
327
|
+
search: {},
|
|
328
|
+
pathname,
|
|
329
|
+
url: syntheticUrl,
|
|
330
|
+
originalUrl: syntheticUrl,
|
|
331
|
+
get env(): TEnv {
|
|
332
|
+
return throwUnavailable("env");
|
|
333
|
+
},
|
|
334
|
+
get var(): any {
|
|
335
|
+
return throwUnavailable("var");
|
|
336
|
+
},
|
|
337
|
+
get: ((keyOrVar: any) => contextGet(variables, keyOrVar)) as any,
|
|
338
|
+
set: ((keyOrVar: any, value: any) => {
|
|
339
|
+
contextSet(variables, keyOrVar, value);
|
|
340
|
+
}) as any,
|
|
341
|
+
get res(): Response {
|
|
342
|
+
return throwUnavailable("res");
|
|
343
|
+
},
|
|
344
|
+
get headers(): Headers {
|
|
345
|
+
return throwUnavailable("headers");
|
|
346
|
+
},
|
|
347
|
+
// Placeholder use() - replaced by setupBuildUse
|
|
348
|
+
use: () => {
|
|
349
|
+
throw new Error("ctx.use() called before build context was initialized");
|
|
350
|
+
},
|
|
351
|
+
theme: undefined,
|
|
352
|
+
setTheme: undefined,
|
|
353
|
+
routeName: (routeName && !isAutoGeneratedRouteName(routeName)
|
|
354
|
+
? routeName
|
|
355
|
+
: undefined) as HandlerContext["routeName"],
|
|
356
|
+
setLocationState: () => {
|
|
357
|
+
throwUnavailable("setLocationState");
|
|
358
|
+
},
|
|
359
|
+
reverse: createReverseFunction(
|
|
360
|
+
routeMap,
|
|
361
|
+
routeName,
|
|
362
|
+
params,
|
|
363
|
+
routeName ? isRouteRootScoped(routeName) : undefined,
|
|
364
|
+
),
|
|
365
|
+
passthrough: createPrerenderPassthroughFn(
|
|
366
|
+
true,
|
|
367
|
+
isPassthroughRoute === true,
|
|
368
|
+
),
|
|
369
|
+
_routeName: routeName,
|
|
370
|
+
} as InternalHandlerContext<any, TEnv>;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Create a StaticContext for Static() handlers at build time.
|
|
375
|
+
*
|
|
376
|
+
* Returns an InternalHandlerContext where only reverse and use(handle) work.
|
|
377
|
+
* Static handlers have no URL, no params, no pathname — everything else throws.
|
|
378
|
+
*/
|
|
379
|
+
export function createStaticContext<TEnv>(
|
|
380
|
+
routeMap: Record<string, string>,
|
|
381
|
+
routeName?: string,
|
|
382
|
+
): InternalHandlerContext<any, TEnv> {
|
|
383
|
+
const variables: Record<string, any> = {};
|
|
384
|
+
|
|
385
|
+
function throwUnavailable(prop: string): never {
|
|
386
|
+
throw new Error(
|
|
387
|
+
`Property "${prop}" is not available in Static() handlers. ` +
|
|
388
|
+
`Static handlers render content without request context.`,
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return {
|
|
393
|
+
get params(): any {
|
|
394
|
+
return throwUnavailable("params");
|
|
395
|
+
},
|
|
396
|
+
build: true,
|
|
397
|
+
get request(): Request {
|
|
398
|
+
return throwUnavailable("request");
|
|
399
|
+
},
|
|
400
|
+
get searchParams(): URLSearchParams {
|
|
401
|
+
return throwUnavailable("searchParams");
|
|
402
|
+
},
|
|
403
|
+
get search(): any {
|
|
404
|
+
return throwUnavailable("search");
|
|
405
|
+
},
|
|
406
|
+
get pathname(): string {
|
|
407
|
+
return throwUnavailable("pathname");
|
|
408
|
+
},
|
|
409
|
+
get url(): URL {
|
|
410
|
+
return throwUnavailable("url");
|
|
411
|
+
},
|
|
412
|
+
get originalUrl(): URL {
|
|
413
|
+
return throwUnavailable("originalUrl");
|
|
414
|
+
},
|
|
415
|
+
get env(): TEnv {
|
|
416
|
+
return throwUnavailable("env");
|
|
417
|
+
},
|
|
418
|
+
get var(): any {
|
|
419
|
+
return throwUnavailable("var");
|
|
420
|
+
},
|
|
421
|
+
get: ((keyOrVar: any) => contextGet(variables, keyOrVar)) as any,
|
|
422
|
+
set: ((keyOrVar: any, value: any) => {
|
|
423
|
+
contextSet(variables, keyOrVar, value);
|
|
424
|
+
}) as any,
|
|
425
|
+
get res(): Response {
|
|
426
|
+
return throwUnavailable("res");
|
|
427
|
+
},
|
|
428
|
+
get headers(): Headers {
|
|
429
|
+
return throwUnavailable("headers");
|
|
430
|
+
},
|
|
431
|
+
// Placeholder use() - replaced by setupBuildUse
|
|
432
|
+
use: () => {
|
|
433
|
+
throw new Error("ctx.use() called before build context was initialized");
|
|
434
|
+
},
|
|
435
|
+
theme: undefined,
|
|
436
|
+
setTheme: undefined,
|
|
437
|
+
routeName: (routeName && !isAutoGeneratedRouteName(routeName)
|
|
438
|
+
? routeName
|
|
439
|
+
: undefined) as HandlerContext["routeName"],
|
|
440
|
+
setLocationState: () => {
|
|
441
|
+
throwUnavailable("setLocationState");
|
|
442
|
+
},
|
|
443
|
+
reverse: createReverseFunction(
|
|
444
|
+
routeMap,
|
|
445
|
+
routeName,
|
|
446
|
+
undefined,
|
|
447
|
+
routeName ? isRouteRootScoped(routeName) : undefined,
|
|
448
|
+
),
|
|
449
|
+
_routeName: routeName,
|
|
450
|
+
} as InternalHandlerContext<any, TEnv>;
|
|
451
|
+
}
|