@rangojs/router 0.0.0-experimental.122 → 0.0.0-experimental.125
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/dist/bin/rango.js +10 -6
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +55 -48
- package/package.json +61 -21
- package/skills/caching/SKILL.md +2 -1
- package/skills/hooks/SKILL.md +40 -29
- package/skills/host-router/SKILL.md +16 -2
- package/skills/intercept/SKILL.md +4 -2
- package/skills/layout/SKILL.md +11 -6
- package/skills/loader/SKILL.md +6 -2
- package/skills/middleware/SKILL.md +4 -2
- package/skills/migrate-nextjs/SKILL.md +3 -1
- package/skills/parallel/SKILL.md +9 -4
- package/skills/rango/SKILL.md +12 -0
- package/skills/route/SKILL.md +10 -2
- package/skills/testing/SKILL.md +129 -0
- package/skills/testing/bindings.md +89 -0
- package/skills/testing/cache-prerender.md +98 -0
- package/skills/testing/client-components.md +122 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +89 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +118 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +120 -0
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +1 -1
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +1 -83
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/navigation-bridge.ts +14 -1
- package/src/browser/navigation-client.ts +14 -1
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +26 -51
- package/src/browser/navigation-transaction.ts +0 -32
- package/src/browser/partial-update.ts +1 -83
- package/src/browser/prefetch/cache.ts +6 -45
- package/src/browser/prefetch/fetch.ts +7 -0
- package/src/browser/prefetch/queue.ts +6 -3
- package/src/browser/rango-state.ts +157 -99
- package/src/browser/react/Link.tsx +0 -2
- package/src/browser/react/NavigationProvider.tsx +2 -1
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +0 -2
- package/src/browser/react/index.ts +0 -51
- package/src/browser/react/location-state-shared.ts +0 -13
- package/src/browser/react/location-state.ts +0 -1
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +0 -5
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +0 -3
- package/src/browser/react/use-params.ts +0 -2
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +0 -13
- package/src/browser/rsc-router.tsx +12 -4
- package/src/browser/server-action-bridge.ts +77 -15
- package/src/browser/types.ts +7 -2
- package/src/browser/validate-redirect-origin.ts +4 -5
- package/src/build/route-trie.ts +3 -0
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/router-processing.ts +0 -8
- package/src/cache/cache-policy.ts +0 -54
- package/src/cache/cache-runtime.ts +27 -24
- package/src/cache/cache-scope.ts +0 -27
- package/src/cache/cache-tag.ts +0 -37
- package/src/cache/cf/cf-cache-store.ts +94 -46
- package/src/cache/cf/index.ts +0 -24
- package/src/cache/document-cache.ts +11 -36
- package/src/cache/handle-snapshot.ts +0 -40
- package/src/cache/index.ts +0 -27
- package/src/cache/memory-segment-store.ts +2 -48
- package/src/cache/profile-registry.ts +7 -3
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +0 -16
- package/src/cache/types.ts +0 -98
- package/src/client.rsc.tsx +1 -22
- package/src/client.tsx +14 -38
- package/src/component-utils.ts +19 -0
- package/src/deps/ssr.ts +0 -1
- package/src/handle.ts +28 -18
- package/src/handles/MetaTags.tsx +0 -14
- package/src/handles/meta.ts +0 -39
- package/src/host/cookie-handler.ts +0 -36
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +6 -0
- package/src/host/pattern-matcher.ts +7 -50
- package/src/host/router.ts +1 -65
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +6 -2
- package/src/href-client.ts +0 -4
- package/src/index.rsc.ts +42 -3
- package/src/index.ts +31 -1
- package/src/internal-debug.ts +2 -4
- package/src/loader.rsc.ts +19 -9
- package/src/loader.ts +12 -4
- package/src/network-error-thrower.tsx +1 -6
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +23 -30
- package/src/prerender.ts +58 -3
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +1 -44
- package/src/route-definition/dsl-helpers.ts +7 -19
- package/src/route-definition/helpers-types.ts +3 -3
- package/src/route-definition/redirect.ts +11 -1
- package/src/route-map-builder.ts +0 -16
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +0 -13
- package/src/router/error-handling.ts +12 -16
- package/src/router/find-match.ts +4 -30
- package/src/router/intercept-resolution.ts +10 -1
- package/src/router/lazy-includes.ts +1 -57
- package/src/router/loader-resolution.ts +3 -2
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +1 -25
- package/src/router/match-api.ts +0 -20
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +57 -58
- package/src/router/match-middleware/background-revalidation.ts +0 -7
- package/src/router/match-middleware/cache-lookup.ts +1 -54
- package/src/router/match-middleware/cache-store.ts +0 -31
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -21
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +1 -52
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-cookies.ts +0 -13
- package/src/router/middleware-types.ts +0 -115
- package/src/router/middleware.ts +7 -30
- package/src/router/navigation-snapshot.ts +0 -51
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +1 -33
- package/src/router/prerender-match.ts +33 -45
- package/src/router/request-classification.ts +1 -38
- package/src/router/revalidation.ts +5 -58
- package/src/router/router-context.ts +0 -26
- package/src/router/router-interfaces.ts +7 -0
- package/src/router/router-options.ts +30 -0
- package/src/router/segment-resolution/fresh.ts +25 -57
- package/src/router/segment-resolution/helpers.ts +34 -0
- package/src/router/segment-resolution/loader-cache.ts +10 -13
- package/src/router/segment-resolution/revalidation.ts +5 -42
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +63 -40
- package/src/router/types.ts +1 -63
- package/src/router/url-params.ts +0 -5
- package/src/router.ts +40 -9
- package/src/rsc/handler.ts +14 -2
- package/src/rsc/helpers.ts +34 -0
- package/src/rsc/origin-guard.ts +0 -12
- package/src/rsc/progressive-enhancement.ts +4 -1
- package/src/rsc/rsc-rendering.ts +4 -7
- package/src/rsc/runtime-warnings.ts +14 -0
- package/src/rsc/server-action.ts +30 -28
- package/src/rsc/types.ts +2 -1
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +0 -16
- package/src/segment-loader-promise.ts +14 -2
- package/src/segment-system.tsx +79 -88
- package/src/server/cookie-store.ts +52 -1
- package/src/server/handle-store.ts +7 -24
- package/src/server/loader-registry.ts +5 -24
- package/src/server/request-context.ts +74 -77
- package/src/ssr/index.tsx +14 -14
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +119 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +581 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +127 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +186 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +98 -0
- package/src/testing/internal/context.ts +348 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +311 -0
- package/src/testing/render-route.tsx +504 -0
- package/src/testing/run-loader.ts +378 -0
- package/src/testing/run-middleware.ts +205 -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 +305 -0
- package/src/theme/ThemeProvider.tsx +0 -52
- package/src/theme/ThemeScript.tsx +0 -6
- package/src/theme/constants.ts +0 -12
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +0 -14
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/error-types.ts +25 -89
- package/src/types/global-namespace.ts +15 -15
- package/src/types/handler-context.ts +16 -13
- package/src/types/index.ts +0 -10
- package/src/types/request-scope.ts +0 -19
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +0 -13
- package/src/urls/include-helper.ts +0 -4
- package/src/urls/index.ts +0 -6
- package/src/urls/path-helper-types.ts +2 -2
- package/src/urls/path-helper.ts +0 -54
- package/src/urls/urls-function.ts +0 -13
- package/src/use-loader.tsx +0 -186
- package/src/vite/discovery/bundle-postprocess.ts +2 -1
- package/src/vite/discovery/discover-routers.ts +6 -7
- package/src/vite/discovery/virtual-module-codegen.ts +1 -11
- package/src/vite/plugin-types.ts +3 -1
- package/src/vite/plugins/cjs-to-esm.ts +0 -11
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +0 -10
- package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
- package/src/vite/plugins/expose-action-id.ts +2 -73
- package/src/vite/plugins/expose-id-utils.ts +0 -55
- package/src/vite/plugins/expose-ids/export-analysis.ts +0 -38
- package/src/vite/plugins/expose-ids/handler-transform.ts +0 -15
- package/src/vite/plugins/expose-ids/loader-transform.ts +0 -15
- package/src/vite/plugins/expose-ids/router-transform.ts +0 -13
- package/src/vite/plugins/expose-internal-ids.ts +10 -0
- package/src/vite/plugins/performance-tracks.ts +0 -3
- package/src/vite/plugins/use-cache-transform.ts +0 -36
- package/src/vite/plugins/version-injector.ts +0 -20
- package/src/vite/plugins/version-plugin.ts +1 -49
- package/src/vite/plugins/virtual-entries.ts +0 -15
- package/src/vite/rango.ts +1 -108
- package/src/vite/router-discovery.ts +2 -1
- package/src/vite/utils/ast-handler-extract.ts +0 -16
- package/src/vite/utils/bundle-analysis.ts +6 -13
- package/src/vite/utils/client-chunks.ts +0 -6
- package/src/vite/utils/forward-user-plugins.ts +0 -22
- package/src/vite/utils/manifest-utils.ts +0 -4
- package/src/vite/utils/package-resolution.ts +1 -73
- package/src/vite/utils/prerender-utils.ts +0 -35
- package/src/vite/utils/shared-utils.ts +3 -35
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
|
@@ -13,10 +13,15 @@
|
|
|
13
13
|
|
|
14
14
|
import type { CacheItemResult, CacheItemOptions } from "./types.js";
|
|
15
15
|
import { runBackground } from "./background-task.js";
|
|
16
|
+
import { reportCacheError } from "./cache-error.js";
|
|
17
|
+
import type { CacheErrorReporter } from "./cache-error.js";
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
// The host carries both the optional waitUntil (for background scheduling) and
|
|
20
|
+
// the CacheErrorReporter seam (for routing degradation errors through onError).
|
|
21
|
+
// loader-cache.ts passes the request context, which provides both.
|
|
22
|
+
type WaitUntilHost = {
|
|
18
23
|
waitUntil?: (fn: () => Promise<void>) => void;
|
|
19
|
-
}
|
|
24
|
+
} & CacheErrorReporter;
|
|
20
25
|
|
|
21
26
|
export interface ReadThroughItemConfig<T> {
|
|
22
27
|
/** Retrieve a cached item by key */
|
|
@@ -74,11 +79,20 @@ export async function readThroughItem<T>(
|
|
|
74
79
|
host,
|
|
75
80
|
} = config;
|
|
76
81
|
|
|
77
|
-
// Cache lookup
|
|
82
|
+
// Cache lookup. An infra read failure (getItem) is reported by the store
|
|
83
|
+
// itself, so here we just degrade to a miss. A deserialize failure is a
|
|
84
|
+
// corrupt/truncated stored entry, which this layer owns: report it LOUD as
|
|
85
|
+
// cache-corrupt, then fall through to a fresh execution (the miss-path write
|
|
86
|
+
// self-heals the bad entry).
|
|
87
|
+
let cached: CacheItemResult | null = null;
|
|
78
88
|
try {
|
|
79
|
-
|
|
89
|
+
cached = await getItem(key);
|
|
90
|
+
} catch {
|
|
91
|
+
cached = null;
|
|
92
|
+
}
|
|
80
93
|
|
|
81
|
-
|
|
94
|
+
if (cached) {
|
|
95
|
+
try {
|
|
82
96
|
const data = await deserialize(cached.value);
|
|
83
97
|
|
|
84
98
|
if (!cached.shouldRevalidate) {
|
|
@@ -97,16 +111,27 @@ export async function readThroughItem<T>(
|
|
|
97
111
|
if (serialized !== null) {
|
|
98
112
|
await setItem(key, serialized, storeOptions);
|
|
99
113
|
}
|
|
100
|
-
} catch {
|
|
101
|
-
|
|
114
|
+
} catch (error) {
|
|
115
|
+
reportCacheError(
|
|
116
|
+
error,
|
|
117
|
+
"stale-revalidation",
|
|
118
|
+
"[read-through] background revalidation",
|
|
119
|
+
host ?? undefined,
|
|
120
|
+
);
|
|
102
121
|
}
|
|
103
122
|
},
|
|
104
123
|
true,
|
|
105
124
|
);
|
|
106
125
|
return data;
|
|
126
|
+
} catch (error) {
|
|
127
|
+
reportCacheError(
|
|
128
|
+
error,
|
|
129
|
+
"cache-corrupt",
|
|
130
|
+
"[read-through] deserialize stored entry",
|
|
131
|
+
host ?? undefined,
|
|
132
|
+
);
|
|
133
|
+
// fall through to fresh execution
|
|
107
134
|
}
|
|
108
|
-
} catch {
|
|
109
|
-
// Cache lookup failed, fall through to fresh execution
|
|
110
135
|
}
|
|
111
136
|
|
|
112
137
|
// Cache miss
|
|
@@ -123,8 +148,13 @@ export async function readThroughItem<T>(
|
|
|
123
148
|
await setItem(key, serialized, storeOptions);
|
|
124
149
|
onCached?.();
|
|
125
150
|
}
|
|
126
|
-
} catch {
|
|
127
|
-
|
|
151
|
+
} catch (error) {
|
|
152
|
+
reportCacheError(
|
|
153
|
+
error,
|
|
154
|
+
"cache-write",
|
|
155
|
+
"[read-through] cache write",
|
|
156
|
+
host ?? undefined,
|
|
157
|
+
);
|
|
128
158
|
}
|
|
129
159
|
},
|
|
130
160
|
true,
|
|
@@ -16,10 +16,6 @@ import {
|
|
|
16
16
|
} from "@vitejs/plugin-rsc/rsc";
|
|
17
17
|
import { createFromReadableStream } from "@vitejs/plugin-rsc/rsc";
|
|
18
18
|
|
|
19
|
-
// ============================================================================
|
|
20
|
-
// Stream Utilities (internal)
|
|
21
|
-
// ============================================================================
|
|
22
|
-
|
|
23
19
|
/**
|
|
24
20
|
* Convert a ReadableStream to a string.
|
|
25
21
|
*/
|
|
@@ -55,10 +51,6 @@ export function stringToStream(str: string): ReadableStream<Uint8Array> {
|
|
|
55
51
|
});
|
|
56
52
|
}
|
|
57
53
|
|
|
58
|
-
// ============================================================================
|
|
59
|
-
// RSC Serialization Primitives (internal)
|
|
60
|
-
// ============================================================================
|
|
61
|
-
|
|
62
54
|
/**
|
|
63
55
|
* RSC-serialize a value using React Server Components stream.
|
|
64
56
|
* Used for serializing loaderData, layout, loading components etc.
|
|
@@ -90,10 +82,6 @@ export async function rscDeserialize<T>(
|
|
|
90
82
|
return createFromReadableStream<T>(stream, { temporaryReferences });
|
|
91
83
|
}
|
|
92
84
|
|
|
93
|
-
// ============================================================================
|
|
94
|
-
// Null-Preserving RSC Serialization (for caching)
|
|
95
|
-
// ============================================================================
|
|
96
|
-
|
|
97
85
|
/**
|
|
98
86
|
* RSC-serialize any value including null.
|
|
99
87
|
* Unlike rscSerialize(), this does NOT skip null — it serializes it through
|
|
@@ -122,10 +110,6 @@ export async function deserializeResult<T>(encoded: string): Promise<T> {
|
|
|
122
110
|
return createFromReadableStream<T>(stream, { temporaryReferences });
|
|
123
111
|
}
|
|
124
112
|
|
|
125
|
-
// ============================================================================
|
|
126
|
-
// Public API
|
|
127
|
-
// ============================================================================
|
|
128
|
-
|
|
129
113
|
/**
|
|
130
114
|
* RSC-deserialize a single encoded component string back to a React element.
|
|
131
115
|
* Used by the static handler runtime to revive pre-rendered components.
|
package/src/cache/types.ts
CHANGED
|
@@ -12,10 +12,6 @@
|
|
|
12
12
|
import type { ResolvedSegment } from "../types.js";
|
|
13
13
|
import type { RequestContext } from "../server/request-context.js";
|
|
14
14
|
|
|
15
|
-
// ============================================================================
|
|
16
|
-
// Segment Cache Store (low-level storage interface)
|
|
17
|
-
// ============================================================================
|
|
18
|
-
|
|
19
15
|
/**
|
|
20
16
|
* Result from cache get() including data and revalidation status
|
|
21
17
|
*/
|
|
@@ -116,12 +112,6 @@ export interface SegmentCacheStore<TEnv = unknown> {
|
|
|
116
112
|
*/
|
|
117
113
|
clear?(): Promise<void>;
|
|
118
114
|
|
|
119
|
-
// ============================================================================
|
|
120
|
-
// Document Cache Methods (optional)
|
|
121
|
-
// ============================================================================
|
|
122
|
-
// These methods are for caching full HTTP responses (document-level caching).
|
|
123
|
-
// Stores that support response caching should implement these methods.
|
|
124
|
-
|
|
125
115
|
/**
|
|
126
116
|
* Get a cached Response by key.
|
|
127
117
|
* Returns the response and whether it should be revalidated (SWR).
|
|
@@ -146,12 +136,6 @@ export interface SegmentCacheStore<TEnv = unknown> {
|
|
|
146
136
|
tags?: string[],
|
|
147
137
|
): Promise<void>;
|
|
148
138
|
|
|
149
|
-
// ============================================================================
|
|
150
|
-
// Function Cache Methods (optional, for "use cache" directive)
|
|
151
|
-
// ============================================================================
|
|
152
|
-
// These methods cache individual function/component return values.
|
|
153
|
-
// Stores that support "use cache" should implement these methods.
|
|
154
|
-
|
|
155
139
|
/**
|
|
156
140
|
* Get a cached function result by key.
|
|
157
141
|
* Returns the serialized value, optional handle data, and staleness flag.
|
|
@@ -170,10 +154,6 @@ export interface SegmentCacheStore<TEnv = unknown> {
|
|
|
170
154
|
options?: CacheItemOptions,
|
|
171
155
|
): Promise<void>;
|
|
172
156
|
|
|
173
|
-
// ============================================================================
|
|
174
|
-
// Tag-based Invalidation (optional)
|
|
175
|
-
// ============================================================================
|
|
176
|
-
|
|
177
157
|
/**
|
|
178
158
|
* Invalidate every cache entry (segment, response, item) tagged with any of
|
|
179
159
|
* `tags`. Store-level primitive that the public updateTag()/revalidateTag()
|
|
@@ -264,10 +244,6 @@ export interface CachedEntryData {
|
|
|
264
244
|
taggedAt?: number;
|
|
265
245
|
}
|
|
266
246
|
|
|
267
|
-
// ============================================================================
|
|
268
|
-
// Cache Configuration
|
|
269
|
-
// ============================================================================
|
|
270
|
-
|
|
271
247
|
/**
|
|
272
248
|
* Default cache options applied to all cache() boundaries.
|
|
273
249
|
* Individual cache() calls can override any of these values.
|
|
@@ -292,82 +268,8 @@ export interface CacheDefaults {
|
|
|
292
268
|
swr?: number;
|
|
293
269
|
}
|
|
294
270
|
|
|
295
|
-
/**
|
|
296
|
-
* Cache configuration for RSC handler
|
|
297
|
-
*/
|
|
298
|
-
export interface CacheConfig {
|
|
299
|
-
/** Cache store implementation (includes defaults) */
|
|
300
|
-
store: SegmentCacheStore;
|
|
301
|
-
/** Enable/disable caching (default: true) */
|
|
302
|
-
enabled?: boolean;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Cache configuration - can be static or a function receiving env
|
|
307
|
-
*/
|
|
308
|
-
export type CacheConfigOrFactory<TEnv> =
|
|
309
|
-
| CacheConfig
|
|
310
|
-
| ((env: TEnv) => CacheConfig);
|
|
311
|
-
|
|
312
|
-
// ============================================================================
|
|
313
|
-
// Segment Cache Provider (request-level interface)
|
|
314
|
-
// ============================================================================
|
|
315
|
-
|
|
316
271
|
/**
|
|
317
272
|
* Handle data for a single segment
|
|
318
273
|
* Structure: { handleName: [values...] }
|
|
319
274
|
*/
|
|
320
275
|
export type SegmentHandleData = Record<string, unknown[]>;
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Result from cache get() including segments and their handle data
|
|
324
|
-
* Each entry can produce multiple segments (main + parallels)
|
|
325
|
-
*/
|
|
326
|
-
export interface CachedEntryResult {
|
|
327
|
-
/** All segments for this entry (main segment + parallels) */
|
|
328
|
-
segments: ResolvedSegment[];
|
|
329
|
-
/** Handle data keyed by segment ID */
|
|
330
|
-
handles: Record<string, SegmentHandleData>;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Segment cache provider interface
|
|
335
|
-
*
|
|
336
|
-
* Used by router to check/store segment cache during matching.
|
|
337
|
-
* Accessed via request context - if not present, caching is disabled.
|
|
338
|
-
*
|
|
339
|
-
* @internal Not currently implemented - CacheScope is used directly.
|
|
340
|
-
* Reserved for future extensibility.
|
|
341
|
-
*/
|
|
342
|
-
export interface SegmentCacheProvider {
|
|
343
|
-
/** Whether caching is enabled for this request */
|
|
344
|
-
readonly enabled: boolean;
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Get cached segments and restore handles/loaders.
|
|
348
|
-
*
|
|
349
|
-
* Combines cache get with handle replay and loader data restoration.
|
|
350
|
-
* Returns tuple of [segments, segmentIds] if cache hit, null if miss or disabled.
|
|
351
|
-
*
|
|
352
|
-
* @param cacheKey - Cache key to look up
|
|
353
|
-
* @param params - Route params for cache key generation
|
|
354
|
-
* @param loaderPromises - Map to restore loader data into
|
|
355
|
-
* @returns Tuple of [segments, segmentIds] or null if miss
|
|
356
|
-
*/
|
|
357
|
-
restore(
|
|
358
|
-
cacheKey: string,
|
|
359
|
-
params: Record<string, string>,
|
|
360
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
361
|
-
): Promise<[ResolvedSegment[], string[]] | null>;
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Cache entry with automatic handle collection (non-blocking).
|
|
365
|
-
*
|
|
366
|
-
* Schedules caching via waitUntil - handles are collected after they settle.
|
|
367
|
-
* Validates segments have actual components before caching.
|
|
368
|
-
*
|
|
369
|
-
* @param cacheKey - The cache key to store under
|
|
370
|
-
* @param segments - All resolved segments for this entry
|
|
371
|
-
*/
|
|
372
|
-
cacheEntry(cacheKey: string, segments: ResolvedSegment[]): void;
|
|
373
|
-
}
|
package/src/client.rsc.tsx
CHANGED
|
@@ -14,60 +14,43 @@
|
|
|
14
14
|
export {
|
|
15
15
|
Outlet,
|
|
16
16
|
ParallelOutlet,
|
|
17
|
-
OutletProvider,
|
|
18
17
|
useOutlet,
|
|
19
18
|
useLoader,
|
|
20
19
|
ErrorBoundary,
|
|
21
20
|
type ErrorBoundaryProps,
|
|
22
21
|
} from "./client.js";
|
|
23
22
|
|
|
24
|
-
// Re-export the server's createLoader for RSC context
|
|
25
|
-
// This version includes the actual loader function
|
|
26
23
|
export { createLoader } from "./route-definition.js";
|
|
27
24
|
|
|
28
|
-
// Re-export Link component (can be used in server components)
|
|
29
25
|
export {
|
|
30
26
|
Link,
|
|
31
27
|
type LinkProps,
|
|
32
28
|
type PrefetchStrategy,
|
|
33
29
|
} from "./browser/react/Link.js";
|
|
34
30
|
|
|
35
|
-
// Re-export ScrollRestoration (can be used in server components)
|
|
36
31
|
export {
|
|
37
32
|
ScrollRestoration,
|
|
38
33
|
type ScrollRestorationProps,
|
|
39
34
|
} from "./browser/react/ScrollRestoration.js";
|
|
40
35
|
|
|
41
|
-
// Re-export NavigationProvider (needed for setup)
|
|
42
36
|
export {
|
|
43
37
|
NavigationProvider,
|
|
44
38
|
type NavigationProviderProps,
|
|
45
39
|
} from "./browser/react/NavigationProvider.js";
|
|
46
40
|
|
|
47
|
-
// Re-export href function (can be used in server components)
|
|
48
41
|
export { href } from "./href-client.js";
|
|
49
42
|
|
|
50
|
-
// Mount context re-exports (useMount is client-only, but MountContext can be referenced)
|
|
51
43
|
export { MountContext } from "./browser/react/mount-context.js";
|
|
52
44
|
|
|
53
|
-
//
|
|
54
|
-
// because they use client-side state and should only be used in client components
|
|
45
|
+
// useNavigation and useAction are NOT re-exported here because they use client-side state
|
|
55
46
|
|
|
56
|
-
// Handle API - for accumulating data across route segments
|
|
57
|
-
// Works in both RSC and client contexts
|
|
58
47
|
export { createHandle, isHandle, type Handle } from "./handle.js";
|
|
59
48
|
|
|
60
|
-
// Built-in handles
|
|
61
|
-
// Meta handle works in RSC context
|
|
62
49
|
export { Meta } from "./handles/meta.js";
|
|
63
|
-
// MetaTags is a "use client" component that can be imported from RSC
|
|
64
50
|
export { MetaTags } from "./handles/MetaTags.js";
|
|
65
51
|
export type { MetaDescriptor, MetaDescriptorBase } from "./router/types.js";
|
|
66
|
-
// Breadcrumbs handle works in RSC context
|
|
67
52
|
export { Breadcrumbs, type BreadcrumbItem } from "./handles/breadcrumbs.js";
|
|
68
53
|
|
|
69
|
-
// Location state - createLocationState works in RSC (just creates definition)
|
|
70
|
-
// useLocationState is NOT exported here as it uses client hooks
|
|
71
54
|
export {
|
|
72
55
|
createLocationState,
|
|
73
56
|
type LocationStateDefinition,
|
|
@@ -75,14 +58,10 @@ export {
|
|
|
75
58
|
type LocationStateOptions,
|
|
76
59
|
} from "./browser/react/location-state-shared.js";
|
|
77
60
|
|
|
78
|
-
// Re-export useHref - it's a "use client" hook
|
|
79
61
|
export { useHref } from "./browser/react/use-href.js";
|
|
80
62
|
|
|
81
|
-
// Re-export useReverse - it's a "use client" hook
|
|
82
63
|
export { useReverse } from "./browser/react/use-reverse.js";
|
|
83
64
|
|
|
84
|
-
// Re-export useHandle - it's a "use client" hook
|
|
85
65
|
export { useHandle } from "./browser/react/use-handle.js";
|
|
86
66
|
|
|
87
|
-
// Re-export useLocationState - it's a "use client" hook
|
|
88
67
|
export { useLocationState } from "./browser/react/location-state.js";
|
package/src/client.tsx
CHANGED
|
@@ -111,6 +111,11 @@ function useSlotSegment(
|
|
|
111
111
|
* the parallel segment with that slot name instead of the default content.
|
|
112
112
|
* This is used for parallel routes and intercepting routes.
|
|
113
113
|
*
|
|
114
|
+
* For a named slot, `<Outlet name="@x" />` is equivalent to
|
|
115
|
+
* `<ParallelOutlet name="@x" />` — both run the same resolution + wrapping
|
|
116
|
+
* pipeline. Convention: use bare `<Outlet />` for default content and
|
|
117
|
+
* `<ParallelOutlet name="@x" />` for named slots.
|
|
118
|
+
*
|
|
114
119
|
* @param name - Optional slot name for parallel/intercept content (must start with @)
|
|
115
120
|
*
|
|
116
121
|
* @example
|
|
@@ -163,6 +168,9 @@ export function Outlet({ name }: { name?: `@${string}` } = {}): ReactNode {
|
|
|
163
168
|
* is wrapped in Suspense with the loading component as fallback.
|
|
164
169
|
* This enables streaming and navigation loading states for parallels.
|
|
165
170
|
*
|
|
171
|
+
* Equivalent to `<Outlet name="@x" />` for a named slot; ParallelOutlet
|
|
172
|
+
* requires `name` and is named-slot-only, which reads clearer at the call site.
|
|
173
|
+
*
|
|
166
174
|
* @param name - The slot name (must start with @, e.g., "@modal", "@sidebar")
|
|
167
175
|
*
|
|
168
176
|
* @example
|
|
@@ -186,10 +194,9 @@ export function ParallelOutlet({ name }: { name: `@${string}` }): ReactNode {
|
|
|
186
194
|
}
|
|
187
195
|
|
|
188
196
|
// OutletProvider is defined in outlet-provider.tsx to break a circular
|
|
189
|
-
// dependency between client.tsx and route-content-wrapper.tsx.
|
|
190
|
-
//
|
|
191
|
-
// and
|
|
192
|
-
export { OutletProvider };
|
|
197
|
+
// dependency between client.tsx and route-content-wrapper.tsx. It is imported
|
|
198
|
+
// at the top of this file for local use in Outlet/ParallelOutlet only; it is an
|
|
199
|
+
// internal component and is intentionally not part of the public ./client API.
|
|
193
200
|
|
|
194
201
|
/**
|
|
195
202
|
* Hook to access outlet content programmatically
|
|
@@ -210,7 +217,6 @@ export function useOutlet(): ReactNode {
|
|
|
210
217
|
return context?.content ?? null;
|
|
211
218
|
}
|
|
212
219
|
|
|
213
|
-
// Loader hooks - re-exported from dedicated file
|
|
214
220
|
export {
|
|
215
221
|
useLoader,
|
|
216
222
|
useFetchLoader,
|
|
@@ -329,12 +335,6 @@ export class ErrorBoundary extends Component<
|
|
|
329
335
|
}
|
|
330
336
|
}
|
|
331
337
|
|
|
332
|
-
// ============================================================================
|
|
333
|
-
// Re-exports from browser/react for convenience
|
|
334
|
-
// These are the most commonly used client-side navigation utilities
|
|
335
|
-
// ============================================================================
|
|
336
|
-
|
|
337
|
-
// Navigation hooks
|
|
338
338
|
export { useNavigation } from "./browser/react/use-navigation.js";
|
|
339
339
|
export { useRouter } from "./browser/react/use-router.js";
|
|
340
340
|
export { usePathname } from "./browser/react/use-pathname.js";
|
|
@@ -346,62 +346,48 @@ export type {
|
|
|
346
346
|
ReadonlyURLSearchParams,
|
|
347
347
|
} from "./browser/types.js";
|
|
348
348
|
|
|
349
|
-
// Action state tracking hook
|
|
350
349
|
export {
|
|
351
350
|
useAction,
|
|
352
351
|
type ServerActionFunction,
|
|
353
352
|
} from "./browser/react/use-action.js";
|
|
354
353
|
|
|
355
|
-
// Segments state hook
|
|
356
354
|
export {
|
|
357
355
|
useSegments,
|
|
358
356
|
type SegmentsState,
|
|
359
357
|
} from "./browser/react/use-segments.js";
|
|
360
358
|
|
|
361
|
-
// Client cache controls hook
|
|
362
|
-
export {
|
|
363
|
-
useClientCache,
|
|
364
|
-
type ClientCacheControls,
|
|
365
|
-
} from "./browser/react/use-client-cache.js";
|
|
366
|
-
|
|
367
|
-
// Provider
|
|
368
359
|
export {
|
|
369
360
|
NavigationProvider,
|
|
370
361
|
type NavigationProviderProps,
|
|
371
362
|
} from "./browser/react/NavigationProvider.js";
|
|
372
363
|
|
|
373
|
-
// Link component
|
|
374
364
|
export {
|
|
375
365
|
Link,
|
|
376
366
|
type LinkProps,
|
|
377
367
|
type PrefetchStrategy,
|
|
378
368
|
type StateOrGetter,
|
|
369
|
+
type LinkState,
|
|
379
370
|
} from "./browser/react/Link.js";
|
|
380
371
|
|
|
381
|
-
// Link status hook
|
|
382
372
|
export {
|
|
383
373
|
useLinkStatus,
|
|
384
374
|
type LinkStatus,
|
|
385
375
|
} from "./browser/react/use-link-status.js";
|
|
386
376
|
|
|
387
|
-
// Scroll restoration
|
|
388
377
|
export {
|
|
389
378
|
ScrollRestoration,
|
|
390
379
|
useScrollRestoration,
|
|
391
380
|
type ScrollRestorationProps,
|
|
392
381
|
} from "./browser/react/ScrollRestoration.js";
|
|
393
382
|
|
|
394
|
-
// Handle data hook (client-side only — createHandle/isHandle are server APIs from the root export)
|
|
395
383
|
export { type Handle } from "./handle.js";
|
|
396
384
|
export { useHandle } from "./browser/react/use-handle.js";
|
|
397
385
|
|
|
398
|
-
// Built-in handles
|
|
399
386
|
export { Meta } from "./handles/meta.js";
|
|
400
387
|
export { MetaTags } from "./handles/MetaTags.js";
|
|
401
388
|
export type { MetaDescriptor, MetaDescriptorBase } from "./router/types.js";
|
|
402
389
|
export { Breadcrumbs, type BreadcrumbItem } from "./handles/breadcrumbs.js";
|
|
403
390
|
|
|
404
|
-
// Location state - type-safe navigation state
|
|
405
391
|
export {
|
|
406
392
|
createLocationState,
|
|
407
393
|
useLocationState,
|
|
@@ -410,29 +396,19 @@ export {
|
|
|
410
396
|
type LocationStateOptions,
|
|
411
397
|
} from "./browser/react/location-state.js";
|
|
412
398
|
|
|
413
|
-
//
|
|
414
|
-
// are ambient as `Rango.Path` / `Rango.PathResponse` (declared in
|
|
415
|
-
// href-client.ts) — no import needed.
|
|
399
|
+
// Ambient Rango.Path / Rango.PathResponse types (declared in href-client.ts)
|
|
416
400
|
export { href, type PatternToPath } from "./href-client.js";
|
|
417
401
|
|
|
418
|
-
//
|
|
419
|
-
// On a non-2xx response, `await res.json()` yields this shape; on success the
|
|
420
|
-
// body is the bare value (no envelope). Discriminate on `res.ok` / status.
|
|
402
|
+
// RFC 9457 error type for JSON response routes
|
|
421
403
|
export type { ProblemDetails } from "./urls.js";
|
|
422
404
|
|
|
423
|
-
// Mount context for include() scoped components
|
|
424
405
|
export { useMount } from "./browser/react/use-mount.js";
|
|
425
406
|
export { MountContext } from "./browser/react/mount-context.js";
|
|
426
407
|
|
|
427
|
-
// Mount-aware href hook - auto-prefixes paths with include() mount
|
|
428
408
|
export { useHref } from "./browser/react/use-href.js";
|
|
429
409
|
|
|
430
|
-
// Mount-aware reverse hook - resolves dot-prefixed names against an imported
|
|
431
|
-
// generated routes map (from a urls() module's .gen.ts).
|
|
432
410
|
export { useReverse } from "./browser/react/use-reverse.js";
|
|
433
411
|
|
|
434
|
-
// Type-safe scoped reverse function for scopedReverse<typeof patterns>()
|
|
435
412
|
export type { ScopedReverseFunction, LocalReverseFunction } from "./reverse.js";
|
|
436
413
|
|
|
437
|
-
// Loader definition type - for typing loader props in client components
|
|
438
414
|
export type { LoaderDefinition } from "./types.js";
|
package/src/component-utils.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { ComponentType } from "react";
|
|
8
|
+
import { isUnderTestRunner } from "./runtime-env.js";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Symbol used by React to mark client component references.
|
|
@@ -48,11 +49,21 @@ export function isClientComponent(
|
|
|
48
49
|
*
|
|
49
50
|
* @param component - The component to check
|
|
50
51
|
* @param name - Name to use in error message (e.g., "document")
|
|
52
|
+
* @param opts.allowServerInTest - When true AND running under a test runner
|
|
53
|
+
* (`isUnderTestRunner()`), relax ONLY the "use client" requirement: a server
|
|
54
|
+
* component is accepted. The plugin's "use client" transform does not run in a
|
|
55
|
+
* bare unit test, so a real exported `document` (almost every app sets one) has
|
|
56
|
+
* no client marker and would otherwise throw at `createRouter`, blocking
|
|
57
|
+
* `dispatch`/`assertGeneratedRoutesMatch` against the real router. The document
|
|
58
|
+
* reference is irrelevant to those (no Flight render). The "not a JSX element"
|
|
59
|
+
* guard still fires, and a real dev/build still throws (mirrors the runtime
|
|
60
|
+
* fallback-id gating in handle.ts/loader.ts).
|
|
51
61
|
* @throws Error if the component is not a client component
|
|
52
62
|
*/
|
|
53
63
|
export function assertClientComponent(
|
|
54
64
|
component: ComponentType<unknown> | unknown,
|
|
55
65
|
name: string,
|
|
66
|
+
opts?: { allowServerInTest?: boolean },
|
|
56
67
|
): asserts component is ComponentType<unknown> {
|
|
57
68
|
if (typeof component !== "function") {
|
|
58
69
|
throw new Error(
|
|
@@ -62,6 +73,14 @@ export function assertClientComponent(
|
|
|
62
73
|
);
|
|
63
74
|
}
|
|
64
75
|
|
|
76
|
+
// Under a test runner the "use client" transform did not run, so a real
|
|
77
|
+
// server-rendered `document` has no client marker; accept it (the reference is
|
|
78
|
+
// never serialized in dispatch/route-map checks). Outside a test runner this
|
|
79
|
+
// still throws — the build-time safety net is preserved.
|
|
80
|
+
if (opts?.allowServerInTest && isUnderTestRunner()) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
65
84
|
if (!isClientComponent(component)) {
|
|
66
85
|
throw new Error(
|
|
67
86
|
`${name} must be a client component with "use client" directive at the top of the file. ` +
|
package/src/deps/ssr.ts
CHANGED
package/src/handle.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { missingInjectedIdError } from "./missing-id-error.js";
|
|
2
|
+
import { isUnderTestRunner } from "./runtime-env.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Handle definition for accumulating data across route segments.
|
|
@@ -45,10 +46,10 @@ function defaultCollect<T>(segments: T[][]): T[] {
|
|
|
45
46
|
// Used by useHandle() to recover collect when handle is deserialized from RSC prop.
|
|
46
47
|
const collectRegistry = new Map<string, (segments: unknown[][]) => unknown>();
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
// Monotonic counter for runtime fallback ids (see createHandle). Only used
|
|
50
|
+
// when no build id was injected (a bare unit test).
|
|
51
|
+
let runtimeHandleIdCounter = 0;
|
|
52
|
+
|
|
52
53
|
export function getCollectFn(
|
|
53
54
|
id: string,
|
|
54
55
|
): ((segments: unknown[][]) => unknown) | undefined {
|
|
@@ -95,24 +96,36 @@ export function createHandle<TData, TAccumulated = TData[]>(
|
|
|
95
96
|
collect?: (segments: TData[][]) => TAccumulated,
|
|
96
97
|
__injectedId?: string,
|
|
97
98
|
): Handle<TData, TAccumulated> {
|
|
98
|
-
|
|
99
|
+
let handleId = __injectedId ?? "";
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
// No build-injected id. Under a test runner: fall back to a synthetic id so the
|
|
102
|
+
// collect registers below and the handle is exercisable in tests (useHandle,
|
|
103
|
+
// collectHandle, renderRoute's `handles` run the REAL collect). Otherwise (dev
|
|
104
|
+
// or a real build) it means an UNSUPPORTED handler shape the plugin skipped —
|
|
105
|
+
// fail loud. The rich, stack-parsing diagnostic stays behind the NODE_ENV check
|
|
106
|
+
// so a production build folds it away and tree-shakes missing-id-error.ts out,
|
|
107
|
+
// shipping the small throw instead. isUnderTestRunner() is runtime-safe.
|
|
108
|
+
if (!handleId) {
|
|
109
|
+
if (isUnderTestRunner()) {
|
|
110
|
+
handleId = `__rango_runtime_handle_${runtimeHandleIdCounter++}`;
|
|
111
|
+
} else if (process.env.NODE_ENV !== "production") {
|
|
112
|
+
throw missingInjectedIdError("Handle", "createHandle");
|
|
113
|
+
} else {
|
|
114
|
+
throw new Error(
|
|
115
|
+
"[rango] Handle is missing $$id — the build plugin did not inject one. " +
|
|
116
|
+
"Export it as `export const X = createHandle(...)`.",
|
|
117
|
+
);
|
|
118
|
+
}
|
|
102
119
|
}
|
|
103
120
|
|
|
104
121
|
const collectFn =
|
|
105
122
|
collect ??
|
|
106
123
|
(defaultCollect as unknown as (segments: TData[][]) => TAccumulated);
|
|
107
124
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
handleId,
|
|
113
|
-
collectFn as (segments: unknown[][]) => unknown,
|
|
114
|
-
);
|
|
115
|
-
}
|
|
125
|
+
collectRegistry.set(
|
|
126
|
+
handleId,
|
|
127
|
+
collectFn as (segments: unknown[][]) => unknown,
|
|
128
|
+
);
|
|
116
129
|
|
|
117
130
|
return {
|
|
118
131
|
__brand: "handle" as const,
|
|
@@ -120,9 +133,6 @@ export function createHandle<TData, TAccumulated = TData[]>(
|
|
|
120
133
|
};
|
|
121
134
|
}
|
|
122
135
|
|
|
123
|
-
/**
|
|
124
|
-
* Type guard to check if a value is a Handle.
|
|
125
|
-
*/
|
|
126
136
|
export function isHandle(value: unknown): value is Handle<unknown, unknown> {
|
|
127
137
|
return (
|
|
128
138
|
typeof value === "object" &&
|