@rangojs/router 0.0.0-experimental.20 → 0.0.0-experimental.204030a9
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 +4 -0
- package/README.md +242 -55
- package/dist/bin/rango.js +277 -99
- package/dist/vite/index.js +2929 -1132
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +68 -21
- package/skills/breadcrumbs/SKILL.md +252 -0
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +243 -21
- package/skills/caching/SKILL.md +159 -10
- 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 +262 -51
- package/skills/host-router/SKILL.md +243 -0
- 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 +291 -31
- 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 +125 -102
- package/skills/rango/SKILL.md +242 -23
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +66 -9
- package/skills/route/SKILL.md +91 -8
- package/skills/router-setup/SKILL.md +98 -8
- package/skills/server-actions/SKILL.md +751 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/testing/SKILL.md +511 -188
- package/skills/typesafety/SKILL.md +354 -50
- 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 +92 -0
- 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/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +183 -18
- package/src/browser/navigation-client.ts +187 -57
- package/src/browser/navigation-store.ts +75 -17
- package/src/browser/navigation-transaction.ts +21 -37
- package/src/browser/partial-update.ts +143 -40
- package/src/browser/prefetch/cache.ts +275 -28
- package/src/browser/prefetch/fetch.ts +191 -46
- package/src/browser/prefetch/policy.ts +6 -0
- 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 +98 -14
- 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 +200 -75
- package/src/browser/scroll-restoration.ts +46 -39
- 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 +81 -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 +69 -26
- 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 +5 -1
- package/src/client.tsx +95 -284
- 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/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/host/index.ts +2 -5
- 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 +15 -40
- package/src/index.ts +92 -76
- 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 +57 -15
- 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 -3
- 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 +105 -39
- package/src/router/intercept-resolution.ts +15 -22
- package/src/router/lazy-includes.ts +12 -9
- package/src/router/loader-resolution.ts +175 -23
- package/src/router/logging.ts +5 -2
- package/src/router/manifest.ts +31 -16
- package/src/router/match-api.ts +129 -193
- 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 +124 -18
- package/src/router/metrics.ts +239 -14
- package/src/router/middleware-types.ts +61 -31
- package/src/router/middleware.ts +226 -124
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +118 -19
- package/src/router/prerender-match.ts +114 -10
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +286 -0
- package/src/router/revalidation.ts +85 -9
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +6 -1
- package/src/router/router-interfaces.ts +91 -29
- package/src/router/router-options.ts +89 -19
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +240 -23
- package/src/router/segment-resolution/helpers.ts +30 -25
- package/src/router/segment-resolution/loader-cache.ts +1 -0
- package/src/router/segment-resolution/revalidation.ts +483 -289
- 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 +38 -15
- package/src/router/types.ts +9 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +120 -32
- package/src/rsc/handler-context.ts +2 -2
- package/src/rsc/handler.ts +524 -370
- package/src/rsc/helpers.ts +91 -43
- package/src/rsc/index.ts +1 -21
- 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 +39 -10
- package/src/rsc/response-route-handler.ts +46 -53
- package/src/rsc/rsc-rendering.ts +69 -89
- package/src/rsc/runtime-warnings.ts +9 -10
- package/src/rsc/server-action.ts +39 -47
- package/src/rsc/ssr-setup.ts +144 -0
- package/src/rsc/types.ts +19 -3
- package/src/search-params.ts +20 -17
- 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 +285 -63
- 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 +228 -65
- package/src/server.ts +6 -0
- 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 +98 -0
- package/src/testing/internal/context.ts +151 -0
- package/src/testing/render-route.tsx +536 -0
- package/src/testing/run-loader.ts +296 -0
- package/src/testing/run-middleware.ts +170 -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 +112 -0
- package/src/theme/index.ts +4 -13
- package/src/types/cache-types.ts +4 -4
- package/src/types/global-namespace.ts +39 -26
- package/src/types/handler-context.ts +197 -79
- 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-config.ts +17 -8
- package/src/types/route-entry.ts +19 -1
- package/src/types/segments.ts +37 -6
- 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 +63 -91
- package/src/vite/discovery/discover-routers.ts +106 -53
- 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 +222 -107
- 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 +50 -13
- package/src/vite/discovery/virtual-module-codegen.ts +13 -23
- package/src/vite/index.ts +10 -3
- 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 +127 -0
- 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 +924 -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 +98 -5
- package/src/vite/utils/shared-utils.ts +109 -27
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/route-definition/route-function.ts +0 -119
|
@@ -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
|
-
import { track } from "./context.js";
|
|
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,19 +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 (system params like _rsc* are NOT filtered here) */
|
|
53
|
-
url: URL;
|
|
54
|
-
/** URL pathname */
|
|
55
|
-
pathname: string;
|
|
56
|
-
/** URL search params (system params like _rsc* are NOT filtered here) */
|
|
57
|
-
searchParams: URLSearchParams;
|
|
58
|
-
/** Variables set by middleware (same as ctx.var) */
|
|
59
|
-
var: Record<string, any>;
|
|
63
|
+
> extends RequestScope<TEnv> {
|
|
64
|
+
/** @internal Shared variable backing store for ctx.get()/ctx.set(). */
|
|
65
|
+
_variables: Record<string, any>;
|
|
60
66
|
/** Get a variable set by middleware */
|
|
61
67
|
get: {
|
|
62
68
|
<T>(contextVar: ContextVar<T>): T | undefined;
|
|
@@ -64,20 +70,19 @@ export interface RequestContext<
|
|
|
64
70
|
};
|
|
65
71
|
/** Set a variable (shared with middleware and handlers) */
|
|
66
72
|
set: {
|
|
67
|
-
<T>(
|
|
68
|
-
|
|
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;
|
|
69
79
|
};
|
|
70
80
|
/**
|
|
71
81
|
* Route params (populated after route matching)
|
|
72
82
|
* Initially empty, then set to matched params
|
|
73
83
|
*/
|
|
74
84
|
params: TParams;
|
|
75
|
-
/**
|
|
76
|
-
* Stub response for setting headers/cookies (read-only).
|
|
77
|
-
* Headers set here are merged into the final response.
|
|
78
|
-
* Use header() or setStatus() to mutate response headers/status.
|
|
79
|
-
* Use cookies().set()/cookies().delete() for cookie mutations.
|
|
80
|
-
*/
|
|
85
|
+
/** @internal Stub response for collecting headers/cookies. Use ctx.headers or ctx.header() instead. */
|
|
81
86
|
readonly res: Response;
|
|
82
87
|
|
|
83
88
|
/** @internal Get a cookie value (effective: request + response mutations). Use cookies().get() instead. */
|
|
@@ -95,6 +100,8 @@ export interface RequestContext<
|
|
|
95
100
|
header(name: string, value: string): void;
|
|
96
101
|
/** Set the response status code */
|
|
97
102
|
setStatus(status: number): void;
|
|
103
|
+
/** @internal Set status bypassing cache-exec guard (for framework error handling) */
|
|
104
|
+
_setStatus(status: number): void;
|
|
98
105
|
|
|
99
106
|
/**
|
|
100
107
|
* Access loader data or push handle data.
|
|
@@ -139,20 +146,6 @@ export interface RequestContext<
|
|
|
139
146
|
import("../cache/profile-registry.js").CacheProfile
|
|
140
147
|
>;
|
|
141
148
|
|
|
142
|
-
/**
|
|
143
|
-
* Schedule work to run after the response is sent.
|
|
144
|
-
* On Cloudflare Workers, uses ctx.waitUntil().
|
|
145
|
-
* On Node.js, runs as fire-and-forget.
|
|
146
|
-
*
|
|
147
|
-
* @example
|
|
148
|
-
* ```typescript
|
|
149
|
-
* ctx.waitUntil(async () => {
|
|
150
|
-
* await cacheStore.set(key, data, ttl);
|
|
151
|
-
* });
|
|
152
|
-
* ```
|
|
153
|
-
*/
|
|
154
|
-
waitUntil(fn: () => Promise<void>): void;
|
|
155
|
-
|
|
156
149
|
/**
|
|
157
150
|
* Register a callback to run when the response is created.
|
|
158
151
|
* Callbacks are sync and receive the response. They can:
|
|
@@ -256,6 +249,54 @@ export interface RequestContext<
|
|
|
256
249
|
/** @internal Previous route key (from the navigation source), used for revalidation */
|
|
257
250
|
_prevRouteKey?: string;
|
|
258
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
|
+
|
|
259
300
|
/** @internal Per-request error dedup set for onError reporting */
|
|
260
301
|
_reportedErrors: WeakSet<object>;
|
|
261
302
|
|
|
@@ -266,6 +307,30 @@ export interface RequestContext<
|
|
|
266
307
|
* errors without failing the response.
|
|
267
308
|
*/
|
|
268
309
|
_reportBackgroundError?: (error: unknown, category: string) => void;
|
|
310
|
+
|
|
311
|
+
/** @internal Per-request debug performance override (set via ctx.debugPerformance()) */
|
|
312
|
+
_debugPerformance?: boolean;
|
|
313
|
+
|
|
314
|
+
/** @internal Request-scoped performance metrics store */
|
|
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[];
|
|
269
334
|
}
|
|
270
335
|
|
|
271
336
|
/**
|
|
@@ -292,7 +357,22 @@ export type PublicRequestContext<
|
|
|
292
357
|
| "_routeName"
|
|
293
358
|
| "_prevRouteKey"
|
|
294
359
|
| "_reportedErrors"
|
|
360
|
+
| "_renderBarrier"
|
|
361
|
+
| "_resolveRenderBarrier"
|
|
362
|
+
| "_renderBarrierSegmentOrder"
|
|
363
|
+
| "_treeHasStreaming"
|
|
364
|
+
| "_renderBarrierWaiters"
|
|
365
|
+
| "_handlerLoaderDeps"
|
|
366
|
+
| "_renderBarrierHandleSnapshot"
|
|
295
367
|
| "_reportBackgroundError"
|
|
368
|
+
| "_debugPerformance"
|
|
369
|
+
| "_metricsStore"
|
|
370
|
+
| "_basename"
|
|
371
|
+
| "_setStatus"
|
|
372
|
+
| "_variables"
|
|
373
|
+
| "_classifiedRoute"
|
|
374
|
+
| "_cacheSignal"
|
|
375
|
+
| "res"
|
|
296
376
|
>;
|
|
297
377
|
|
|
298
378
|
// AsyncLocalStorage instance for request context
|
|
@@ -401,13 +481,7 @@ export function requireRequestContext<
|
|
|
401
481
|
return getRequestContext<TEnv>();
|
|
402
482
|
}
|
|
403
483
|
|
|
404
|
-
|
|
405
|
-
* Cloudflare Workers ExecutionContext (subset we need)
|
|
406
|
-
*/
|
|
407
|
-
export interface ExecutionContext {
|
|
408
|
-
waitUntil(promise: Promise<any>): void;
|
|
409
|
-
passThroughOnException(): void;
|
|
410
|
-
}
|
|
484
|
+
export type { ExecutionContext };
|
|
411
485
|
|
|
412
486
|
/**
|
|
413
487
|
* Options for creating a request context
|
|
@@ -491,6 +565,18 @@ export function createRequestContext<TEnv>(
|
|
|
491
565
|
responseCookieCache = null;
|
|
492
566
|
};
|
|
493
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
|
+
|
|
494
580
|
// Effective cookie read: response stub Set-Cookie wins, then original header.
|
|
495
581
|
// The stub IS the source of truth for same-request mutations.
|
|
496
582
|
const effectiveCookie = (name: string): string | undefined => {
|
|
@@ -543,19 +629,31 @@ export function createRequestContext<TEnv>(
|
|
|
543
629
|
invalidateResponseCookieCache();
|
|
544
630
|
};
|
|
545
631
|
|
|
632
|
+
// Strip internal _rsc* params so userland sees a clean URL.
|
|
633
|
+
const cleanUrl = stripInternalParams(url);
|
|
634
|
+
|
|
546
635
|
// Build the context object first (without use), then add use
|
|
547
636
|
const ctx: RequestContext<TEnv> = {
|
|
548
637
|
env,
|
|
549
638
|
request,
|
|
550
|
-
url,
|
|
639
|
+
url: cleanUrl,
|
|
640
|
+
originalUrl: new URL(request.url),
|
|
551
641
|
pathname: url.pathname,
|
|
552
|
-
searchParams:
|
|
553
|
-
|
|
554
|
-
get: ((keyOrVar: any) =>
|
|
555
|
-
|
|
556
|
-
|
|
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) => {
|
|
557
655
|
assertNotInsideCacheExec(ctx, "set");
|
|
558
|
-
contextSet(variables, keyOrVar, value);
|
|
656
|
+
contextSet(variables, keyOrVar, value, options);
|
|
559
657
|
}) as RequestContext<TEnv>["set"],
|
|
560
658
|
params: {} as Record<string, string>,
|
|
561
659
|
|
|
@@ -593,6 +691,7 @@ export function createRequestContext<TEnv>(
|
|
|
593
691
|
|
|
594
692
|
setCookie(name: string, value: string, options?: CookieOptions): void {
|
|
595
693
|
assertNotInsideCacheExec(ctx, "setCookie");
|
|
694
|
+
assertNotInsideCacheScopeALS("setCookie");
|
|
596
695
|
stubResponse.headers.append(
|
|
597
696
|
"Set-Cookie",
|
|
598
697
|
serializeCookieValue(name, value, options),
|
|
@@ -605,6 +704,7 @@ export function createRequestContext<TEnv>(
|
|
|
605
704
|
options?: Pick<CookieOptions, "domain" | "path">,
|
|
606
705
|
): void {
|
|
607
706
|
assertNotInsideCacheExec(ctx, "deleteCookie");
|
|
707
|
+
assertNotInsideCacheScopeALS("deleteCookie");
|
|
608
708
|
stubResponse.headers.append(
|
|
609
709
|
"Set-Cookie",
|
|
610
710
|
serializeCookieValue(name, "", { ...options, maxAge: 0 }),
|
|
@@ -614,13 +714,20 @@ export function createRequestContext<TEnv>(
|
|
|
614
714
|
|
|
615
715
|
header(name: string, value: string): void {
|
|
616
716
|
assertNotInsideCacheExec(ctx, "header");
|
|
717
|
+
assertNotInsideCacheScopeALS("header");
|
|
617
718
|
stubResponse.headers.set(name, value);
|
|
618
719
|
},
|
|
619
720
|
|
|
620
721
|
setStatus(status: number): void {
|
|
621
722
|
assertNotInsideCacheExec(ctx, "setStatus");
|
|
622
|
-
|
|
623
|
-
|
|
723
|
+
assertNotInsideCacheScopeALS("setStatus");
|
|
724
|
+
stubResponse = new Response(null, {
|
|
725
|
+
status,
|
|
726
|
+
headers: stubResponse.headers,
|
|
727
|
+
});
|
|
728
|
+
},
|
|
729
|
+
|
|
730
|
+
_setStatus(status: number): void {
|
|
624
731
|
stubResponse = new Response(null, {
|
|
625
732
|
status,
|
|
626
733
|
headers: stubResponse.headers,
|
|
@@ -638,20 +745,19 @@ export function createRequestContext<TEnv>(
|
|
|
638
745
|
|
|
639
746
|
waitUntil(fn: () => Promise<void>): void {
|
|
640
747
|
if (executionContext?.waitUntil) {
|
|
641
|
-
// Cloudflare Workers: use native waitUntil
|
|
642
748
|
executionContext.waitUntil(fn());
|
|
643
749
|
} else {
|
|
644
|
-
|
|
645
|
-
fn().catch((err) =>
|
|
646
|
-
console.error("[waitUntil] Background task failed:", err),
|
|
647
|
-
);
|
|
750
|
+
fireAndForgetWaitUntil(fn);
|
|
648
751
|
}
|
|
649
752
|
},
|
|
650
753
|
|
|
754
|
+
executionContext,
|
|
755
|
+
|
|
651
756
|
_onResponseCallbacks: [],
|
|
652
757
|
|
|
653
758
|
onResponse(callback: (response: Response) => Response): void {
|
|
654
759
|
assertNotInsideCacheExec(ctx, "onResponse");
|
|
760
|
+
assertNotInsideCacheScopeALS("onResponse");
|
|
655
761
|
this._onResponseCallbacks.push(callback);
|
|
656
762
|
},
|
|
657
763
|
|
|
@@ -677,10 +783,60 @@ export function createRequestContext<TEnv>(
|
|
|
677
783
|
_locationState: undefined,
|
|
678
784
|
|
|
679
785
|
_reportedErrors: new WeakSet<object>(),
|
|
786
|
+
_metricsStore: undefined,
|
|
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,
|
|
680
792
|
|
|
681
793
|
reverse: createReverseFunction(getGlobalRouteMap(), undefined, {}),
|
|
682
794
|
};
|
|
683
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
|
+
|
|
684
840
|
// Now create use() with access to ctx
|
|
685
841
|
ctx.use = createUseFunction({
|
|
686
842
|
handleStore,
|
|
@@ -862,15 +1018,17 @@ export function createUseFunction<TEnv>(
|
|
|
862
1018
|
search: (ctx as any).search ?? {},
|
|
863
1019
|
pathname: ctx.pathname,
|
|
864
1020
|
url: ctx.url,
|
|
1021
|
+
originalUrl: ctx.originalUrl,
|
|
865
1022
|
env: ctx.env as any,
|
|
866
|
-
|
|
1023
|
+
waitUntil: ctx.waitUntil.bind(ctx),
|
|
1024
|
+
executionContext: ctx.executionContext,
|
|
867
1025
|
get: ctx.get as any,
|
|
868
|
-
use: <TDep, TDepParams = any>(
|
|
1026
|
+
use: (<TDep, TDepParams = any>(
|
|
869
1027
|
dep: LoaderDefinition<TDep, TDepParams>,
|
|
870
1028
|
): Promise<TDep> => {
|
|
871
1029
|
// Recursive call - will start dep loader if not already started
|
|
872
1030
|
return ctx.use(dep);
|
|
873
|
-
},
|
|
1031
|
+
}) as LoaderContext["use"],
|
|
874
1032
|
method: "GET",
|
|
875
1033
|
body: undefined,
|
|
876
1034
|
reverse: createReverseFunction(
|
|
@@ -879,10 +1037,15 @@ export function createUseFunction<TEnv>(
|
|
|
879
1037
|
ctx.params as Record<string, string>,
|
|
880
1038
|
ctx._routeName ? isRouteRootScoped(ctx._routeName) : undefined,
|
|
881
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
|
+
},
|
|
882
1046
|
};
|
|
883
1047
|
|
|
884
|
-
|
|
885
|
-
const doneLoader = track(`loader:${loader.$$id}`);
|
|
1048
|
+
const doneLoader = track(`loader:${loader.$$id}`, 2);
|
|
886
1049
|
const promise = Promise.resolve(loaderFn(loaderCtx)).finally(() => {
|
|
887
1050
|
doneLoader();
|
|
888
1051
|
});
|
package/src/server.ts
CHANGED
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
// Router registry (used by Vite plugin for build-time discovery)
|
|
12
12
|
export { RSC_ROUTER_BRAND, RouterRegistry } from "./router.js";
|
|
13
13
|
|
|
14
|
+
// Host router registry (used by Vite plugin for host-router lazy discovery)
|
|
15
|
+
export {
|
|
16
|
+
HostRouterRegistry,
|
|
17
|
+
type HostRouterRegistryEntry,
|
|
18
|
+
} from "./host/router.js";
|
|
19
|
+
|
|
14
20
|
// Route map builder (Vite plugin injects these via virtual modules)
|
|
15
21
|
export {
|
|
16
22
|
registerRouteMap,
|
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
|
}
|