@rangojs/router 0.0.0-experimental.b9cb8739 → 0.0.0-experimental.bd6e11bc
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/README.md +196 -43
- package/dist/bin/rango.js +277 -99
- package/dist/testing/vitest.js +48 -0
- package/dist/vite/index.js +2779 -1064
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +57 -11
- package/skills/breadcrumbs/SKILL.md +3 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +243 -21
- package/skills/caching/SKILL.md +155 -6
- package/skills/composability/SKILL.md +27 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +229 -20
- package/skills/host-router/SKILL.md +45 -20
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +46 -4
- package/skills/layout/SKILL.md +28 -7
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +273 -53
- package/skills/middleware/SKILL.md +49 -12
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +197 -6
- package/skills/prerender/SKILL.md +123 -100
- package/skills/rango/SKILL.md +242 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +66 -9
- package/skills/route/SKILL.md +88 -4
- package/skills/router-setup/SKILL.md +90 -5
- package/skills/server-actions/SKILL.md +751 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/testing/SKILL.md +716 -0
- package/skills/typesafety/SKILL.md +329 -27
- package/skills/use-cache/SKILL.md +34 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +117 -0
- package/src/__internal.ts +1 -1
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +91 -70
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/navigation-bridge.ts +102 -16
- package/src/browser/navigation-client.ts +164 -59
- package/src/browser/navigation-store.ts +75 -17
- package/src/browser/navigation-transaction.ts +21 -37
- package/src/browser/partial-update.ts +139 -38
- package/src/browser/prefetch/cache.ts +175 -15
- package/src/browser/prefetch/fetch.ts +180 -33
- package/src/browser/prefetch/queue.ts +123 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +53 -13
- package/src/browser/react/Link.tsx +81 -9
- package/src/browser/react/NavigationProvider.tsx +110 -33
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +51 -7
- package/src/browser/react/index.ts +3 -0
- package/src/browser/react/location-state-shared.ts +175 -4
- package/src/browser/react/location-state.ts +39 -13
- package/src/browser/react/use-handle.ts +23 -64
- package/src/browser/react/use-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +20 -8
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +43 -10
- package/src/browser/react/use-segments.ts +11 -8
- package/src/browser/response-adapter.ts +25 -0
- package/src/browser/rsc-router.tsx +191 -74
- package/src/browser/scroll-restoration.ts +41 -14
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +31 -36
- package/src/browser/types.ts +57 -5
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +65 -40
- package/src/build/generate-route-types.ts +5 -0
- package/src/build/index.ts +2 -0
- package/src/build/route-trie.ts +52 -25
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +9 -2
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +278 -88
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-runtime.ts +15 -11
- package/src/cache/cache-scope.ts +76 -49
- package/src/cache/cf/cf-cache-store.ts +501 -18
- package/src/cache/cf/index.ts +5 -1
- package/src/cache/document-cache.ts +17 -7
- package/src/cache/index.ts +1 -0
- package/src/cache/taint.ts +55 -0
- package/src/client.rsc.tsx +3 -0
- package/src/client.tsx +94 -238
- package/src/context-var.ts +72 -2
- package/src/debug.ts +2 -2
- package/src/decode-loader-results.ts +36 -0
- package/src/errors.ts +30 -1
- package/src/handle.ts +65 -12
- package/src/host/index.ts +2 -2
- package/src/host/router.ts +129 -57
- package/src/host/types.ts +31 -2
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +140 -20
- package/src/index.rsc.ts +12 -5
- package/src/index.ts +61 -11
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +2 -5
- package/src/loader.ts +3 -10
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/prerender/store.ts +5 -4
- package/src/prerender.ts +141 -80
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -15
- package/src/route-content-wrapper.tsx +6 -28
- package/src/route-definition/dsl-helpers.ts +435 -260
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +110 -34
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +11 -3
- package/src/route-definition/resolve-handler-use.ts +155 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +7 -1
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +113 -1
- package/src/router/error-handling.ts +1 -1
- package/src/router/find-match.ts +4 -2
- package/src/router/handler-context.ts +77 -38
- package/src/router/intercept-resolution.ts +15 -22
- package/src/router/lazy-includes.ts +12 -9
- package/src/router/loader-resolution.ts +174 -22
- package/src/router/logging.ts +5 -2
- package/src/router/manifest.ts +31 -16
- package/src/router/match-api.ts +128 -192
- package/src/router/match-handlers.ts +63 -20
- package/src/router/match-middleware/background-revalidation.ts +30 -2
- package/src/router/match-middleware/cache-lookup.ts +136 -106
- package/src/router/match-middleware/cache-store.ts +54 -10
- package/src/router/match-middleware/intercept-resolution.ts +9 -7
- package/src/router/match-middleware/segment-resolution.ts +61 -5
- package/src/router/match-result.ts +125 -10
- package/src/router/metrics.ts +7 -2
- package/src/router/middleware-types.ts +21 -34
- package/src/router/middleware.ts +103 -90
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +101 -17
- package/src/router/prerender-match.ts +110 -10
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +286 -0
- package/src/router/revalidation.ts +58 -2
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +6 -1
- package/src/router/router-interfaces.ts +77 -28
- package/src/router/router-options.ts +76 -11
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +223 -24
- package/src/router/segment-resolution/helpers.ts +29 -24
- package/src/router/segment-resolution/loader-cache.ts +1 -0
- package/src/router/segment-resolution/revalidation.ts +466 -285
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/segment-wrappers.ts +2 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry.ts +99 -0
- package/src/router/trie-matching.ts +18 -13
- package/src/router/types.ts +9 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +91 -23
- package/src/rsc/handler-context.ts +2 -2
- package/src/rsc/handler.ts +440 -381
- package/src/rsc/helpers.ts +91 -43
- package/src/rsc/index.ts +1 -1
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/origin-guard.ts +28 -10
- package/src/rsc/progressive-enhancement.ts +18 -2
- package/src/rsc/response-route-handler.ts +46 -53
- package/src/rsc/rsc-rendering.ts +41 -48
- package/src/rsc/runtime-warnings.ts +9 -10
- package/src/rsc/server-action.ts +25 -37
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +17 -3
- package/src/search-params.ts +4 -4
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +219 -67
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +277 -61
- package/src/server/cookie-store.ts +28 -4
- package/src/server/handle-store.ts +19 -0
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +204 -60
- package/src/ssr/index.tsx +9 -1
- package/src/static-handler.ts +19 -7
- package/src/testing/cache-status.ts +166 -0
- package/src/testing/collect-handle.ts +63 -0
- package/src/testing/dispatch.ts +440 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +154 -0
- package/src/testing/e2e/index.ts +149 -0
- package/src/testing/e2e/matchers.ts +51 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +306 -0
- package/src/testing/e2e/server.ts +183 -0
- package/src/testing/flight-matchers.ts +104 -0
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/flight.entry.ts +22 -0
- package/src/testing/flight.ts +182 -0
- package/src/testing/generated-routes.ts +223 -0
- package/src/testing/index.ts +106 -0
- package/src/testing/internal/context.ts +255 -0
- package/src/testing/render-route.tsx +565 -0
- package/src/testing/run-loader.ts +296 -0
- package/src/testing/run-middleware.ts +179 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +183 -0
- package/src/types/cache-types.ts +4 -4
- package/src/types/global-namespace.ts +39 -26
- package/src/types/handler-context.ts +194 -72
- package/src/types/index.ts +1 -0
- package/src/types/loader-types.ts +41 -15
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-entry.ts +19 -1
- package/src/types/segments.ts +37 -1
- package/src/urls/include-helper.ts +34 -67
- package/src/urls/index.ts +0 -3
- package/src/urls/path-helper-types.ts +50 -9
- package/src/urls/path-helper.ts +63 -63
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +26 -116
- package/src/urls/urls-function.ts +1 -5
- package/src/use-loader.tsx +487 -44
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +34 -37
- package/src/vite/discovery/discover-routers.ts +105 -51
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +188 -93
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +46 -6
- package/src/vite/discovery/virtual-module-codegen.ts +13 -23
- package/src/vite/index.ts +6 -0
- package/src/vite/plugin-types.ts +111 -72
- package/src/vite/plugins/cjs-to-esm.ts +8 -7
- package/src/vite/plugins/client-ref-dedup.ts +16 -0
- package/src/vite/plugins/client-ref-hashing.ts +28 -5
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/plugins/expose-action-id.ts +55 -33
- package/src/vite/plugins/expose-id-utils.ts +24 -8
- package/src/vite/plugins/expose-ids/export-analysis.ts +100 -20
- package/src/vite/plugins/expose-ids/handler-transform.ts +12 -35
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -5
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
- package/src/vite/plugins/expose-internal-ids.ts +544 -317
- package/src/vite/plugins/performance-tracks.ts +92 -0
- package/src/vite/plugins/refresh-cmd.ts +88 -26
- package/src/vite/plugins/use-cache-transform.ts +65 -50
- package/src/vite/plugins/version-injector.ts +39 -23
- package/src/vite/plugins/version-plugin.ts +72 -3
- package/src/vite/plugins/virtual-entries.ts +2 -2
- package/src/vite/rango.ts +265 -226
- package/src/vite/router-discovery.ts +920 -137
- package/src/vite/utils/ast-handler-extract.ts +15 -15
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/bundle-analysis.ts +4 -2
- package/src/vite/utils/client-chunks.ts +190 -0
- package/src/vite/utils/forward-user-plugins.ts +193 -0
- package/src/vite/utils/manifest-utils.ts +21 -5
- package/src/vite/utils/package-resolution.ts +41 -1
- package/src/vite/utils/prerender-utils.ts +38 -5
- package/src/vite/utils/shared-utils.ts +109 -27
- package/src/browser/action-response-classifier.ts +0 -99
|
@@ -20,17 +20,33 @@ import type {
|
|
|
20
20
|
DefaultRouteName,
|
|
21
21
|
} from "../types/global-namespace.js";
|
|
22
22
|
import type { Handle } from "../handle.js";
|
|
23
|
-
import {
|
|
24
|
-
|
|
23
|
+
import {
|
|
24
|
+
type ContextVar,
|
|
25
|
+
contextGet,
|
|
26
|
+
contextSet,
|
|
27
|
+
isNonCacheable,
|
|
28
|
+
} from "../context-var.js";
|
|
29
|
+
import {
|
|
30
|
+
createHandleStore,
|
|
31
|
+
buildHandleSnapshot,
|
|
32
|
+
type HandleStore,
|
|
33
|
+
type HandleData,
|
|
34
|
+
} from "./handle-store.js";
|
|
25
35
|
import { isHandle } from "../handle.js";
|
|
26
36
|
import { track, type MetricsStore } from "./context.js";
|
|
27
37
|
import { getFetchableLoader } from "./fetchable-loader-store.js";
|
|
28
38
|
import type { SegmentCacheStore } from "../cache/types.js";
|
|
29
39
|
import type { Theme, ResolvedThemeConfig } from "../theme/types.js";
|
|
40
|
+
import type { ExecutionContext, RequestScope } from "../types/request-scope.js";
|
|
41
|
+
import { fireAndForgetWaitUntil } from "../types/request-scope.js";
|
|
30
42
|
import { THEME_COOKIE } from "../theme/constants.js";
|
|
31
43
|
import type { LocationStateEntry } from "../browser/react/location-state-shared.js";
|
|
32
44
|
import { NOCACHE_SYMBOL, assertNotInsideCacheExec } from "../cache/taint.js";
|
|
33
|
-
import {
|
|
45
|
+
import { isInsideCacheScope } from "./context.js";
|
|
46
|
+
import {
|
|
47
|
+
createReverseFunction,
|
|
48
|
+
stripInternalParams,
|
|
49
|
+
} from "../router/handler-context.js";
|
|
34
50
|
import { getGlobalRouteMap, isRouteRootScoped } from "../route-map-builder.js";
|
|
35
51
|
import { invariant } from "../errors.js";
|
|
36
52
|
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
@@ -44,24 +60,9 @@ import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
|
44
60
|
export interface RequestContext<
|
|
45
61
|
TEnv = DefaultEnv,
|
|
46
62
|
TParams = Record<string, string>,
|
|
47
|
-
> {
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
/** Original HTTP request */
|
|
51
|
-
request: Request;
|
|
52
|
-
/** Parsed URL (with internal `_rsc*` params stripped) */
|
|
53
|
-
url: URL;
|
|
54
|
-
/**
|
|
55
|
-
* The original request URL with all parameters intact, including
|
|
56
|
-
* internal `_rsc*` transport params.
|
|
57
|
-
*/
|
|
58
|
-
originalUrl: URL;
|
|
59
|
-
/** URL pathname */
|
|
60
|
-
pathname: string;
|
|
61
|
-
/** URL search params (system params like _rsc* are NOT filtered here) */
|
|
62
|
-
searchParams: URLSearchParams;
|
|
63
|
-
/** Variables set by middleware (same as ctx.var) */
|
|
64
|
-
var: Record<string, any>;
|
|
63
|
+
> extends RequestScope<TEnv> {
|
|
64
|
+
/** @internal Shared variable backing store for ctx.get()/ctx.set(). */
|
|
65
|
+
_variables: Record<string, any>;
|
|
65
66
|
/** Get a variable set by middleware */
|
|
66
67
|
get: {
|
|
67
68
|
<T>(contextVar: ContextVar<T>): T | undefined;
|
|
@@ -69,8 +70,12 @@ export interface RequestContext<
|
|
|
69
70
|
};
|
|
70
71
|
/** Set a variable (shared with middleware and handlers) */
|
|
71
72
|
set: {
|
|
72
|
-
<T>(
|
|
73
|
-
|
|
73
|
+
<T>(
|
|
74
|
+
contextVar: ContextVar<T>,
|
|
75
|
+
value: T,
|
|
76
|
+
options?: { cache?: boolean },
|
|
77
|
+
): void;
|
|
78
|
+
<K extends string>(key: K, value: any, options?: { cache?: boolean }): void;
|
|
74
79
|
};
|
|
75
80
|
/**
|
|
76
81
|
* Route params (populated after route matching)
|
|
@@ -141,20 +146,6 @@ export interface RequestContext<
|
|
|
141
146
|
import("../cache/profile-registry.js").CacheProfile
|
|
142
147
|
>;
|
|
143
148
|
|
|
144
|
-
/**
|
|
145
|
-
* Schedule work to run after the response is sent.
|
|
146
|
-
* On Cloudflare Workers, uses ctx.waitUntil().
|
|
147
|
-
* On Node.js, runs as fire-and-forget.
|
|
148
|
-
*
|
|
149
|
-
* @example
|
|
150
|
-
* ```typescript
|
|
151
|
-
* ctx.waitUntil(async () => {
|
|
152
|
-
* await cacheStore.set(key, data, ttl);
|
|
153
|
-
* });
|
|
154
|
-
* ```
|
|
155
|
-
*/
|
|
156
|
-
waitUntil(fn: () => Promise<void>): void;
|
|
157
|
-
|
|
158
149
|
/**
|
|
159
150
|
* Register a callback to run when the response is created.
|
|
160
151
|
* Callbacks are sync and receive the response. They can:
|
|
@@ -258,6 +249,54 @@ export interface RequestContext<
|
|
|
258
249
|
/** @internal Previous route key (from the navigation source), used for revalidation */
|
|
259
250
|
_prevRouteKey?: string;
|
|
260
251
|
|
|
252
|
+
/**
|
|
253
|
+
* @internal Render barrier for experimental `rendered()` API.
|
|
254
|
+
* Resolves when all non-loader segments have settled and handle data
|
|
255
|
+
* is available. Used by DSL loaders that call `ctx.rendered()`.
|
|
256
|
+
*/
|
|
257
|
+
_renderBarrier: Promise<void>;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @internal Resolve the render barrier. Accepts resolved segments, filters
|
|
261
|
+
* out loaders, and captures non-loader segment IDs as the handle ordering.
|
|
262
|
+
* Called after segment resolution (fresh) or handle replay (cache/prerender).
|
|
263
|
+
*/
|
|
264
|
+
_resolveRenderBarrier: (
|
|
265
|
+
segments: Array<{ type: string; id: string }>,
|
|
266
|
+
) => void;
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* @internal Segment order at barrier resolution time, used by loader
|
|
270
|
+
* ctx.use(handle) to collect handle data in correct order.
|
|
271
|
+
*/
|
|
272
|
+
_renderBarrierSegmentOrder?: string[];
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* @internal Set to true when the matched entry tree contains any `loading()`
|
|
276
|
+
* entries (streaming). Used by rendered() to fail fast.
|
|
277
|
+
*/
|
|
278
|
+
_treeHasStreaming?: boolean;
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* @internal Loader IDs that have called rendered() and are waiting for the
|
|
282
|
+
* barrier. Used to detect deadlocks when a handler tries to await the same
|
|
283
|
+
* loader via ctx.use(Loader).
|
|
284
|
+
*/
|
|
285
|
+
_renderBarrierWaiters?: Set<string>;
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* @internal Loader IDs that handlers have started awaiting via ctx.use().
|
|
289
|
+
* Used for bidirectional deadlock detection: if a loader later calls
|
|
290
|
+
* rendered() and a handler already awaits it, we can detect the deadlock.
|
|
291
|
+
*/
|
|
292
|
+
_handlerLoaderDeps?: Set<string>;
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* @internal Cached HandleData snapshot built at barrier resolution time.
|
|
296
|
+
* Avoids rebuilding the snapshot on every loader ctx.use(handle) call.
|
|
297
|
+
*/
|
|
298
|
+
_renderBarrierHandleSnapshot?: HandleData;
|
|
299
|
+
|
|
261
300
|
/** @internal Per-request error dedup set for onError reporting */
|
|
262
301
|
_reportedErrors: WeakSet<object>;
|
|
263
302
|
|
|
@@ -274,6 +313,24 @@ export interface RequestContext<
|
|
|
274
313
|
|
|
275
314
|
/** @internal Request-scoped performance metrics store */
|
|
276
315
|
_metricsStore?: MetricsStore;
|
|
316
|
+
|
|
317
|
+
/** @internal Router basename for this request (used by redirect()) */
|
|
318
|
+
_basename?: string;
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* @internal RouteSnapshot from classifyRequest, reused by match/matchPartial
|
|
322
|
+
* to avoid a second resolveRoute call. Cleared on HMR invalidation.
|
|
323
|
+
*/
|
|
324
|
+
_classifiedRoute?: import("../router/route-snapshot.js").RouteSnapshot;
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* @internal Coarse route-level cache signal for the X-Rango-Cache debug
|
|
328
|
+
* header. Populated by match/matchPartial only when the debug cache signal
|
|
329
|
+
* gate is enabled (debugCacheSignal option or RANGO_TEST_SIGNALS=1). Read by
|
|
330
|
+
* the response-finalization path (createResponseWithMergedHeaders). Undefined
|
|
331
|
+
* when the gate is off, so no header is emitted.
|
|
332
|
+
*/
|
|
333
|
+
_cacheSignal?: import("../router/telemetry.js").CacheSegmentSignal[];
|
|
277
334
|
}
|
|
278
335
|
|
|
279
336
|
/**
|
|
@@ -300,10 +357,21 @@ export type PublicRequestContext<
|
|
|
300
357
|
| "_routeName"
|
|
301
358
|
| "_prevRouteKey"
|
|
302
359
|
| "_reportedErrors"
|
|
360
|
+
| "_renderBarrier"
|
|
361
|
+
| "_resolveRenderBarrier"
|
|
362
|
+
| "_renderBarrierSegmentOrder"
|
|
363
|
+
| "_treeHasStreaming"
|
|
364
|
+
| "_renderBarrierWaiters"
|
|
365
|
+
| "_handlerLoaderDeps"
|
|
366
|
+
| "_renderBarrierHandleSnapshot"
|
|
303
367
|
| "_reportBackgroundError"
|
|
304
368
|
| "_debugPerformance"
|
|
305
369
|
| "_metricsStore"
|
|
370
|
+
| "_basename"
|
|
306
371
|
| "_setStatus"
|
|
372
|
+
| "_variables"
|
|
373
|
+
| "_classifiedRoute"
|
|
374
|
+
| "_cacheSignal"
|
|
307
375
|
| "res"
|
|
308
376
|
>;
|
|
309
377
|
|
|
@@ -413,13 +481,7 @@ export function requireRequestContext<
|
|
|
413
481
|
return getRequestContext<TEnv>();
|
|
414
482
|
}
|
|
415
483
|
|
|
416
|
-
|
|
417
|
-
* Cloudflare Workers ExecutionContext (subset we need)
|
|
418
|
-
*/
|
|
419
|
-
export interface ExecutionContext {
|
|
420
|
-
waitUntil(promise: Promise<any>): void;
|
|
421
|
-
passThroughOnException(): void;
|
|
422
|
-
}
|
|
484
|
+
export type { ExecutionContext };
|
|
423
485
|
|
|
424
486
|
/**
|
|
425
487
|
* Options for creating a request context
|
|
@@ -503,6 +565,18 @@ export function createRequestContext<TEnv>(
|
|
|
503
565
|
responseCookieCache = null;
|
|
504
566
|
};
|
|
505
567
|
|
|
568
|
+
// Guard: throw if a response-level side effect is called inside a cache() scope.
|
|
569
|
+
// Uses ALS to detect the scope (set during segment resolution).
|
|
570
|
+
function assertNotInsideCacheScopeALS(methodName: string): void {
|
|
571
|
+
if (isInsideCacheScope()) {
|
|
572
|
+
throw new Error(
|
|
573
|
+
`ctx.${methodName}() cannot be called inside a cache() boundary. ` +
|
|
574
|
+
`On cache hit the handler is skipped, so this side effect would be lost. ` +
|
|
575
|
+
`Move ctx.${methodName}() to a middleware or layout outside the cache() scope.`,
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
506
580
|
// Effective cookie read: response stub Set-Cookie wins, then original header.
|
|
507
581
|
// The stub IS the source of truth for same-request mutations.
|
|
508
582
|
const effectiveCookie = (name: string): string | undefined => {
|
|
@@ -555,20 +629,31 @@ export function createRequestContext<TEnv>(
|
|
|
555
629
|
invalidateResponseCookieCache();
|
|
556
630
|
};
|
|
557
631
|
|
|
632
|
+
// Strip internal _rsc* params so userland sees a clean URL.
|
|
633
|
+
const cleanUrl = stripInternalParams(url);
|
|
634
|
+
|
|
558
635
|
// Build the context object first (without use), then add use
|
|
559
636
|
const ctx: RequestContext<TEnv> = {
|
|
560
637
|
env,
|
|
561
638
|
request,
|
|
562
|
-
url,
|
|
639
|
+
url: cleanUrl,
|
|
563
640
|
originalUrl: new URL(request.url),
|
|
564
641
|
pathname: url.pathname,
|
|
565
|
-
searchParams:
|
|
566
|
-
|
|
567
|
-
get: ((keyOrVar: any) =>
|
|
568
|
-
|
|
569
|
-
|
|
642
|
+
searchParams: cleanUrl.searchParams,
|
|
643
|
+
_variables: variables,
|
|
644
|
+
get: ((keyOrVar: any) => {
|
|
645
|
+
if (isNonCacheable(variables, keyOrVar) && isInsideCacheScope()) {
|
|
646
|
+
throw new Error(
|
|
647
|
+
`ctx.get() for a non-cacheable variable cannot be called inside a cache() boundary. ` +
|
|
648
|
+
`The variable was created with { cache: false } or set with { cache: false }, ` +
|
|
649
|
+
`and its value would be stale on cache hit. Move the read outside the cached scope.`,
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
return contextGet(variables, keyOrVar);
|
|
653
|
+
}) as RequestContext<TEnv>["get"],
|
|
654
|
+
set: ((keyOrVar: any, value: any, options?: any) => {
|
|
570
655
|
assertNotInsideCacheExec(ctx, "set");
|
|
571
|
-
contextSet(variables, keyOrVar, value);
|
|
656
|
+
contextSet(variables, keyOrVar, value, options);
|
|
572
657
|
}) as RequestContext<TEnv>["set"],
|
|
573
658
|
params: {} as Record<string, string>,
|
|
574
659
|
|
|
@@ -606,6 +691,7 @@ export function createRequestContext<TEnv>(
|
|
|
606
691
|
|
|
607
692
|
setCookie(name: string, value: string, options?: CookieOptions): void {
|
|
608
693
|
assertNotInsideCacheExec(ctx, "setCookie");
|
|
694
|
+
assertNotInsideCacheScopeALS("setCookie");
|
|
609
695
|
stubResponse.headers.append(
|
|
610
696
|
"Set-Cookie",
|
|
611
697
|
serializeCookieValue(name, value, options),
|
|
@@ -618,6 +704,7 @@ export function createRequestContext<TEnv>(
|
|
|
618
704
|
options?: Pick<CookieOptions, "domain" | "path">,
|
|
619
705
|
): void {
|
|
620
706
|
assertNotInsideCacheExec(ctx, "deleteCookie");
|
|
707
|
+
assertNotInsideCacheScopeALS("deleteCookie");
|
|
621
708
|
stubResponse.headers.append(
|
|
622
709
|
"Set-Cookie",
|
|
623
710
|
serializeCookieValue(name, "", { ...options, maxAge: 0 }),
|
|
@@ -627,11 +714,13 @@ export function createRequestContext<TEnv>(
|
|
|
627
714
|
|
|
628
715
|
header(name: string, value: string): void {
|
|
629
716
|
assertNotInsideCacheExec(ctx, "header");
|
|
717
|
+
assertNotInsideCacheScopeALS("header");
|
|
630
718
|
stubResponse.headers.set(name, value);
|
|
631
719
|
},
|
|
632
720
|
|
|
633
721
|
setStatus(status: number): void {
|
|
634
722
|
assertNotInsideCacheExec(ctx, "setStatus");
|
|
723
|
+
assertNotInsideCacheScopeALS("setStatus");
|
|
635
724
|
stubResponse = new Response(null, {
|
|
636
725
|
status,
|
|
637
726
|
headers: stubResponse.headers,
|
|
@@ -656,20 +745,19 @@ export function createRequestContext<TEnv>(
|
|
|
656
745
|
|
|
657
746
|
waitUntil(fn: () => Promise<void>): void {
|
|
658
747
|
if (executionContext?.waitUntil) {
|
|
659
|
-
// Cloudflare Workers: use native waitUntil
|
|
660
748
|
executionContext.waitUntil(fn());
|
|
661
749
|
} else {
|
|
662
|
-
|
|
663
|
-
fn().catch((err) =>
|
|
664
|
-
console.error("[waitUntil] Background task failed:", err),
|
|
665
|
-
);
|
|
750
|
+
fireAndForgetWaitUntil(fn);
|
|
666
751
|
}
|
|
667
752
|
},
|
|
668
753
|
|
|
754
|
+
executionContext,
|
|
755
|
+
|
|
669
756
|
_onResponseCallbacks: [],
|
|
670
757
|
|
|
671
758
|
onResponse(callback: (response: Response) => Response): void {
|
|
672
759
|
assertNotInsideCacheExec(ctx, "onResponse");
|
|
760
|
+
assertNotInsideCacheScopeALS("onResponse");
|
|
673
761
|
this._onResponseCallbacks.push(callback);
|
|
674
762
|
},
|
|
675
763
|
|
|
@@ -697,9 +785,58 @@ export function createRequestContext<TEnv>(
|
|
|
697
785
|
_reportedErrors: new WeakSet<object>(),
|
|
698
786
|
_metricsStore: undefined,
|
|
699
787
|
|
|
788
|
+
// Render barrier: deferred promise resolved after non-loader segments settle.
|
|
789
|
+
_renderBarrier: null as any, // set below
|
|
790
|
+
_resolveRenderBarrier: null as any, // set below
|
|
791
|
+
_renderBarrierSegmentOrder: undefined,
|
|
792
|
+
|
|
700
793
|
reverse: createReverseFunction(getGlobalRouteMap(), undefined, {}),
|
|
701
794
|
};
|
|
702
795
|
|
|
796
|
+
// Lazy render barrier: only allocate the Promise when a loader actually
|
|
797
|
+
// calls rendered(). Requests that don't use rendered() pay zero cost.
|
|
798
|
+
let barrierResolved = false;
|
|
799
|
+
let resolveBarrier: (() => void) | undefined;
|
|
800
|
+
ctx._renderBarrier = null as any; // lazy — created on first access
|
|
801
|
+
ctx._resolveRenderBarrier = (
|
|
802
|
+
segments: Array<{ type: string; id: string }>,
|
|
803
|
+
) => {
|
|
804
|
+
if (barrierResolved) return;
|
|
805
|
+
barrierResolved = true;
|
|
806
|
+
const segOrder = segments
|
|
807
|
+
.filter((s) => s.type !== "loader")
|
|
808
|
+
.map((s) => s.id);
|
|
809
|
+
ctx._renderBarrierSegmentOrder = segOrder;
|
|
810
|
+
// Build and cache handle snapshot so loader ctx.use(handle) calls
|
|
811
|
+
// don't rebuild it on every invocation.
|
|
812
|
+
ctx._renderBarrierHandleSnapshot = buildHandleSnapshot(
|
|
813
|
+
handleStore,
|
|
814
|
+
segOrder,
|
|
815
|
+
);
|
|
816
|
+
ctx._renderBarrierWaiters = undefined;
|
|
817
|
+
ctx._handlerLoaderDeps = undefined;
|
|
818
|
+
if (resolveBarrier) resolveBarrier();
|
|
819
|
+
};
|
|
820
|
+
Object.defineProperty(ctx, "_renderBarrier", {
|
|
821
|
+
get() {
|
|
822
|
+
// Barrier already resolved (cache/prerender hit) or first lazy access.
|
|
823
|
+
// Either way, replace the getter with a concrete value to avoid
|
|
824
|
+
// repeated Promise.resolve() allocations on subsequent reads.
|
|
825
|
+
const p = barrierResolved
|
|
826
|
+
? Promise.resolve()
|
|
827
|
+
: new Promise<void>((resolve) => {
|
|
828
|
+
resolveBarrier = resolve;
|
|
829
|
+
});
|
|
830
|
+
Object.defineProperty(ctx, "_renderBarrier", {
|
|
831
|
+
value: p,
|
|
832
|
+
writable: false,
|
|
833
|
+
configurable: false,
|
|
834
|
+
});
|
|
835
|
+
return p;
|
|
836
|
+
},
|
|
837
|
+
configurable: true,
|
|
838
|
+
});
|
|
839
|
+
|
|
703
840
|
// Now create use() with access to ctx
|
|
704
841
|
ctx.use = createUseFunction({
|
|
705
842
|
handleStore,
|
|
@@ -881,15 +1018,17 @@ export function createUseFunction<TEnv>(
|
|
|
881
1018
|
search: (ctx as any).search ?? {},
|
|
882
1019
|
pathname: ctx.pathname,
|
|
883
1020
|
url: ctx.url,
|
|
1021
|
+
originalUrl: ctx.originalUrl,
|
|
884
1022
|
env: ctx.env as any,
|
|
885
|
-
|
|
1023
|
+
waitUntil: ctx.waitUntil.bind(ctx),
|
|
1024
|
+
executionContext: ctx.executionContext,
|
|
886
1025
|
get: ctx.get as any,
|
|
887
|
-
use: <TDep, TDepParams = any>(
|
|
1026
|
+
use: (<TDep, TDepParams = any>(
|
|
888
1027
|
dep: LoaderDefinition<TDep, TDepParams>,
|
|
889
1028
|
): Promise<TDep> => {
|
|
890
1029
|
// Recursive call - will start dep loader if not already started
|
|
891
1030
|
return ctx.use(dep);
|
|
892
|
-
},
|
|
1031
|
+
}) as LoaderContext["use"],
|
|
893
1032
|
method: "GET",
|
|
894
1033
|
body: undefined,
|
|
895
1034
|
reverse: createReverseFunction(
|
|
@@ -898,9 +1037,14 @@ export function createUseFunction<TEnv>(
|
|
|
898
1037
|
ctx.params as Record<string, string>,
|
|
899
1038
|
ctx._routeName ? isRouteRootScoped(ctx._routeName) : undefined,
|
|
900
1039
|
),
|
|
1040
|
+
rendered: () => {
|
|
1041
|
+
throw new Error(
|
|
1042
|
+
`ctx.rendered() is only available in DSL loaders (registered via loader() in urls()). ` +
|
|
1043
|
+
`It cannot be used from request-context loaders or server actions.`,
|
|
1044
|
+
);
|
|
1045
|
+
},
|
|
901
1046
|
};
|
|
902
1047
|
|
|
903
|
-
// Start loader execution with tracking
|
|
904
1048
|
const doneLoader = track(`loader:${loader.$$id}`, 2);
|
|
905
1049
|
const promise = Promise.resolve(loaderFn(loaderCtx)).finally(() => {
|
|
906
1050
|
doneLoader();
|
package/src/ssr/index.tsx
CHANGED
|
@@ -129,6 +129,7 @@ interface RscPayload {
|
|
|
129
129
|
matched?: string[];
|
|
130
130
|
pathname?: string;
|
|
131
131
|
params?: Record<string, string>;
|
|
132
|
+
basename?: string;
|
|
132
133
|
themeConfig?: ResolvedThemeConfig | null;
|
|
133
134
|
initialTheme?: Theme;
|
|
134
135
|
version?: string;
|
|
@@ -161,13 +162,18 @@ function createSsrEventController(opts: {
|
|
|
161
162
|
}): EventController {
|
|
162
163
|
const location = new URL(opts.pathname, "http://localhost");
|
|
163
164
|
let params = opts.params ?? {};
|
|
165
|
+
const rawMatched = opts.matched ?? [];
|
|
164
166
|
const handleState = {
|
|
165
167
|
data: opts.handleData ?? {},
|
|
166
|
-
segmentOrder: filterSegmentOrder(
|
|
168
|
+
segmentOrder: filterSegmentOrder(rawMatched),
|
|
169
|
+
routeSegmentIds: rawMatched.filter(
|
|
170
|
+
(id) => !id.includes(".@") && !/D\d+\./.test(id),
|
|
171
|
+
),
|
|
167
172
|
};
|
|
168
173
|
const state: DerivedNavigationState = {
|
|
169
174
|
state: "idle",
|
|
170
175
|
isStreaming: false,
|
|
176
|
+
isNavigating: false,
|
|
171
177
|
location,
|
|
172
178
|
pendingUrl: null,
|
|
173
179
|
inflightActions: [],
|
|
@@ -260,6 +266,7 @@ export function createSSRHandler<TEnv = unknown>(deps: SSRDependencies<TEnv>) {
|
|
|
260
266
|
function SsrRoot() {
|
|
261
267
|
payload ??= createFromReadableStream<RscPayload>(rscStream1);
|
|
262
268
|
const resolved = React.use(payload);
|
|
269
|
+
|
|
263
270
|
const themeConfig = resolved.metadata?.themeConfig ?? null;
|
|
264
271
|
const pathname = resolved.metadata?.pathname ?? "/";
|
|
265
272
|
|
|
@@ -285,6 +292,7 @@ export function createSSRHandler<TEnv = unknown>(deps: SSRDependencies<TEnv>) {
|
|
|
285
292
|
navigate: async () => {},
|
|
286
293
|
refresh: async () => {},
|
|
287
294
|
version: resolved.metadata?.version,
|
|
295
|
+
basename: resolved.metadata?.basename,
|
|
288
296
|
};
|
|
289
297
|
|
|
290
298
|
// Build content tree from segments.
|
package/src/static-handler.ts
CHANGED
|
@@ -32,11 +32,21 @@
|
|
|
32
32
|
*/
|
|
33
33
|
import type { ReactNode } from "react";
|
|
34
34
|
import type { Handler } from "./types.js";
|
|
35
|
-
import type {
|
|
35
|
+
import type { StaticBuildContext } from "./prerender.js";
|
|
36
|
+
import type { UseItems, HandlerUseItem } from "./route-types.js";
|
|
36
37
|
import { isCachedFunction } from "./cache/taint.js";
|
|
37
38
|
|
|
38
39
|
// -- Types ------------------------------------------------------------------
|
|
39
40
|
|
|
41
|
+
export interface StaticHandlerOptions {
|
|
42
|
+
/**
|
|
43
|
+
* Keep handler in server bundle for live fallback (default: false).
|
|
44
|
+
* false: handler replaced with stub, source-only APIs excluded from bundle.
|
|
45
|
+
* true: handler stays in bundle, renders live at request time.
|
|
46
|
+
*/
|
|
47
|
+
passthrough?: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
40
50
|
export interface StaticHandlerDefinition<
|
|
41
51
|
TParams extends Record<string, any> = any,
|
|
42
52
|
> {
|
|
@@ -46,14 +56,16 @@ export interface StaticHandlerDefinition<
|
|
|
46
56
|
/** In dev mode, the actual handler function that layout/path/parallel can call. */
|
|
47
57
|
handler: Handler<TParams>;
|
|
48
58
|
/** Static handler options (passthrough support). */
|
|
49
|
-
options?:
|
|
59
|
+
options?: StaticHandlerOptions;
|
|
60
|
+
/** Composable default DSL items merged when the handler is mounted. */
|
|
61
|
+
use?: () => UseItems<HandlerUseItem>;
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
// -- Function ---------------------------------------------------------------
|
|
53
65
|
|
|
54
66
|
export function Static<TParams extends Record<string, any> = {}>(
|
|
55
67
|
handler: (ctx: StaticBuildContext) => ReactNode | Promise<ReactNode>,
|
|
56
|
-
options?:
|
|
68
|
+
options?: StaticHandlerOptions,
|
|
57
69
|
__injectedId?: string,
|
|
58
70
|
): StaticHandlerDefinition<TParams>;
|
|
59
71
|
|
|
@@ -61,7 +73,7 @@ export function Static<TParams extends Record<string, any> = {}>(
|
|
|
61
73
|
|
|
62
74
|
export function Static<TParams extends Record<string, any>>(
|
|
63
75
|
handler: Function,
|
|
64
|
-
optionsOrId?:
|
|
76
|
+
optionsOrId?: StaticHandlerOptions | string,
|
|
65
77
|
maybeId?: string,
|
|
66
78
|
): StaticHandlerDefinition<TParams> {
|
|
67
79
|
if (isCachedFunction(handler)) {
|
|
@@ -72,19 +84,19 @@ export function Static<TParams extends Record<string, any>>(
|
|
|
72
84
|
);
|
|
73
85
|
}
|
|
74
86
|
|
|
75
|
-
let options:
|
|
87
|
+
let options: StaticHandlerOptions | undefined;
|
|
76
88
|
let id: string;
|
|
77
89
|
|
|
78
90
|
if (typeof optionsOrId === "string") {
|
|
79
91
|
id = optionsOrId;
|
|
80
92
|
} else {
|
|
81
|
-
options = optionsOrId as
|
|
93
|
+
options = optionsOrId as StaticHandlerOptions | undefined;
|
|
82
94
|
id = maybeId ?? "";
|
|
83
95
|
}
|
|
84
96
|
|
|
85
97
|
if (!id) {
|
|
86
98
|
throw new Error(
|
|
87
|
-
"[
|
|
99
|
+
"[rango] Static: missing $$id. " +
|
|
88
100
|
"Ensure the exposeInternalIds Vite plugin is configured.",
|
|
89
101
|
);
|
|
90
102
|
}
|