@rangojs/router 0.0.0-experimental.7 → 0.0.0-experimental.71
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 +942 -4
- package/dist/bin/rango.js +1689 -0
- package/dist/vite/index.js +4951 -930
- package/package.json +70 -60
- package/skills/breadcrumbs/SKILL.md +250 -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 +167 -0
- package/skills/hooks/SKILL.md +334 -72
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +131 -8
- package/skills/layout/SKILL.md +100 -3
- package/skills/links/SKILL.md +92 -31
- package/skills/loader/SKILL.md +404 -44
- package/skills/middleware/SKILL.md +173 -34
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +204 -1
- package/skills/prerender/SKILL.md +685 -0
- package/skills/rango/SKILL.md +85 -16
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +257 -14
- package/skills/router-setup/SKILL.md +210 -32
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +328 -89
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +102 -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/app-version.ts +14 -0
- package/src/browser/event-controller.ts +92 -64
- 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 +296 -558
- package/src/browser/navigation-client.ts +179 -69
- package/src/browser/navigation-store.ts +73 -55
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +328 -313
- package/src/browser/prefetch/cache.ts +206 -0
- package/src/browser/prefetch/fetch.ts +150 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +160 -0
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +230 -74
- package/src/browser/react/NavigationProvider.tsx +87 -11
- package/src/browser/react/context.ts +11 -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 +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 -126
- package/src/browser/react/use-href.tsx +2 -2
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +22 -63
- 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 +76 -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 +214 -58
- package/src/browser/scroll-restoration.ts +127 -52
- package/src/browser/segment-reconciler.ts +221 -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 +141 -48
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +235 -24
- package/src/build/generate-route-types.ts +39 -0
- package/src/build/index.ts +13 -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 +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 -309
- package/src/cache/cf/cf-cache-store.ts +571 -17
- 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 +3 -1
- package/src/client.tsx +105 -179
- 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 +108 -2
- package/src/handle.ts +55 -29
- 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 +119 -29
- package/src/index.rsc.ts +155 -19
- package/src/index.ts +223 -30
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +26 -157
- 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 +186 -0
- package/src/prerender.ts +524 -0
- package/src/reverse.ts +351 -0
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +982 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +434 -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 +149 -0
- package/src/route-definition.ts +1 -1428
- package/src/route-map-builder.ts +217 -123
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +70 -8
- 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 +435 -86
- package/src/router/intercept-resolution.ts +402 -0
- package/src/router/lazy-includes.ts +237 -0
- package/src/router/loader-resolution.ts +356 -128
- package/src/router/logging.ts +251 -0
- package/src/router/manifest.ts +154 -35
- package/src/router/match-api.ts +555 -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 +108 -93
- package/src/router/match-middleware/cache-lookup.ts +459 -10
- package/src/router/match-middleware/cache-store.ts +98 -26
- package/src/router/match-middleware/intercept-resolution.ts +57 -17
- package/src/router/match-middleware/segment-resolution.ts +80 -6
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +135 -35
- package/src/router/metrics.ts +240 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +220 -0
- package/src/router/middleware.ts +324 -369
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +211 -43
- 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 +137 -38
- 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 +748 -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 +1379 -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 +78 -3
- package/src/router.ts +740 -4252
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +907 -797
- package/src/rsc/helpers.ts +140 -6
- 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 +391 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +246 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +356 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +46 -11
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +165 -17
- package/src/server/context.ts +315 -58
- 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 +607 -81
- package/src/server.ts +35 -130
- package/src/ssr/index.tsx +103 -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 +791 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +210 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +109 -0
- package/src/types/segments.ts +151 -0
- package/src/types.ts +1 -1623
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +346 -0
- package/src/urls/path-helper.ts +364 -0
- package/src/urls/pattern-types.ts +107 -0
- package/src/urls/response-types.ts +116 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -802
- package/src/use-loader.tsx +161 -81
- package/src/vite/discovery/bundle-postprocess.ts +181 -0
- package/src/vite/discovery/discover-routers.ts +348 -0
- package/src/vite/discovery/prerender-collection.ts +439 -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 +117 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +15 -1129
- package/src/vite/plugin-types.ts +103 -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 -53
- 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 +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +786 -0
- package/src/vite/plugins/performance-tracks.ts +88 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -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/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +462 -0
- package/src/vite/router-discovery.ts +918 -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 +207 -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/href.ts +0 -255
- package/src/server/route-manifest-cache.ts +0 -173
- 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/{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
|
/**
|
|
@@ -105,12 +116,14 @@ export type InterceptSegmentsState = {
|
|
|
105
116
|
* @internal This type is an implementation detail and may change without notice.
|
|
106
117
|
*/
|
|
107
118
|
export type InterceptSelectorContext<TEnv = any> = {
|
|
108
|
-
from: URL;
|
|
109
|
-
to: URL;
|
|
110
|
-
params: Record<string, string>;
|
|
111
|
-
request: Request;
|
|
112
|
-
env: TEnv;
|
|
113
|
-
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)
|
|
114
127
|
};
|
|
115
128
|
|
|
116
129
|
/**
|
|
@@ -119,7 +132,9 @@ export type InterceptSelectorContext<TEnv = any> = {
|
|
|
119
132
|
*
|
|
120
133
|
* @internal This type is an implementation detail and may change without notice.
|
|
121
134
|
*/
|
|
122
|
-
export type InterceptWhenFn<TEnv = any> = (
|
|
135
|
+
export type InterceptWhenFn<TEnv = any> = (
|
|
136
|
+
ctx: InterceptSelectorContext<TEnv>,
|
|
137
|
+
) => boolean;
|
|
123
138
|
|
|
124
139
|
/**
|
|
125
140
|
* Intercept entry stored in EntryData
|
|
@@ -128,55 +143,88 @@ export type InterceptWhenFn<TEnv = any> = (ctx: InterceptSelectorContext<TEnv>)
|
|
|
128
143
|
* @internal This type is an implementation detail and may change without notice.
|
|
129
144
|
*/
|
|
130
145
|
export type InterceptEntry = {
|
|
131
|
-
slotName: `@${string}`;
|
|
132
|
-
routeName: string;
|
|
133
|
-
handler: ReactNode | Handler<any, any>;
|
|
146
|
+
slotName: `@${string}`; // e.g., "@modal"
|
|
147
|
+
routeName: string; // e.g., "card"
|
|
148
|
+
handler: ReactNode | Handler<any, any, any>;
|
|
134
149
|
middleware: MiddlewareFn<any, any>[];
|
|
135
150
|
revalidate: ShouldRevalidateFn<any, any>[];
|
|
136
151
|
errorBoundary: (ReactNode | ErrorBoundaryHandler)[];
|
|
137
152
|
notFoundBoundary: (ReactNode | NotFoundBoundaryHandler)[];
|
|
138
153
|
loader: LoaderEntry[];
|
|
139
154
|
loading?: ReactNode | false;
|
|
140
|
-
|
|
141
|
-
|
|
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
|
|
142
158
|
};
|
|
143
159
|
|
|
160
|
+
export interface ParallelEntryData
|
|
161
|
+
extends EntryPropCommon, EntryPropDatas, EntryPropSegments {
|
|
162
|
+
type: "parallel";
|
|
163
|
+
handler: Record<`@${string}`, Handler<any, any, any> | ReactNode>;
|
|
164
|
+
loading?: ReactNode | false;
|
|
165
|
+
transition?: TransitionConfig;
|
|
166
|
+
/** Set when any parallel slot is a Static definition */
|
|
167
|
+
isStaticPrerender?: true;
|
|
168
|
+
/** Per-slot static handler $$ids for build-time store lookup */
|
|
169
|
+
staticHandlerIds?: Record<string, string>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export type ParallelEntries = Partial<Record<`@${string}`, ParallelEntryData>>;
|
|
173
|
+
|
|
144
174
|
export type EntryPropSegments = {
|
|
145
175
|
loader: LoaderEntry[];
|
|
146
176
|
layout: EntryData[];
|
|
147
|
-
parallel:
|
|
177
|
+
parallel: ParallelEntries; // slot -> parallel entry (same entry may back multiple slots)
|
|
148
178
|
intercept: InterceptEntry[]; // intercept definitions for soft navigation
|
|
149
179
|
};
|
|
150
180
|
|
|
151
181
|
export type EntryData =
|
|
152
182
|
| ({
|
|
153
183
|
type: "route";
|
|
154
|
-
handler: Handler<any, any>;
|
|
184
|
+
handler: Handler<any, any, any>;
|
|
155
185
|
loading?: ReactNode | false;
|
|
186
|
+
transition?: TransitionConfig;
|
|
156
187
|
/** URL pattern for this route (used by path() in urls()) */
|
|
157
188
|
pattern?: string;
|
|
189
|
+
/** Set when handler is a Prerender definition */
|
|
190
|
+
isPrerender?: true;
|
|
191
|
+
/** Original PrerenderHandlerDefinition (for build-time getParams access) */
|
|
192
|
+
prerenderDef?: {
|
|
193
|
+
getParams?: (ctx: any) => Promise<any[]> | any[];
|
|
194
|
+
options?: { concurrency?: number };
|
|
195
|
+
};
|
|
196
|
+
/** Set when route is wrapped with Passthrough() — has a separate live handler */
|
|
197
|
+
isPassthrough?: true;
|
|
198
|
+
/** Live handler for runtime fallback (only set on Passthrough routes) */
|
|
199
|
+
liveHandler?: Handler<any, any, any>;
|
|
200
|
+
/** Set when handler is a Static definition (build-time only) */
|
|
201
|
+
isStaticPrerender?: true;
|
|
202
|
+
/** Static handler $$id for build-time store lookup */
|
|
203
|
+
staticHandlerId?: string;
|
|
204
|
+
/** Response type for non-RSC routes (json, text, image, any) */
|
|
205
|
+
responseType?: string;
|
|
158
206
|
} & EntryPropCommon &
|
|
159
207
|
EntryPropDatas &
|
|
160
208
|
EntryPropSegments)
|
|
161
209
|
| ({
|
|
162
210
|
type: "layout";
|
|
163
|
-
handler: ReactNode | Handler<any, any>;
|
|
164
|
-
loading?: ReactNode | false;
|
|
165
|
-
} & EntryPropCommon &
|
|
166
|
-
EntryPropDatas &
|
|
167
|
-
EntryPropSegments)
|
|
168
|
-
| ({
|
|
169
|
-
type: "parallel";
|
|
170
|
-
handler: Record<`@${string}`, Handler<any, any> | ReactNode>;
|
|
211
|
+
handler: ReactNode | Handler<any, any, any>;
|
|
171
212
|
loading?: ReactNode | false;
|
|
213
|
+
transition?: TransitionConfig;
|
|
214
|
+
/** Set when handler is a Static definition (build-time only) */
|
|
215
|
+
isStaticPrerender?: true;
|
|
216
|
+
/** Static handler $$id for build-time store lookup */
|
|
217
|
+
staticHandlerId?: string;
|
|
172
218
|
} & EntryPropCommon &
|
|
173
219
|
EntryPropDatas &
|
|
174
220
|
EntryPropSegments)
|
|
221
|
+
| ParallelEntryData
|
|
175
222
|
| ({
|
|
176
223
|
type: "cache";
|
|
177
224
|
/** Cache entries create cache boundaries and render like layouts (with Outlet) */
|
|
178
|
-
handler: ReactNode | Handler<any, any>;
|
|
225
|
+
handler: ReactNode | Handler<any, any, any>;
|
|
179
226
|
loading?: ReactNode | false;
|
|
227
|
+
transition?: TransitionConfig;
|
|
180
228
|
} & EntryPropCommon &
|
|
181
229
|
EntryPropDatas &
|
|
182
230
|
EntryPropSegments);
|
|
@@ -211,17 +259,37 @@ interface HelperContext {
|
|
|
211
259
|
patternsByPrefix?: Map<string, Map<string, string>>;
|
|
212
260
|
/** Trailing slash config per route name */
|
|
213
261
|
trailingSlash?: Map<string, "never" | "always" | "ignore">;
|
|
262
|
+
/** Search param schemas per route name */
|
|
263
|
+
searchSchemas?: Map<string, Record<string, string>>;
|
|
214
264
|
/** URL prefix from include() - applied to all path() patterns */
|
|
215
265
|
urlPrefix?: string;
|
|
216
266
|
/** Name prefix from include() - applied to all named routes */
|
|
217
267
|
namePrefix?: string;
|
|
268
|
+
/** True when this scope is at root level (no named include boundary above).
|
|
269
|
+
* Routes at root scope allow dot-local reverse to fall back to bare names. */
|
|
270
|
+
rootScoped?: boolean;
|
|
218
271
|
/** Run helper for cleaner middleware code */
|
|
219
272
|
run?: <T>(fn: () => T | Promise<T>) => T | Promise<T>;
|
|
220
273
|
/** Tracked includes for build-time manifest generation */
|
|
221
274
|
trackedIncludes?: TrackedInclude[];
|
|
275
|
+
/** Cache profiles for DSL-time cache("profileName") resolution */
|
|
276
|
+
cacheProfiles?: Record<
|
|
277
|
+
string,
|
|
278
|
+
import("../cache/profile-registry.js").CacheProfile
|
|
279
|
+
>;
|
|
280
|
+
/** True when resolving handlers inside a cache() DSL boundary.
|
|
281
|
+
* Read by ctx.get() to guard non-cacheable variable reads. */
|
|
282
|
+
insideCacheScope?: boolean;
|
|
222
283
|
}
|
|
223
|
-
|
|
224
|
-
|
|
284
|
+
// Use a global symbol key so the AsyncLocalStorage instance survives HMR
|
|
285
|
+
// module re-evaluation. Without this, Vite's RSC module runner may create
|
|
286
|
+
// a new instance when context.ts is re-evaluated, while other modules still
|
|
287
|
+
// hold references to the old instance — causing getStore() to return
|
|
288
|
+
// undefined even inside a run() callback.
|
|
289
|
+
const RSC_CONTEXT_KEY = Symbol.for("rangojs-router:rsc-context");
|
|
290
|
+
export const RSCRouterContext: AsyncLocalStorage<HelperContext> = ((
|
|
291
|
+
globalThis as any
|
|
292
|
+
)[RSC_CONTEXT_KEY] ??= new AsyncLocalStorage<HelperContext>());
|
|
225
293
|
|
|
226
294
|
export const getContext = (): {
|
|
227
295
|
context: AsyncLocalStorage<HelperContext>;
|
|
@@ -229,21 +297,21 @@ export const getContext = (): {
|
|
|
229
297
|
getParent: () => EntryData | null;
|
|
230
298
|
getOrCreateStore: (forRoute?: string) => HelperContext;
|
|
231
299
|
getNextIndex: (
|
|
232
|
-
type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate"
|
|
300
|
+
type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate",
|
|
233
301
|
) => string;
|
|
234
302
|
getShortCode: (
|
|
235
|
-
type: "layout" | "parallel" | "route" | "loader" | "cache"
|
|
303
|
+
type: "layout" | "parallel" | "route" | "loader" | "cache",
|
|
236
304
|
) => string;
|
|
237
305
|
run: <T>(
|
|
238
306
|
namespace: string,
|
|
239
307
|
parent: EntryData | null,
|
|
240
|
-
callback: (...args: any[]) => T
|
|
308
|
+
callback: (...args: any[]) => T,
|
|
241
309
|
) => T;
|
|
242
310
|
runWithStore: <T>(
|
|
243
311
|
store: HelperContext,
|
|
244
312
|
namespace: string,
|
|
245
313
|
parent: EntryData | null,
|
|
246
|
-
callback: (...args: any[]) => T
|
|
314
|
+
callback: (...args: any[]) => T,
|
|
247
315
|
) => T;
|
|
248
316
|
} => {
|
|
249
317
|
const context = RSCRouterContext;
|
|
@@ -262,6 +330,7 @@ export const getContext = (): {
|
|
|
262
330
|
patterns: new Map<string, string>(),
|
|
263
331
|
patternsByPrefix: new Map<string, Map<string, string>>(),
|
|
264
332
|
trailingSlash: new Map<string, "never" | "always" | "ignore">(),
|
|
333
|
+
searchSchemas: new Map<string, Record<string, string>>(),
|
|
265
334
|
} satisfies HelperContext;
|
|
266
335
|
}
|
|
267
336
|
return store;
|
|
@@ -270,7 +339,7 @@ export const getContext = (): {
|
|
|
270
339
|
const store = context.getStore();
|
|
271
340
|
if (!store) {
|
|
272
341
|
throw new Error(
|
|
273
|
-
"RSC Router context store is not available. Make sure to run within RSC Router context."
|
|
342
|
+
"RSC Router context store is not available. Make sure to run within RSC Router context.",
|
|
274
343
|
);
|
|
275
344
|
}
|
|
276
345
|
return store;
|
|
@@ -284,7 +353,7 @@ export const getContext = (): {
|
|
|
284
353
|
return store.parent;
|
|
285
354
|
},
|
|
286
355
|
getNextIndex: (
|
|
287
|
-
type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate"
|
|
356
|
+
type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate",
|
|
288
357
|
) => {
|
|
289
358
|
const store = context.getStore();
|
|
290
359
|
invariant(store, "No context RSCRouterContext available");
|
|
@@ -293,17 +362,31 @@ export const getContext = (): {
|
|
|
293
362
|
store.counters[type] = index + 1;
|
|
294
363
|
return `$${type}.${index}`;
|
|
295
364
|
},
|
|
296
|
-
getShortCode: (
|
|
365
|
+
getShortCode: (
|
|
366
|
+
type: "layout" | "parallel" | "route" | "loader" | "cache",
|
|
367
|
+
) => {
|
|
297
368
|
const store = context.getStore();
|
|
298
369
|
invariant(store, "No context RSCRouterContext available");
|
|
299
370
|
|
|
300
371
|
const parent = store.parent;
|
|
301
|
-
const prefix =
|
|
302
|
-
|
|
372
|
+
const prefix =
|
|
373
|
+
type === "layout"
|
|
374
|
+
? "L"
|
|
375
|
+
: type === "parallel"
|
|
376
|
+
? "P"
|
|
377
|
+
: type === "loader"
|
|
378
|
+
? "D"
|
|
379
|
+
: type === "cache"
|
|
380
|
+
? "C"
|
|
381
|
+
: "R";
|
|
382
|
+
const mountPrefix =
|
|
383
|
+
store.mountIndex !== undefined ? `M${store.mountIndex}` : "";
|
|
303
384
|
|
|
304
385
|
if (!parent) {
|
|
305
386
|
// Root entry: prefix with mount index and use mount-scoped counter
|
|
306
|
-
const counterKey = mountPrefix
|
|
387
|
+
const counterKey = mountPrefix
|
|
388
|
+
? `${mountPrefix}_root_${type}`
|
|
389
|
+
: `root_${type}`;
|
|
307
390
|
store.counters[counterKey] ??= 0;
|
|
308
391
|
const index = store.counters[counterKey];
|
|
309
392
|
store.counters[counterKey] = index + 1;
|
|
@@ -321,7 +404,7 @@ export const getContext = (): {
|
|
|
321
404
|
store: HelperContext,
|
|
322
405
|
namespace: string,
|
|
323
406
|
parent: EntryData | null,
|
|
324
|
-
callback: (...args: any[]) => T
|
|
407
|
+
callback: (...args: any[]) => T,
|
|
325
408
|
): T => {
|
|
326
409
|
return context.run(
|
|
327
410
|
{
|
|
@@ -335,24 +418,32 @@ export const getContext = (): {
|
|
|
335
418
|
isSSR: store.isSSR,
|
|
336
419
|
patterns: store.patterns,
|
|
337
420
|
trailingSlash: store.trailingSlash,
|
|
421
|
+
searchSchemas: store.searchSchemas,
|
|
338
422
|
urlPrefix: store.urlPrefix,
|
|
339
423
|
namePrefix: store.namePrefix,
|
|
424
|
+
rootScoped: store.rootScoped,
|
|
340
425
|
trackedIncludes: store.trackedIncludes,
|
|
426
|
+
cacheProfiles: store.cacheProfiles,
|
|
341
427
|
},
|
|
342
|
-
callback
|
|
428
|
+
callback,
|
|
343
429
|
);
|
|
344
430
|
},
|
|
345
431
|
run: <T>(
|
|
346
432
|
namespace: string,
|
|
347
433
|
parent: EntryData | null,
|
|
348
|
-
callback: (...args: any[]) => T
|
|
434
|
+
callback: (...args: any[]) => T,
|
|
349
435
|
) => {
|
|
350
436
|
const store = context.getStore();
|
|
351
437
|
// Preserve parent counters to ensure globally unique shortCodes
|
|
352
438
|
const counters = store?.counters || {};
|
|
353
439
|
const manifest = store ? store.manifest : new Map<string, EntryData>();
|
|
354
440
|
const patterns = store?.patterns || new Map<string, string>();
|
|
355
|
-
const
|
|
441
|
+
const patternsByPrefix = store?.patternsByPrefix;
|
|
442
|
+
const trailingSlash =
|
|
443
|
+
store?.trailingSlash ||
|
|
444
|
+
new Map<string, "never" | "always" | "ignore">();
|
|
445
|
+
const searchSchemas =
|
|
446
|
+
store?.searchSchemas || new Map<string, Record<string, string>>();
|
|
356
447
|
return context.run(
|
|
357
448
|
{
|
|
358
449
|
manifest,
|
|
@@ -364,12 +455,16 @@ export const getContext = (): {
|
|
|
364
455
|
metrics: store?.metrics,
|
|
365
456
|
isSSR: store?.isSSR,
|
|
366
457
|
patterns,
|
|
458
|
+
patternsByPrefix,
|
|
367
459
|
trailingSlash,
|
|
460
|
+
searchSchemas,
|
|
368
461
|
urlPrefix: store?.urlPrefix,
|
|
369
462
|
namePrefix: store?.namePrefix,
|
|
463
|
+
rootScoped: store?.rootScoped,
|
|
370
464
|
trackedIncludes: store?.trackedIncludes,
|
|
465
|
+
cacheProfiles: store?.cacheProfiles,
|
|
371
466
|
},
|
|
372
|
-
callback
|
|
467
|
+
callback,
|
|
373
468
|
);
|
|
374
469
|
},
|
|
375
470
|
};
|
|
@@ -382,30 +477,61 @@ export const getContext = (): {
|
|
|
382
477
|
export function runWithPrefixes<T>(
|
|
383
478
|
urlPrefix: string,
|
|
384
479
|
namePrefix: string | undefined,
|
|
385
|
-
callback: () => T
|
|
480
|
+
callback: () => T,
|
|
386
481
|
): T {
|
|
387
482
|
const store = RSCRouterContext.getStore();
|
|
388
483
|
if (!store) {
|
|
389
484
|
throw new Error("runWithPrefixes must be called within router context");
|
|
390
485
|
}
|
|
391
486
|
|
|
392
|
-
// Combine prefixes if there are existing ones
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
487
|
+
// Combine prefixes if there are existing ones, avoiding double slashes
|
|
488
|
+
let combinedUrlPrefix: string;
|
|
489
|
+
if (store.urlPrefix) {
|
|
490
|
+
if (store.urlPrefix.endsWith("/") && urlPrefix.startsWith("/")) {
|
|
491
|
+
combinedUrlPrefix = store.urlPrefix + urlPrefix.slice(1);
|
|
492
|
+
} else {
|
|
493
|
+
combinedUrlPrefix = store.urlPrefix + urlPrefix;
|
|
494
|
+
}
|
|
495
|
+
} else {
|
|
496
|
+
combinedUrlPrefix = urlPrefix;
|
|
497
|
+
}
|
|
498
|
+
const combinedNamePrefix =
|
|
499
|
+
namePrefix !== undefined
|
|
500
|
+
? namePrefix === ""
|
|
501
|
+
? store.namePrefix
|
|
502
|
+
: store.namePrefix
|
|
503
|
+
? `${store.namePrefix}.${namePrefix}`
|
|
504
|
+
: namePrefix
|
|
505
|
+
: store.namePrefix;
|
|
506
|
+
|
|
507
|
+
// Track root scope for dot-local reverse resolution.
|
|
508
|
+
//
|
|
509
|
+
// The flag answers: "can this route reach bare names at root scope?"
|
|
510
|
+
// It propagates through the include chain:
|
|
511
|
+
//
|
|
512
|
+
// { name: "" } — transparent: inherit parent, default true
|
|
513
|
+
// { name: "foo" } — inherit parent if already set, else create boundary (false)
|
|
514
|
+
// no name — inherit parent unchanged
|
|
515
|
+
//
|
|
516
|
+
// This means { name: "" } + nested { name: "sub" } keeps rootScoped=true
|
|
517
|
+
// (the outer transparent include establishes root access, and the inner
|
|
518
|
+
// named include inherits it). But a direct { name: "sub" } at root gets
|
|
519
|
+
// rootScoped=false (no prior root-access grant, so it creates a boundary).
|
|
520
|
+
const combinedRootScoped =
|
|
521
|
+
namePrefix === ""
|
|
522
|
+
? (store.rootScoped ?? true)
|
|
523
|
+
: namePrefix !== undefined
|
|
524
|
+
? (store.rootScoped ?? false)
|
|
525
|
+
: store.rootScoped;
|
|
401
526
|
|
|
402
527
|
return RSCRouterContext.run(
|
|
403
528
|
{
|
|
404
529
|
...store,
|
|
405
530
|
urlPrefix: combinedUrlPrefix,
|
|
406
531
|
namePrefix: combinedNamePrefix,
|
|
532
|
+
rootScoped: combinedRootScoped,
|
|
407
533
|
},
|
|
408
|
-
callback
|
|
534
|
+
callback,
|
|
409
535
|
);
|
|
410
536
|
}
|
|
411
537
|
|
|
@@ -425,9 +551,92 @@ export function getNamePrefix(): string | undefined {
|
|
|
425
551
|
return store?.namePrefix;
|
|
426
552
|
}
|
|
427
553
|
|
|
554
|
+
/**
|
|
555
|
+
* Get whether the current scope is at root level (no named include boundary above).
|
|
556
|
+
* Returns true at root or inside { name: "" } includes, false inside named includes.
|
|
557
|
+
*/
|
|
558
|
+
export function getRootScoped(): boolean {
|
|
559
|
+
const store = RSCRouterContext.getStore();
|
|
560
|
+
return store?.rootScoped ?? true;
|
|
561
|
+
}
|
|
562
|
+
|
|
428
563
|
// Export HelperContext type for use in other modules
|
|
429
564
|
export type { HelperContext };
|
|
430
565
|
|
|
566
|
+
/**
|
|
567
|
+
* Return an isolated copy of a lazy include's captured parent entry.
|
|
568
|
+
*
|
|
569
|
+
* DSL helpers (loader(), middleware(), etc.) mutate ctx.parent in place.
|
|
570
|
+
* Multiple include() scopes capture the *same* syntheticMapRoot as their
|
|
571
|
+
* parent, so without isolation one include's loaders/middleware leak into
|
|
572
|
+
* every other route that shares that root.
|
|
573
|
+
*
|
|
574
|
+
* The clone is shallow: only the mutable arrays are copied so each
|
|
575
|
+
* include pushes to its own list. The rest of the entry (id, shortCode,
|
|
576
|
+
* parent pointer, handler) stays shared, which is correct and cheap.
|
|
577
|
+
*/
|
|
578
|
+
export function getIsolatedLazyParent(
|
|
579
|
+
captured: EntryData | null | undefined,
|
|
580
|
+
): EntryData | null {
|
|
581
|
+
if (!captured) return null;
|
|
582
|
+
return {
|
|
583
|
+
...captured,
|
|
584
|
+
loader: [...captured.loader],
|
|
585
|
+
middleware: [...captured.middleware],
|
|
586
|
+
revalidate: [...captured.revalidate],
|
|
587
|
+
errorBoundary: [...captured.errorBoundary],
|
|
588
|
+
notFoundBoundary: [...captured.notFoundBoundary],
|
|
589
|
+
layout: [...captured.layout],
|
|
590
|
+
parallel: { ...captured.parallel },
|
|
591
|
+
intercept: [...captured.intercept],
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
export function getParallelEntries(
|
|
596
|
+
parallels: ParallelEntries | EntryData[] | undefined,
|
|
597
|
+
): ParallelEntryData[] {
|
|
598
|
+
if (!parallels) return [];
|
|
599
|
+
if (Array.isArray(parallels)) {
|
|
600
|
+
return parallels.filter(
|
|
601
|
+
(entry): entry is ParallelEntryData => entry.type === "parallel",
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
return Object.values(parallels).filter(
|
|
605
|
+
(entry): entry is ParallelEntryData => !!entry,
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
export function getParallelSlotEntries(
|
|
610
|
+
parallels: ParallelEntries | EntryData[] | undefined,
|
|
611
|
+
): Array<{ slot: `@${string}`; entry: ParallelEntryData }> {
|
|
612
|
+
if (!parallels) return [];
|
|
613
|
+
|
|
614
|
+
if (Array.isArray(parallels)) {
|
|
615
|
+
return getParallelEntries(parallels).flatMap((entry) =>
|
|
616
|
+
(Object.keys(entry.handler) as `@${string}`[]).map((slot) => ({
|
|
617
|
+
slot,
|
|
618
|
+
entry,
|
|
619
|
+
})),
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
return Object.entries(parallels)
|
|
624
|
+
.filter(([, entry]) => !!entry)
|
|
625
|
+
.map(([slot, entry]) => ({
|
|
626
|
+
slot: slot as `@${string}`,
|
|
627
|
+
entry: entry!,
|
|
628
|
+
}));
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
export function getParallelSlotCount(
|
|
632
|
+
parallels: ParallelEntries | EntryData[] | undefined,
|
|
633
|
+
): number {
|
|
634
|
+
if (!parallels) return 0;
|
|
635
|
+
return Array.isArray(parallels)
|
|
636
|
+
? parallels.filter((entry) => entry?.type === "parallel").length
|
|
637
|
+
: Object.keys(parallels).length;
|
|
638
|
+
}
|
|
639
|
+
|
|
431
640
|
// ============================================================================
|
|
432
641
|
// Performance Metrics Helpers
|
|
433
642
|
// ============================================================================
|
|
@@ -443,7 +652,7 @@ export type { HelperContext };
|
|
|
443
652
|
* done(); // Records duration
|
|
444
653
|
* ```
|
|
445
654
|
*/
|
|
446
|
-
export function track(label: string): () => void {
|
|
655
|
+
export function track(label: string, depth?: number): () => void {
|
|
447
656
|
const store = RSCRouterContext.getStore();
|
|
448
657
|
|
|
449
658
|
// No-op if context unavailable or metrics not enabled
|
|
@@ -454,7 +663,55 @@ export function track(label: string): () => void {
|
|
|
454
663
|
const startTime = performance.now() - store.metrics.requestStart;
|
|
455
664
|
|
|
456
665
|
return () => {
|
|
457
|
-
const duration =
|
|
458
|
-
|
|
666
|
+
const duration =
|
|
667
|
+
performance.now() - store.metrics!.requestStart - startTime;
|
|
668
|
+
store.metrics!.metrics.push({
|
|
669
|
+
label,
|
|
670
|
+
duration,
|
|
671
|
+
startTime,
|
|
672
|
+
...(depth != null ? { depth } : {}),
|
|
673
|
+
});
|
|
459
674
|
};
|
|
460
675
|
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Separate ALS for tracking loader execution scope.
|
|
679
|
+
* Uses a dedicated ALS (not RSCRouterContext) to avoid issues with
|
|
680
|
+
* nested RSCRouterContext.run() calls in Vite's module runner.
|
|
681
|
+
*/
|
|
682
|
+
const LOADER_SCOPE_KEY = Symbol.for("rangojs-router:loader-scope");
|
|
683
|
+
const loaderScopeALS: AsyncLocalStorage<{ active: true }> = ((
|
|
684
|
+
globalThis as any
|
|
685
|
+
)[LOADER_SCOPE_KEY] ??= new AsyncLocalStorage<{ active: true }>());
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Check if the current execution is inside a cache() DSL boundary.
|
|
689
|
+
* Returns false inside loader execution — loaders are always fresh
|
|
690
|
+
* (never cached), so non-cacheable reads are safe.
|
|
691
|
+
*/
|
|
692
|
+
export function isInsideCacheScope(): boolean {
|
|
693
|
+
if (RSCRouterContext.getStore()?.insideCacheScope !== true) return false;
|
|
694
|
+
// Loaders are always fresh — even inside a cache() boundary, the loader
|
|
695
|
+
// function re-executes on every request. Skip the guard when running
|
|
696
|
+
// inside a loader.
|
|
697
|
+
if (loaderScopeALS.getStore()?.active) return false;
|
|
698
|
+
return true;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Check if the current execution is inside a DSL loader scope
|
|
703
|
+
* (wrapped by runInsideLoaderScope). Used by rendered() barrier
|
|
704
|
+
* to distinguish DSL loaders from handler-invoked loaders.
|
|
705
|
+
*/
|
|
706
|
+
export function isInsideLoaderScope(): boolean {
|
|
707
|
+
return loaderScopeALS.getStore()?.active === true;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Run `fn` inside a loader scope. While active, cache-scope guards
|
|
712
|
+
* are bypassed because loaders are always fresh (never cached) and
|
|
713
|
+
* their side effects (setCookie, header, etc.) are safe.
|
|
714
|
+
*/
|
|
715
|
+
export function runInsideLoaderScope<T>(fn: () => T): T {
|
|
716
|
+
return loaderScopeALS.run({ active: true }, fn);
|
|
717
|
+
}
|