@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
package/src/server/context.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
PartialCacheOptions,
|
|
5
|
+
ErrorBoundaryHandler,
|
|
6
|
+
Handler,
|
|
7
|
+
LoaderDefinition,
|
|
8
|
+
MiddlewareFn,
|
|
9
|
+
NotFoundBoundaryHandler,
|
|
10
|
+
ShouldRevalidateFn,
|
|
11
|
+
TransitionConfig,
|
|
12
|
+
} from "../types";
|
|
4
13
|
import { invariant } from "../errors";
|
|
14
|
+
import type { DefaultRouteName } from "../types/global-namespace.js";
|
|
5
15
|
|
|
6
16
|
// ============================================================================
|
|
7
17
|
// Performance Metrics Types
|
|
@@ -13,9 +23,10 @@ import { invariant } from "../errors";
|
|
|
13
23
|
* @internal This type is an implementation detail and may change without notice.
|
|
14
24
|
*/
|
|
15
25
|
export interface PerformanceMetric {
|
|
16
|
-
label: string;
|
|
17
|
-
duration: number;
|
|
18
|
-
startTime: number;
|
|
26
|
+
label: string; // e.g., "route-matching", "loader:UserLoader"
|
|
27
|
+
duration: number; // milliseconds
|
|
28
|
+
startTime: number; // relative to request start
|
|
29
|
+
depth?: number; // nesting level for hierarchical display (0 = top-level)
|
|
19
30
|
}
|
|
20
31
|
|
|
21
32
|
/**
|
|
@@ -55,6 +66,8 @@ export type EntryPropCommon = {
|
|
|
55
66
|
parent: EntryData | null;
|
|
56
67
|
/** Cache configuration for this entry (set by cache() DSL) */
|
|
57
68
|
cache?: EntryCacheConfig;
|
|
69
|
+
/** URL prefix from include() scope, used for MountContext on client */
|
|
70
|
+
mountPath?: string;
|
|
58
71
|
};
|
|
59
72
|
|
|
60
73
|
/**
|
|
@@ -103,12 +116,14 @@ export type InterceptSegmentsState = {
|
|
|
103
116
|
* @internal This type is an implementation detail and may change without notice.
|
|
104
117
|
*/
|
|
105
118
|
export type InterceptSelectorContext<TEnv = any> = {
|
|
106
|
-
from: URL;
|
|
107
|
-
to: URL;
|
|
108
|
-
params: Record<string, string>;
|
|
109
|
-
request: Request;
|
|
110
|
-
env: TEnv;
|
|
111
|
-
segments: InterceptSegmentsState;
|
|
119
|
+
from: URL; // Source URL (where user is coming from)
|
|
120
|
+
to: URL; // Destination URL (where user is navigating to)
|
|
121
|
+
params: Record<string, string>; // Matched route params
|
|
122
|
+
request: Request; // The HTTP request object
|
|
123
|
+
env: TEnv; // Platform bindings (Cloudflare env, etc.)
|
|
124
|
+
segments: InterceptSegmentsState; // Client's current segments (where navigating FROM)
|
|
125
|
+
fromRouteName?: DefaultRouteName; // Named route being navigated away from (undefined for unnamed routes)
|
|
126
|
+
toRouteName?: DefaultRouteName; // Named route being navigated to (undefined for unnamed routes)
|
|
112
127
|
};
|
|
113
128
|
|
|
114
129
|
/**
|
|
@@ -117,7 +132,9 @@ export type InterceptSelectorContext<TEnv = any> = {
|
|
|
117
132
|
*
|
|
118
133
|
* @internal This type is an implementation detail and may change without notice.
|
|
119
134
|
*/
|
|
120
|
-
export type InterceptWhenFn<TEnv = any> = (
|
|
135
|
+
export type InterceptWhenFn<TEnv = any> = (
|
|
136
|
+
ctx: InterceptSelectorContext<TEnv>,
|
|
137
|
+
) => boolean;
|
|
121
138
|
|
|
122
139
|
/**
|
|
123
140
|
* Intercept entry stored in EntryData
|
|
@@ -126,17 +143,18 @@ export type InterceptWhenFn<TEnv = any> = (ctx: InterceptSelectorContext<TEnv>)
|
|
|
126
143
|
* @internal This type is an implementation detail and may change without notice.
|
|
127
144
|
*/
|
|
128
145
|
export type InterceptEntry = {
|
|
129
|
-
slotName: `@${string}`;
|
|
130
|
-
routeName: string;
|
|
131
|
-
handler: ReactNode | Handler<any, any>;
|
|
146
|
+
slotName: `@${string}`; // e.g., "@modal"
|
|
147
|
+
routeName: string; // e.g., "card"
|
|
148
|
+
handler: ReactNode | Handler<any, any, any>;
|
|
132
149
|
middleware: MiddlewareFn<any, any>[];
|
|
133
150
|
revalidate: ShouldRevalidateFn<any, any>[];
|
|
134
151
|
errorBoundary: (ReactNode | ErrorBoundaryHandler)[];
|
|
135
152
|
notFoundBoundary: (ReactNode | NotFoundBoundaryHandler)[];
|
|
136
153
|
loader: LoaderEntry[];
|
|
137
154
|
loading?: ReactNode | false;
|
|
138
|
-
|
|
139
|
-
|
|
155
|
+
transition?: TransitionConfig;
|
|
156
|
+
layout?: ReactNode | Handler<any, any, any>; // Wrapper layout with <Outlet /> for content
|
|
157
|
+
when: InterceptWhenFn[]; // Selector conditions - all must return true to intercept
|
|
140
158
|
};
|
|
141
159
|
|
|
142
160
|
export type EntryPropSegments = {
|
|
@@ -149,36 +167,72 @@ export type EntryPropSegments = {
|
|
|
149
167
|
export type EntryData =
|
|
150
168
|
| ({
|
|
151
169
|
type: "route";
|
|
152
|
-
handler: Handler<any, any>;
|
|
170
|
+
handler: Handler<any, any, any>;
|
|
153
171
|
loading?: ReactNode | false;
|
|
172
|
+
transition?: TransitionConfig;
|
|
154
173
|
/** URL pattern for this route (used by path() in urls()) */
|
|
155
174
|
pattern?: string;
|
|
175
|
+
/** Set when handler is a Prerender definition */
|
|
176
|
+
isPrerender?: true;
|
|
177
|
+
/** Original PrerenderHandlerDefinition (for build-time getParams access) */
|
|
178
|
+
prerenderDef?: {
|
|
179
|
+
getParams?: (ctx: any) => Promise<any[]> | any[];
|
|
180
|
+
options?: { passthrough?: boolean };
|
|
181
|
+
};
|
|
182
|
+
/** Set when handler is a Static definition (build-time only) */
|
|
183
|
+
isStaticPrerender?: true;
|
|
184
|
+
/** Static handler $$id for build-time store lookup */
|
|
185
|
+
staticHandlerId?: string;
|
|
186
|
+
/** Response type for non-RSC routes (json, text, image, any) */
|
|
187
|
+
responseType?: string;
|
|
156
188
|
} & EntryPropCommon &
|
|
157
189
|
EntryPropDatas &
|
|
158
190
|
EntryPropSegments)
|
|
159
191
|
| ({
|
|
160
192
|
type: "layout";
|
|
161
|
-
handler: ReactNode | Handler<any, any>;
|
|
193
|
+
handler: ReactNode | Handler<any, any, any>;
|
|
162
194
|
loading?: ReactNode | false;
|
|
195
|
+
transition?: TransitionConfig;
|
|
196
|
+
/** Set when handler is a Static definition (build-time only) */
|
|
197
|
+
isStaticPrerender?: true;
|
|
198
|
+
/** Static handler $$id for build-time store lookup */
|
|
199
|
+
staticHandlerId?: string;
|
|
163
200
|
} & EntryPropCommon &
|
|
164
201
|
EntryPropDatas &
|
|
165
202
|
EntryPropSegments)
|
|
166
203
|
| ({
|
|
167
204
|
type: "parallel";
|
|
168
|
-
handler: Record<`@${string}`, Handler<any, any> | ReactNode>;
|
|
205
|
+
handler: Record<`@${string}`, Handler<any, any, any> | ReactNode>;
|
|
169
206
|
loading?: ReactNode | false;
|
|
207
|
+
transition?: TransitionConfig;
|
|
208
|
+
/** Set when any parallel slot is a Static definition */
|
|
209
|
+
isStaticPrerender?: true;
|
|
210
|
+
/** Per-slot static handler $$ids for build-time store lookup */
|
|
211
|
+
staticHandlerIds?: Record<string, string>;
|
|
170
212
|
} & EntryPropCommon &
|
|
171
213
|
EntryPropDatas &
|
|
172
214
|
EntryPropSegments)
|
|
173
215
|
| ({
|
|
174
216
|
type: "cache";
|
|
175
217
|
/** Cache entries create cache boundaries and render like layouts (with Outlet) */
|
|
176
|
-
handler: ReactNode | Handler<any, any>;
|
|
218
|
+
handler: ReactNode | Handler<any, any, any>;
|
|
177
219
|
loading?: ReactNode | false;
|
|
220
|
+
transition?: TransitionConfig;
|
|
178
221
|
} & EntryPropCommon &
|
|
179
222
|
EntryPropDatas &
|
|
180
223
|
EntryPropSegments);
|
|
181
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Tracked include info for build-time manifest generation
|
|
227
|
+
*/
|
|
228
|
+
export interface TrackedInclude {
|
|
229
|
+
prefix: string;
|
|
230
|
+
fullPrefix: string;
|
|
231
|
+
namePrefix?: string;
|
|
232
|
+
patterns: unknown; // UrlPatterns
|
|
233
|
+
lazy: boolean;
|
|
234
|
+
}
|
|
235
|
+
|
|
182
236
|
/**
|
|
183
237
|
* Context stored in AsyncLocalStorage
|
|
184
238
|
*/
|
|
@@ -194,17 +248,38 @@ interface HelperContext {
|
|
|
194
248
|
isSSR?: boolean;
|
|
195
249
|
/** URL patterns map for path() routes (route name -> pattern) */
|
|
196
250
|
patterns?: Map<string, string>;
|
|
251
|
+
/** URL patterns grouped by include prefix for separate entry creation */
|
|
252
|
+
patternsByPrefix?: Map<string, Map<string, string>>;
|
|
197
253
|
/** Trailing slash config per route name */
|
|
198
254
|
trailingSlash?: Map<string, "never" | "always" | "ignore">;
|
|
255
|
+
/** Search param schemas per route name */
|
|
256
|
+
searchSchemas?: Map<string, Record<string, string>>;
|
|
199
257
|
/** URL prefix from include() - applied to all path() patterns */
|
|
200
258
|
urlPrefix?: string;
|
|
201
259
|
/** Name prefix from include() - applied to all named routes */
|
|
202
260
|
namePrefix?: string;
|
|
261
|
+
/** True when this scope is at root level (no named include boundary above).
|
|
262
|
+
* Routes at root scope allow dot-local reverse to fall back to bare names. */
|
|
263
|
+
rootScoped?: boolean;
|
|
203
264
|
/** Run helper for cleaner middleware code */
|
|
204
265
|
run?: <T>(fn: () => T | Promise<T>) => T | Promise<T>;
|
|
266
|
+
/** Tracked includes for build-time manifest generation */
|
|
267
|
+
trackedIncludes?: TrackedInclude[];
|
|
268
|
+
/** Cache profiles for DSL-time cache("profileName") resolution */
|
|
269
|
+
cacheProfiles?: Record<
|
|
270
|
+
string,
|
|
271
|
+
import("../cache/profile-registry.js").CacheProfile
|
|
272
|
+
>;
|
|
205
273
|
}
|
|
206
|
-
|
|
207
|
-
|
|
274
|
+
// Use a global symbol key so the AsyncLocalStorage instance survives HMR
|
|
275
|
+
// module re-evaluation. Without this, Vite's RSC module runner may create
|
|
276
|
+
// a new instance when context.ts is re-evaluated, while other modules still
|
|
277
|
+
// hold references to the old instance — causing getStore() to return
|
|
278
|
+
// undefined even inside a run() callback.
|
|
279
|
+
const RSC_CONTEXT_KEY = Symbol.for("rangojs-router:rsc-context");
|
|
280
|
+
export const RSCRouterContext: AsyncLocalStorage<HelperContext> = ((
|
|
281
|
+
globalThis as any
|
|
282
|
+
)[RSC_CONTEXT_KEY] ??= new AsyncLocalStorage<HelperContext>());
|
|
208
283
|
|
|
209
284
|
export const getContext = (): {
|
|
210
285
|
context: AsyncLocalStorage<HelperContext>;
|
|
@@ -212,21 +287,21 @@ export const getContext = (): {
|
|
|
212
287
|
getParent: () => EntryData | null;
|
|
213
288
|
getOrCreateStore: (forRoute?: string) => HelperContext;
|
|
214
289
|
getNextIndex: (
|
|
215
|
-
type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate"
|
|
290
|
+
type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate",
|
|
216
291
|
) => string;
|
|
217
292
|
getShortCode: (
|
|
218
|
-
type: "layout" | "parallel" | "route" | "loader" | "cache"
|
|
293
|
+
type: "layout" | "parallel" | "route" | "loader" | "cache",
|
|
219
294
|
) => string;
|
|
220
295
|
run: <T>(
|
|
221
296
|
namespace: string,
|
|
222
297
|
parent: EntryData | null,
|
|
223
|
-
callback: (...args: any[]) => T
|
|
298
|
+
callback: (...args: any[]) => T,
|
|
224
299
|
) => T;
|
|
225
300
|
runWithStore: <T>(
|
|
226
301
|
store: HelperContext,
|
|
227
302
|
namespace: string,
|
|
228
303
|
parent: EntryData | null,
|
|
229
|
-
callback: (...args: any[]) => T
|
|
304
|
+
callback: (...args: any[]) => T,
|
|
230
305
|
) => T;
|
|
231
306
|
} => {
|
|
232
307
|
const context = RSCRouterContext;
|
|
@@ -243,7 +318,9 @@ export const getContext = (): {
|
|
|
243
318
|
forRoute,
|
|
244
319
|
counters: {},
|
|
245
320
|
patterns: new Map<string, string>(),
|
|
321
|
+
patternsByPrefix: new Map<string, Map<string, string>>(),
|
|
246
322
|
trailingSlash: new Map<string, "never" | "always" | "ignore">(),
|
|
323
|
+
searchSchemas: new Map<string, Record<string, string>>(),
|
|
247
324
|
} satisfies HelperContext;
|
|
248
325
|
}
|
|
249
326
|
return store;
|
|
@@ -252,7 +329,7 @@ export const getContext = (): {
|
|
|
252
329
|
const store = context.getStore();
|
|
253
330
|
if (!store) {
|
|
254
331
|
throw new Error(
|
|
255
|
-
"RSC Router context store is not available. Make sure to run within RSC Router context."
|
|
332
|
+
"RSC Router context store is not available. Make sure to run within RSC Router context.",
|
|
256
333
|
);
|
|
257
334
|
}
|
|
258
335
|
return store;
|
|
@@ -266,7 +343,7 @@ export const getContext = (): {
|
|
|
266
343
|
return store.parent;
|
|
267
344
|
},
|
|
268
345
|
getNextIndex: (
|
|
269
|
-
type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate"
|
|
346
|
+
type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate",
|
|
270
347
|
) => {
|
|
271
348
|
const store = context.getStore();
|
|
272
349
|
invariant(store, "No context RSCRouterContext available");
|
|
@@ -275,17 +352,31 @@ export const getContext = (): {
|
|
|
275
352
|
store.counters[type] = index + 1;
|
|
276
353
|
return `$${type}.${index}`;
|
|
277
354
|
},
|
|
278
|
-
getShortCode: (
|
|
355
|
+
getShortCode: (
|
|
356
|
+
type: "layout" | "parallel" | "route" | "loader" | "cache",
|
|
357
|
+
) => {
|
|
279
358
|
const store = context.getStore();
|
|
280
359
|
invariant(store, "No context RSCRouterContext available");
|
|
281
360
|
|
|
282
361
|
const parent = store.parent;
|
|
283
|
-
const prefix =
|
|
284
|
-
|
|
362
|
+
const prefix =
|
|
363
|
+
type === "layout"
|
|
364
|
+
? "L"
|
|
365
|
+
: type === "parallel"
|
|
366
|
+
? "P"
|
|
367
|
+
: type === "loader"
|
|
368
|
+
? "D"
|
|
369
|
+
: type === "cache"
|
|
370
|
+
? "C"
|
|
371
|
+
: "R";
|
|
372
|
+
const mountPrefix =
|
|
373
|
+
store.mountIndex !== undefined ? `M${store.mountIndex}` : "";
|
|
285
374
|
|
|
286
375
|
if (!parent) {
|
|
287
376
|
// Root entry: prefix with mount index and use mount-scoped counter
|
|
288
|
-
const counterKey = mountPrefix
|
|
377
|
+
const counterKey = mountPrefix
|
|
378
|
+
? `${mountPrefix}_root_${type}`
|
|
379
|
+
: `root_${type}`;
|
|
289
380
|
store.counters[counterKey] ??= 0;
|
|
290
381
|
const index = store.counters[counterKey];
|
|
291
382
|
store.counters[counterKey] = index + 1;
|
|
@@ -303,7 +394,7 @@ export const getContext = (): {
|
|
|
303
394
|
store: HelperContext,
|
|
304
395
|
namespace: string,
|
|
305
396
|
parent: EntryData | null,
|
|
306
|
-
callback: (...args: any[]) => T
|
|
397
|
+
callback: (...args: any[]) => T,
|
|
307
398
|
): T => {
|
|
308
399
|
return context.run(
|
|
309
400
|
{
|
|
@@ -317,23 +408,32 @@ export const getContext = (): {
|
|
|
317
408
|
isSSR: store.isSSR,
|
|
318
409
|
patterns: store.patterns,
|
|
319
410
|
trailingSlash: store.trailingSlash,
|
|
411
|
+
searchSchemas: store.searchSchemas,
|
|
320
412
|
urlPrefix: store.urlPrefix,
|
|
321
413
|
namePrefix: store.namePrefix,
|
|
414
|
+
rootScoped: store.rootScoped,
|
|
415
|
+
trackedIncludes: store.trackedIncludes,
|
|
416
|
+
cacheProfiles: store.cacheProfiles,
|
|
322
417
|
},
|
|
323
|
-
callback
|
|
418
|
+
callback,
|
|
324
419
|
);
|
|
325
420
|
},
|
|
326
421
|
run: <T>(
|
|
327
422
|
namespace: string,
|
|
328
423
|
parent: EntryData | null,
|
|
329
|
-
callback: (...args: any[]) => T
|
|
424
|
+
callback: (...args: any[]) => T,
|
|
330
425
|
) => {
|
|
331
426
|
const store = context.getStore();
|
|
332
427
|
// Preserve parent counters to ensure globally unique shortCodes
|
|
333
428
|
const counters = store?.counters || {};
|
|
334
429
|
const manifest = store ? store.manifest : new Map<string, EntryData>();
|
|
335
430
|
const patterns = store?.patterns || new Map<string, string>();
|
|
336
|
-
const
|
|
431
|
+
const patternsByPrefix = store?.patternsByPrefix;
|
|
432
|
+
const trailingSlash =
|
|
433
|
+
store?.trailingSlash ||
|
|
434
|
+
new Map<string, "never" | "always" | "ignore">();
|
|
435
|
+
const searchSchemas =
|
|
436
|
+
store?.searchSchemas || new Map<string, Record<string, string>>();
|
|
337
437
|
return context.run(
|
|
338
438
|
{
|
|
339
439
|
manifest,
|
|
@@ -345,11 +445,16 @@ export const getContext = (): {
|
|
|
345
445
|
metrics: store?.metrics,
|
|
346
446
|
isSSR: store?.isSSR,
|
|
347
447
|
patterns,
|
|
448
|
+
patternsByPrefix,
|
|
348
449
|
trailingSlash,
|
|
450
|
+
searchSchemas,
|
|
349
451
|
urlPrefix: store?.urlPrefix,
|
|
350
452
|
namePrefix: store?.namePrefix,
|
|
453
|
+
rootScoped: store?.rootScoped,
|
|
454
|
+
trackedIncludes: store?.trackedIncludes,
|
|
455
|
+
cacheProfiles: store?.cacheProfiles,
|
|
351
456
|
},
|
|
352
|
-
callback
|
|
457
|
+
callback,
|
|
353
458
|
);
|
|
354
459
|
},
|
|
355
460
|
};
|
|
@@ -362,30 +467,61 @@ export const getContext = (): {
|
|
|
362
467
|
export function runWithPrefixes<T>(
|
|
363
468
|
urlPrefix: string,
|
|
364
469
|
namePrefix: string | undefined,
|
|
365
|
-
callback: () => T
|
|
470
|
+
callback: () => T,
|
|
366
471
|
): T {
|
|
367
472
|
const store = RSCRouterContext.getStore();
|
|
368
473
|
if (!store) {
|
|
369
474
|
throw new Error("runWithPrefixes must be called within router context");
|
|
370
475
|
}
|
|
371
476
|
|
|
372
|
-
// Combine prefixes if there are existing ones
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
477
|
+
// Combine prefixes if there are existing ones, avoiding double slashes
|
|
478
|
+
let combinedUrlPrefix: string;
|
|
479
|
+
if (store.urlPrefix) {
|
|
480
|
+
if (store.urlPrefix.endsWith("/") && urlPrefix.startsWith("/")) {
|
|
481
|
+
combinedUrlPrefix = store.urlPrefix + urlPrefix.slice(1);
|
|
482
|
+
} else {
|
|
483
|
+
combinedUrlPrefix = store.urlPrefix + urlPrefix;
|
|
484
|
+
}
|
|
485
|
+
} else {
|
|
486
|
+
combinedUrlPrefix = urlPrefix;
|
|
487
|
+
}
|
|
488
|
+
const combinedNamePrefix =
|
|
489
|
+
namePrefix !== undefined
|
|
490
|
+
? namePrefix === ""
|
|
491
|
+
? store.namePrefix
|
|
492
|
+
: store.namePrefix
|
|
493
|
+
? `${store.namePrefix}.${namePrefix}`
|
|
494
|
+
: namePrefix
|
|
495
|
+
: store.namePrefix;
|
|
496
|
+
|
|
497
|
+
// Track root scope for dot-local reverse resolution.
|
|
498
|
+
//
|
|
499
|
+
// The flag answers: "can this route reach bare names at root scope?"
|
|
500
|
+
// It propagates through the include chain:
|
|
501
|
+
//
|
|
502
|
+
// { name: "" } — transparent: inherit parent, default true
|
|
503
|
+
// { name: "foo" } — inherit parent if already set, else create boundary (false)
|
|
504
|
+
// no name — inherit parent unchanged
|
|
505
|
+
//
|
|
506
|
+
// This means { name: "" } + nested { name: "sub" } keeps rootScoped=true
|
|
507
|
+
// (the outer transparent include establishes root access, and the inner
|
|
508
|
+
// named include inherits it). But a direct { name: "sub" } at root gets
|
|
509
|
+
// rootScoped=false (no prior root-access grant, so it creates a boundary).
|
|
510
|
+
const combinedRootScoped =
|
|
511
|
+
namePrefix === ""
|
|
512
|
+
? (store.rootScoped ?? true)
|
|
513
|
+
: namePrefix !== undefined
|
|
514
|
+
? (store.rootScoped ?? false)
|
|
515
|
+
: store.rootScoped;
|
|
381
516
|
|
|
382
517
|
return RSCRouterContext.run(
|
|
383
518
|
{
|
|
384
519
|
...store,
|
|
385
520
|
urlPrefix: combinedUrlPrefix,
|
|
386
521
|
namePrefix: combinedNamePrefix,
|
|
522
|
+
rootScoped: combinedRootScoped,
|
|
387
523
|
},
|
|
388
|
-
callback
|
|
524
|
+
callback,
|
|
389
525
|
);
|
|
390
526
|
}
|
|
391
527
|
|
|
@@ -405,6 +541,15 @@ export function getNamePrefix(): string | undefined {
|
|
|
405
541
|
return store?.namePrefix;
|
|
406
542
|
}
|
|
407
543
|
|
|
544
|
+
/**
|
|
545
|
+
* Get whether the current scope is at root level (no named include boundary above).
|
|
546
|
+
* Returns true at root or inside { name: "" } includes, false inside named includes.
|
|
547
|
+
*/
|
|
548
|
+
export function getRootScoped(): boolean {
|
|
549
|
+
const store = RSCRouterContext.getStore();
|
|
550
|
+
return store?.rootScoped ?? true;
|
|
551
|
+
}
|
|
552
|
+
|
|
408
553
|
// Export HelperContext type for use in other modules
|
|
409
554
|
export type { HelperContext };
|
|
410
555
|
|
|
@@ -423,7 +568,7 @@ export type { HelperContext };
|
|
|
423
568
|
* done(); // Records duration
|
|
424
569
|
* ```
|
|
425
570
|
*/
|
|
426
|
-
export function track(label: string): () => void {
|
|
571
|
+
export function track(label: string, depth?: number): () => void {
|
|
427
572
|
const store = RSCRouterContext.getStore();
|
|
428
573
|
|
|
429
574
|
// No-op if context unavailable or metrics not enabled
|
|
@@ -434,7 +579,13 @@ export function track(label: string): () => void {
|
|
|
434
579
|
const startTime = performance.now() - store.metrics.requestStart;
|
|
435
580
|
|
|
436
581
|
return () => {
|
|
437
|
-
const duration =
|
|
438
|
-
|
|
582
|
+
const duration =
|
|
583
|
+
performance.now() - store.metrics!.requestStart - startTime;
|
|
584
|
+
store.metrics!.metrics.push({
|
|
585
|
+
label,
|
|
586
|
+
duration,
|
|
587
|
+
startTime,
|
|
588
|
+
...(depth != null ? { depth } : {}),
|
|
589
|
+
});
|
|
439
590
|
};
|
|
440
591
|
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cookie Store — Next.js-style cookie facade backed by the response-derived model.
|
|
3
|
+
*
|
|
4
|
+
* `cookies()` returns a CookieStore scoped to the current request.
|
|
5
|
+
* Reads merge the original Cookie header with Set-Cookie mutations
|
|
6
|
+
* already queued on the response stub (last-write-wins).
|
|
7
|
+
* Writes append Set-Cookie to the response stub.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { CookieOptions } from "../router/middleware-types.js";
|
|
11
|
+
import { getRequestContext } from "./request-context.js";
|
|
12
|
+
import { INSIDE_CACHE_EXEC } from "../cache/taint.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A single cookie entry returned by get() and getAll().
|
|
16
|
+
*/
|
|
17
|
+
export interface Cookie {
|
|
18
|
+
name: string;
|
|
19
|
+
value: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Request-scoped cookie store.
|
|
24
|
+
*
|
|
25
|
+
* Reads see the effective merged view (original request + same-request mutations).
|
|
26
|
+
* Writes append Set-Cookie headers to the shared response stub.
|
|
27
|
+
*/
|
|
28
|
+
export interface CookieStore {
|
|
29
|
+
/** Get a single cookie by name. Returns undefined if not set or deleted. */
|
|
30
|
+
get(name: string): Cookie | undefined;
|
|
31
|
+
|
|
32
|
+
/** Get all effective cookies, or all cookies with a given name. */
|
|
33
|
+
getAll(name?: string): Cookie[];
|
|
34
|
+
|
|
35
|
+
/** Check whether a cookie exists in the effective view. */
|
|
36
|
+
has(name: string): boolean;
|
|
37
|
+
|
|
38
|
+
/** Set a cookie (appends Set-Cookie to the response stub). */
|
|
39
|
+
set(name: string, value: string, options?: CookieOptions): void;
|
|
40
|
+
|
|
41
|
+
/** Delete a cookie (appends Set-Cookie with maxAge=0 to the response stub). */
|
|
42
|
+
delete(name: string, options?: Pick<CookieOptions, "domain" | "path">): void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get the request-scoped cookie store.
|
|
47
|
+
*
|
|
48
|
+
* Must be called inside a request context (middleware, handler, loader, action).
|
|
49
|
+
* Throws if called outside request scope.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* import { cookies } from "@rangojs/router";
|
|
54
|
+
*
|
|
55
|
+
* // In a handler, loader, or action:
|
|
56
|
+
* const session = cookies().get("session")?.value;
|
|
57
|
+
* cookies().set("session", "new-token", { httpOnly: true });
|
|
58
|
+
* cookies().delete("session");
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export function cookies(): CookieStore {
|
|
62
|
+
const ctx = getRequestContext();
|
|
63
|
+
assertNotInsideCacheContext(ctx, "cookies");
|
|
64
|
+
return createCookieStore(ctx);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Read-only view of HTTP headers.
|
|
69
|
+
* Exposes only the read methods of the Headers API.
|
|
70
|
+
*/
|
|
71
|
+
export interface ReadonlyHeaders {
|
|
72
|
+
get(name: string): string | null;
|
|
73
|
+
has(name: string): boolean;
|
|
74
|
+
entries(): HeadersIterator<[string, string]>;
|
|
75
|
+
keys(): HeadersIterator<string>;
|
|
76
|
+
values(): HeadersIterator<string>;
|
|
77
|
+
forEach(
|
|
78
|
+
callback: (value: string, name: string, parent: ReadonlyHeaders) => void,
|
|
79
|
+
): void;
|
|
80
|
+
[Symbol.iterator](): HeadersIterator<[string, string]>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Minimal iterator interface (avoids pulling IterableIterator from lib.dom)
|
|
84
|
+
type HeadersIterator<T> = IterableIterator<T>;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Throw if called inside a "use cache" function.
|
|
88
|
+
* Reading request-scoped data (cookies, headers) inside a cached function
|
|
89
|
+
* produces results that vary per request but the cache key does not include
|
|
90
|
+
* those values, leading to one user's data being served to another.
|
|
91
|
+
*/
|
|
92
|
+
function assertNotInsideCacheContext(ctx: unknown, fnName: string): void {
|
|
93
|
+
if (
|
|
94
|
+
ctx !== null &&
|
|
95
|
+
ctx !== undefined &&
|
|
96
|
+
typeof ctx === "object" &&
|
|
97
|
+
(INSIDE_CACHE_EXEC as symbol) in (ctx as Record<symbol, unknown>)
|
|
98
|
+
) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`${fnName}() cannot be called inside a "use cache" function. ` +
|
|
101
|
+
`Request-scoped data (cookies, headers) varies per request but is not ` +
|
|
102
|
+
`reflected in the cache key, so cached results would be served to the ` +
|
|
103
|
+
`wrong users. Extract the value before the cached function and pass it ` +
|
|
104
|
+
`as an argument:\n\n` +
|
|
105
|
+
` const locale = cookies().get("locale")?.value ?? "en";\n` +
|
|
106
|
+
` const data = await getCachedData(locale); // locale is now in the cache key`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const HEADERS_MUTATION_METHODS = new Set(["set", "append", "delete"]);
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get the original request headers (read-only).
|
|
115
|
+
*
|
|
116
|
+
* Must be called inside a request context.
|
|
117
|
+
* Returns a read-only view of the incoming request's headers.
|
|
118
|
+
* Mutation methods (set, append, delete) throw at runtime.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* import { headers } from "@rangojs/router";
|
|
123
|
+
*
|
|
124
|
+
* const auth = headers().get("authorization");
|
|
125
|
+
* const contentType = headers().get("content-type");
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export function headers(): ReadonlyHeaders {
|
|
129
|
+
const ctx = getRequestContext();
|
|
130
|
+
assertNotInsideCacheContext(ctx, "headers");
|
|
131
|
+
return new Proxy(ctx.request.headers, {
|
|
132
|
+
get(target, prop, receiver) {
|
|
133
|
+
if (typeof prop === "string" && HEADERS_MUTATION_METHODS.has(prop)) {
|
|
134
|
+
return () => {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`headers().${prop}() is not allowed. headers() returns a read-only view of request headers. ` +
|
|
137
|
+
`Use ctx.header() to set response headers.`,
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const value = Reflect.get(target, prop, receiver);
|
|
142
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
143
|
+
},
|
|
144
|
+
}) as unknown as ReadonlyHeaders;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Create a CookieStore backed by a RequestContext.
|
|
149
|
+
* @internal Shared between cookies() shorthand and context methods.
|
|
150
|
+
*/
|
|
151
|
+
function createCookieStore(ctx: {
|
|
152
|
+
cookie(name: string): string | undefined;
|
|
153
|
+
cookies(): Record<string, string>;
|
|
154
|
+
setCookie(name: string, value: string, options?: CookieOptions): void;
|
|
155
|
+
deleteCookie(
|
|
156
|
+
name: string,
|
|
157
|
+
options?: Pick<CookieOptions, "domain" | "path">,
|
|
158
|
+
): void;
|
|
159
|
+
}): CookieStore {
|
|
160
|
+
return {
|
|
161
|
+
get(name: string): Cookie | undefined {
|
|
162
|
+
const value = ctx.cookie(name);
|
|
163
|
+
return value !== undefined ? { name, value } : undefined;
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
getAll(name?: string): Cookie[] {
|
|
167
|
+
const all = ctx.cookies();
|
|
168
|
+
if (name !== undefined) {
|
|
169
|
+
const value = all[name];
|
|
170
|
+
return value !== undefined ? [{ name, value }] : [];
|
|
171
|
+
}
|
|
172
|
+
return Object.entries(all).map(([n, v]) => ({ name: n, value: v }));
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
has(name: string): boolean {
|
|
176
|
+
return ctx.cookie(name) !== undefined;
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
set(name: string, value: string, options?: CookieOptions): void {
|
|
180
|
+
ctx.setCookie(name, value, options);
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
delete(
|
|
184
|
+
name: string,
|
|
185
|
+
options?: Pick<CookieOptions, "domain" | "path">,
|
|
186
|
+
): void {
|
|
187
|
+
ctx.deleteCookie(name, options);
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|