@rangojs/router 0.24.4 → 0.25.0
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 +1 -0
- package/dist/bin/rango.js +363 -39
- package/dist/testing/vitest.js +37 -4
- package/dist/types/browser/event-controller.d.ts +24 -5
- package/dist/types/browser/intercept-utils.d.ts +5 -2
- package/dist/types/browser/merge-segment-loaders.d.ts +0 -23
- package/dist/types/browser/navigation-transaction.d.ts +5 -5
- package/dist/types/browser/partial-update.d.ts +21 -13
- package/dist/types/browser/prefetch/cache.d.ts +8 -0
- package/dist/types/browser/rango-marker.d.ts +7 -0
- package/dist/types/browser/react/filter-segment-order.d.ts +10 -0
- package/dist/types/browser/react/use-handle.d.ts +20 -0
- package/dist/types/browser/react/use-href.d.ts +6 -0
- package/dist/types/browser/react/use-router.d.ts +6 -1
- package/dist/types/browser/response-adapter.d.ts +5 -3
- package/dist/types/browser/scroll-restoration.d.ts +9 -3
- package/dist/types/browser/segment-reconciler.d.ts +10 -13
- package/dist/types/browser/types.d.ts +38 -8
- package/dist/types/build/route-types/per-module-writer.d.ts +12 -0
- package/dist/types/cache/cf/cf-shell-frame.d.ts +2 -0
- package/dist/types/cache/types.d.ts +8 -0
- package/dist/types/client-urls/client-urls.d.ts +6 -2
- package/dist/types/client-urls/revalidate-registry.d.ts +1 -1
- package/dist/types/client-urls/server-projection.d.ts +9 -5
- package/dist/types/client-urls/types.d.ts +29 -16
- package/dist/types/client.d.ts +2 -2
- package/dist/types/client.rsc.d.ts +2 -2
- package/dist/types/decode-loader-results.d.ts +28 -7
- package/dist/types/href-client.d.ts +3 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/loader-redirect.d.ts +2 -3
- package/dist/types/outlet-context.d.ts +7 -6
- package/dist/types/prefetch-markers.d.ts +14 -5
- package/dist/types/route-content-wrapper.d.ts +17 -14
- package/dist/types/route-definition/helpers-types.d.ts +7 -2
- package/dist/types/route-definition/resolve-handler-use.d.ts +6 -1
- package/dist/types/route-definition/rewrite.d.ts +48 -5
- package/dist/types/route-types.d.ts +2 -2
- package/dist/types/router/handler-context.d.ts +14 -2
- package/dist/types/router/intercept-resolution.d.ts +19 -46
- package/dist/types/router/lazy-includes.d.ts +7 -0
- package/dist/types/router/match-middleware/intercept-resolution.d.ts +2 -2
- package/dist/types/router/middleware.d.ts +0 -16
- package/dist/types/router/navigation-snapshot.d.ts +2 -0
- package/dist/types/router/prefetch-cache-ttl.d.ts +8 -11
- package/dist/types/router/revalidation.d.ts +8 -1
- package/dist/types/router/router-context.d.ts +4 -16
- package/dist/types/router/router-interfaces.d.ts +0 -6
- package/dist/types/router/router-options.d.ts +4 -7
- package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +31 -8
- package/dist/types/router/segment-resolution/revalidation.d.ts +38 -4
- package/dist/types/router/segment-resolution.d.ts +1 -1
- package/dist/types/router/segment-wrappers.d.ts +3 -5
- package/dist/types/router/substitute-pattern-params.d.ts +3 -2
- package/dist/types/router/tracing.d.ts +2 -2
- package/dist/types/rsc/full-payload.d.ts +14 -2
- package/dist/types/rsc/helpers.d.ts +8 -0
- package/dist/types/rsc/manifest-init.d.ts +20 -0
- package/dist/types/rsc/prefetch-render-error.d.ts +22 -0
- package/dist/types/rsc/progressive-enhancement.d.ts +20 -0
- package/dist/types/rsc/rewrite-passes.d.ts +12 -2
- package/dist/types/rsc/server-action.d.ts +6 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +10 -1
- package/dist/types/rsc/shell-capture.d.ts +7 -0
- package/dist/types/rsc/types.d.ts +5 -0
- package/dist/types/segment-system.d.ts +15 -0
- package/dist/types/server/context.d.ts +64 -4
- package/dist/types/server/request-context.d.ts +41 -4
- package/dist/types/server/request-headers.d.ts +12 -0
- package/dist/types/server/rewrite-trail.d.ts +8 -1
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +1 -1
- package/dist/types/testing/index.d.ts +1 -1
- package/dist/types/testing/internal/client-urls-projection.d.ts +4 -3
- package/dist/types/testing/render-route.d.ts +21 -11
- package/dist/types/testing/run-loader.d.ts +7 -0
- package/dist/types/testing/run-middleware.d.ts +7 -0
- package/dist/types/testing/run-revalidate.d.ts +5 -4
- package/dist/types/testing/serve-shell-request.d.ts +41 -4
- package/dist/types/testing/shell-status.d.ts +17 -0
- package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
- package/dist/types/testing/vitest.d.ts +10 -1
- package/dist/types/thenable-status.d.ts +0 -6
- package/dist/types/types/handler-context.d.ts +12 -1
- package/dist/types/types/loader-types.d.ts +4 -3
- package/dist/types/types/route-entry.d.ts +2 -1
- package/dist/types/urls/path-helper-types.d.ts +4 -2
- package/dist/types/vite/discovery/client-urls-projection.d.ts +55 -5
- package/dist/types/vite/discovery/state.d.ts +20 -0
- package/dist/types/vite/plugins/client-urls-revalidate-hoist.d.ts +28 -16
- package/dist/types/vite/plugins/hoist/source.d.ts +8 -7
- package/dist/types/vite/plugins/virtual-entries.d.ts +6 -0
- package/dist/types/vite/utils/prerender-utils.d.ts +7 -0
- package/dist/vite/index.js +6816 -6547
- package/package.json +1 -1
- package/skills/app-structure/SKILL.md +255 -0
- package/skills/app-structure/content-site.md +333 -0
- package/skills/app-structure/e-commerce.md +435 -0
- package/skills/app-structure/large-app.md +885 -0
- package/skills/app-structure/multi-app.md +280 -0
- package/skills/breadcrumbs/SKILL.md +6 -1
- package/skills/caching/SKILL.md +3 -1
- package/skills/catalog.json +12 -0
- package/skills/client-urls/SKILL.md +185 -32
- package/skills/composability/SKILL.md +10 -8
- package/skills/handler-use/SKILL.md +48 -9
- package/skills/hooks/handle-and-actions.md +104 -0
- package/skills/hooks/navigation.md +24 -10
- package/skills/hooks/urls.md +1 -1
- package/skills/i18n/SKILL.md +2 -1
- package/skills/intercept/SKILL.md +60 -29
- package/skills/layout/SKILL.md +6 -3
- package/skills/links/SKILL.md +151 -17
- package/skills/loader/SKILL.md +32 -10
- package/skills/middleware/SKILL.md +111 -15
- package/skills/observability/SKILL.md +11 -0
- package/skills/parallel/SKILL.md +5 -5
- package/skills/ppr/SKILL.md +43 -8
- package/skills/prerender/SKILL.md +1 -2
- package/skills/rango/SKILL.md +3 -0
- package/skills/response-routes/SKILL.md +4 -1
- package/skills/scripts/SKILL.md +12 -1
- package/skills/server-actions/SKILL.md +60 -20
- package/skills/testing/SKILL.md +1 -0
- package/skills/testing/client-components.md +13 -10
- package/skills/testing/loader.md +25 -24
- package/skills/testing/middleware.md +11 -11
- package/skills/testing/response-routes.md +4 -1
- package/skills/testing/serve-shell-request.md +85 -21
- package/skills/ux-architecture/SKILL.md +980 -0
- package/skills/view-transitions/SKILL.md +43 -10
- package/src/__augment-tests__/augmented.check.ts +28 -0
- package/src/browser/event-controller.ts +44 -18
- package/src/browser/intercept-utils.ts +3 -2
- package/src/browser/merge-segment-loaders.ts +0 -85
- package/src/browser/navigation-bridge.ts +112 -84
- package/src/browser/navigation-client.ts +20 -15
- package/src/browser/navigation-store.ts +4 -2
- package/src/browser/navigation-transaction.ts +9 -8
- package/src/browser/partial-update.ts +90 -32
- package/src/browser/prefetch/cache.ts +8 -0
- package/src/browser/prefetch/fetch.ts +13 -3
- package/src/browser/rango-marker.ts +13 -0
- package/src/browser/react/NavigationProvider.tsx +25 -7
- package/src/browser/react/filter-segment-order.ts +13 -0
- package/src/browser/react/use-action.ts +9 -15
- package/src/browser/react/use-handle.ts +63 -71
- package/src/browser/react/use-href.tsx +24 -5
- package/src/browser/react/use-link-status.ts +1 -1
- package/src/browser/react/use-navigation.ts +30 -40
- package/src/browser/react/use-router.ts +37 -6
- package/src/browser/react/use-segments.ts +8 -12
- package/src/browser/response-adapter.ts +11 -5
- package/src/browser/rsc-router.tsx +3 -1
- package/src/browser/scroll-restoration.ts +34 -20
- package/src/browser/segment-reconciler.ts +11 -29
- package/src/browser/server-action-bridge.ts +52 -16
- package/src/browser/types.ts +40 -8
- package/src/build/generate-manifest.ts +12 -1
- package/src/build/route-types/codegen.ts +6 -2
- package/src/build/route-types/per-module-writer.ts +94 -61
- package/src/cache/cache-runtime.ts +9 -0
- package/src/cache/cache-scope.ts +2 -1
- package/src/cache/cf/cf-cache-store.ts +2 -0
- package/src/cache/cf/cf-shell-frame.ts +3 -0
- package/src/cache/types.ts +8 -0
- package/src/cache/vercel/vercel-cache-store.ts +6 -1
- package/src/client-urls/client-urls.ts +202 -153
- package/src/client-urls/revalidate-registry.ts +12 -3
- package/src/client-urls/server-projection.ts +137 -66
- package/src/client-urls/types.ts +30 -15
- package/src/client.rsc.tsx +11 -1
- package/src/client.tsx +9 -1
- package/src/decode-loader-results.ts +64 -31
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +1 -0
- package/src/index.ts +12 -4
- package/src/loader-redirect.tsx +2 -3
- package/src/outlet-context.ts +7 -6
- package/src/prefetch-markers.ts +15 -5
- package/src/reverse.ts +3 -3
- package/src/route-content-wrapper.tsx +19 -17
- package/src/route-definition/dsl-helpers.ts +27 -7
- package/src/route-definition/helpers-types.ts +7 -2
- package/src/route-definition/resolve-handler-use.ts +23 -10
- package/src/route-definition/rewrite.ts +86 -19
- package/src/route-types.ts +1 -2
- package/src/router/find-match.ts +37 -1
- package/src/router/handler-context.ts +85 -40
- package/src/router/intercept-resolution.ts +161 -307
- package/src/router/lazy-includes.ts +43 -27
- package/src/router/loader-resolution.ts +21 -1
- package/src/router/manifest.ts +6 -8
- package/src/router/match-api.ts +11 -1
- package/src/router/match-middleware/background-revalidation.ts +2 -4
- package/src/router/match-middleware/cache-lookup.ts +16 -20
- package/src/router/match-middleware/intercept-resolution.ts +41 -60
- package/src/router/match-result.ts +31 -2
- package/src/router/middleware.ts +6 -147
- package/src/router/navigation-snapshot.ts +38 -2
- package/src/router/prefetch-cache-ttl.ts +10 -18
- package/src/router/prerender-match.ts +2 -1
- package/src/router/revalidation.ts +24 -3
- package/src/router/router-context.ts +5 -18
- package/src/router/router-interfaces.ts +0 -7
- package/src/router/router-options.ts +4 -7
- package/src/router/segment-resolution/fresh.ts +6 -2
- package/src/router/segment-resolution/helpers.ts +7 -1
- package/src/router/segment-resolution/prefetch-deferral.ts +62 -12
- package/src/router/segment-resolution/revalidation.ts +75 -35
- package/src/router/segment-resolution.ts +0 -1
- package/src/router/segment-wrappers.ts +8 -13
- package/src/router/substitute-pattern-params.ts +3 -2
- package/src/router/tracing.ts +2 -2
- package/src/router.ts +2 -9
- package/src/rsc/full-payload.ts +26 -4
- package/src/rsc/handler.ts +83 -41
- package/src/rsc/helpers.ts +13 -0
- package/src/rsc/manifest-init.ts +56 -3
- package/src/rsc/prefetch-render-error.ts +38 -0
- package/src/rsc/progressive-enhancement.ts +161 -211
- package/src/rsc/rewrite-passes.ts +56 -9
- package/src/rsc/rsc-rendering.ts +79 -23
- package/src/rsc/server-action.ts +25 -2
- package/src/rsc/shell-capture-constants.ts +22 -0
- package/src/rsc/shell-capture.ts +11 -0
- package/src/rsc/types.ts +5 -0
- package/src/segment-system.tsx +125 -119
- package/src/server/context.ts +96 -5
- package/src/server/handle-store.ts +3 -2
- package/src/server/request-context.ts +42 -3
- package/src/server/request-headers.ts +32 -0
- package/src/server/rewrite-trail.ts +55 -9
- package/src/ssr/ssr-root.tsx +6 -10
- package/src/testing/dispatch.ts +4 -5
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/flight.entry.ts +1 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/internal/client-urls-projection.ts +31 -11
- package/src/testing/render-route.tsx +83 -40
- package/src/testing/run-loader.ts +26 -2
- package/src/testing/run-middleware.ts +10 -0
- package/src/testing/run-revalidate.ts +13 -6
- package/src/testing/serve-shell-request.ts +77 -4
- package/src/testing/shell-status.ts +40 -0
- package/src/testing/vitest-stubs/version.ts +1 -0
- package/src/testing/vitest.ts +74 -7
- package/src/thenable-status.ts +0 -11
- package/src/types/handler-context.ts +12 -1
- package/src/types/loader-types.ts +4 -3
- package/src/types/route-entry.ts +2 -1
- package/src/urls/include-helper.ts +38 -11
- package/src/urls/path-helper-types.ts +4 -2
- package/src/vite/discovery/client-urls-projection.ts +205 -16
- package/src/vite/discovery/discover-routers.ts +2 -2
- package/src/vite/discovery/prerender-collection.ts +2 -6
- package/src/vite/discovery/state.ts +26 -0
- package/src/vite/plugins/client-urls-revalidate-hoist.ts +190 -77
- package/src/vite/plugins/expose-internal-ids.ts +46 -40
- package/src/vite/plugins/hoist/source.ts +8 -7
- package/src/vite/plugins/transition-when-hoist.ts +1 -0
- package/src/vite/plugins/version-plugin.ts +7 -0
- package/src/vite/plugins/version.d.ts +7 -0
- package/src/vite/plugins/virtual-entries.ts +20 -0
- package/src/vite/rango.ts +4 -1
- package/src/vite/router-discovery.ts +39 -7
- package/src/vite/utils/prerender-utils.ts +17 -0
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
import type { PathHelpers } from "../urls/path-helper-types.js";
|
|
22
22
|
import type { AllUseItems } from "../route-types.js";
|
|
23
23
|
import { urls } from "../urls/urls-function.js";
|
|
24
|
+
import { toShouldRevalidateFn } from "../router/revalidation.js";
|
|
24
25
|
import { isHoistedRevalidateRef } from "./hoisted-revalidate.js";
|
|
25
26
|
import { getClientUrlRevalidator } from "./revalidate-registry.js";
|
|
26
27
|
import { setLayoutLoaderCopies } from "./layout-loader-copies.js";
|
|
@@ -112,11 +113,11 @@ export interface ClientUrlProjectionRoute {
|
|
|
112
113
|
* loader(), so a prefetch of the route defers them. Absent (= none) in
|
|
113
114
|
* projections serialized before the option existed. */
|
|
114
115
|
readonly deferrableLoaderIndices?: readonly number[];
|
|
115
|
-
/** Per loader (aligned with loaderIds),
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* loader declares one. */
|
|
119
|
-
readonly loaderRevalidate?: readonly (readonly string[])[];
|
|
116
|
+
/** Per loader (aligned with loaderIds), its revalidate() arguments in
|
|
117
|
+
* declaration order: a hoisted predicate's key, whose registration
|
|
118
|
+
* materialization attaches (revalidate-registry.ts), or a boolean
|
|
119
|
+
* literal. Absent when no loader declares one. */
|
|
120
|
+
readonly loaderRevalidate?: readonly (readonly (string | boolean)[])[];
|
|
120
121
|
/** One per leading loader its layout()s declare
|
|
121
122
|
* (ClientUrlRouteRecord.layoutLoaders): the record's ordinal in the
|
|
122
123
|
* definition. Two routes whose loader at an index carries the same ordinal
|
|
@@ -137,6 +138,10 @@ export interface ClientUrlProjectionIntercept {
|
|
|
137
138
|
readonly targetName: string;
|
|
138
139
|
readonly loaderIds: readonly string[];
|
|
139
140
|
readonly hasLoading: boolean;
|
|
141
|
+
/** As on a route (ClientUrlProjectionRoute.deferrableLoaderIndices). */
|
|
142
|
+
readonly deferrableLoaderIndices?: readonly number[];
|
|
143
|
+
/** As on a route (ClientUrlProjectionRoute.loaderRevalidate). */
|
|
144
|
+
readonly loaderRevalidate?: readonly (readonly (string | boolean)[])[];
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
export interface ClientUrlProjection {
|
|
@@ -332,29 +337,10 @@ function serializeRoute(
|
|
|
332
337
|
return loader.$$id;
|
|
333
338
|
});
|
|
334
339
|
|
|
335
|
-
const loaderRevalidate = route.loaders.map(({ loader, revalidate }) =>
|
|
336
|
-
revalidate.flatMap((entry) => {
|
|
337
|
-
if (isHoistedRevalidateRef(entry)) return [entry.$$rangoRevalidate];
|
|
338
|
-
if (options.hoisted) {
|
|
339
|
-
throw new Error(
|
|
340
|
-
`clientUrls() revalidate() on loader "${loader.$$id}" of route ` +
|
|
341
|
-
`"${route.pattern}" was not hoisted to the server: call the ` +
|
|
342
|
-
"builder's revalidate() helper with an inline function or an " +
|
|
343
|
-
"imported predicate.",
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
return [];
|
|
347
|
-
}),
|
|
348
|
-
);
|
|
349
|
-
|
|
350
340
|
const awaitedLoaderIndices = indicesWhere(
|
|
351
341
|
route.loaders,
|
|
352
342
|
({ ssr }) => ssr === false,
|
|
353
343
|
);
|
|
354
|
-
const deferrableLoaderIndices = indicesWhere(
|
|
355
|
-
route.loaders,
|
|
356
|
-
({ prefetch }) => prefetch === false,
|
|
357
|
-
);
|
|
358
344
|
|
|
359
345
|
const transition = serializeTransition(route);
|
|
360
346
|
return Object.freeze({
|
|
@@ -367,6 +353,55 @@ function serializeRoute(
|
|
|
367
353
|
...(awaitedLoaderIndices.length > 0
|
|
368
354
|
? { awaitedLoaderIndices: Object.freeze(awaitedLoaderIndices) }
|
|
369
355
|
: {}),
|
|
356
|
+
...serializeLoaderOptions(
|
|
357
|
+
route.loaders,
|
|
358
|
+
`route "${route.pattern}"`,
|
|
359
|
+
options,
|
|
360
|
+
),
|
|
361
|
+
...(layoutLoaders.length > 0
|
|
362
|
+
? { layoutLoaders: Object.freeze(layoutLoaders) }
|
|
363
|
+
: {}),
|
|
364
|
+
...(transition ? { transition } : {}),
|
|
365
|
+
...(typeof route.transition?.when === "function"
|
|
366
|
+
? { transitionWhen: true as const }
|
|
367
|
+
: {}),
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* The loader options a route and an intercept project alike: the `prefetch:
|
|
373
|
+
* false` indices and each loader's revalidate() keys and boolean literals.
|
|
374
|
+
* Under `options.hoisted` a predicate the build left in place fails loudly.
|
|
375
|
+
*/
|
|
376
|
+
function serializeLoaderOptions(
|
|
377
|
+
loaders: readonly ClientUrlLoaderRecord[],
|
|
378
|
+
owner: string,
|
|
379
|
+
options: SerializeClientUrlPatternsOptions,
|
|
380
|
+
): Pick<
|
|
381
|
+
ClientUrlProjectionRoute,
|
|
382
|
+
"deferrableLoaderIndices" | "loaderRevalidate"
|
|
383
|
+
> {
|
|
384
|
+
const loaderRevalidate = loaders.map(({ loader, revalidate }) =>
|
|
385
|
+
revalidate.flatMap((entry): (string | boolean)[] => {
|
|
386
|
+
if (isHoistedRevalidateRef(entry)) return [entry.$$rangoRevalidate];
|
|
387
|
+
if (typeof entry === "boolean") return [entry];
|
|
388
|
+
if (options.hoisted) {
|
|
389
|
+
throw new Error(
|
|
390
|
+
`clientUrls() revalidate() on loader "${loader.$$id}" of ${owner} ` +
|
|
391
|
+
"was not hoisted to the server: call revalidate() (the builder's " +
|
|
392
|
+
'helper, or the one "@rangojs/router/client" exports for a ' +
|
|
393
|
+
"component's .use) with an inline function, an imported " +
|
|
394
|
+
"predicate or a boolean.",
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
return [];
|
|
398
|
+
}),
|
|
399
|
+
);
|
|
400
|
+
const deferrableLoaderIndices = indicesWhere(
|
|
401
|
+
loaders,
|
|
402
|
+
({ prefetch }) => prefetch === false,
|
|
403
|
+
);
|
|
404
|
+
return {
|
|
370
405
|
...(deferrableLoaderIndices.length > 0
|
|
371
406
|
? { deferrableLoaderIndices: Object.freeze(deferrableLoaderIndices) }
|
|
372
407
|
: {}),
|
|
@@ -377,19 +412,13 @@ function serializeRoute(
|
|
|
377
412
|
),
|
|
378
413
|
}
|
|
379
414
|
: {}),
|
|
380
|
-
|
|
381
|
-
? { layoutLoaders: Object.freeze(layoutLoaders) }
|
|
382
|
-
: {}),
|
|
383
|
-
...(transition ? { transition } : {}),
|
|
384
|
-
...(typeof route.transition?.when === "function"
|
|
385
|
-
? { transitionWhen: true as const }
|
|
386
|
-
: {}),
|
|
387
|
-
});
|
|
415
|
+
};
|
|
388
416
|
}
|
|
389
417
|
|
|
390
418
|
function serializeIntercept(
|
|
391
419
|
record: ClientUrlInterceptRecord,
|
|
392
420
|
index: number,
|
|
421
|
+
options: SerializeClientUrlPatternsOptions,
|
|
393
422
|
): ClientUrlProjectionIntercept {
|
|
394
423
|
const loaderIds = record.loaders.map(({ loader }, loaderIndex) => {
|
|
395
424
|
if (typeof loader.$$id !== "string" || loader.$$id.length === 0) {
|
|
@@ -406,6 +435,11 @@ function serializeIntercept(
|
|
|
406
435
|
targetName: record.targetName,
|
|
407
436
|
loaderIds: Object.freeze(loaderIds),
|
|
408
437
|
hasLoading: record.loading !== undefined,
|
|
438
|
+
...serializeLoaderOptions(
|
|
439
|
+
record.loaders,
|
|
440
|
+
`intercept ".${record.targetName}"`,
|
|
441
|
+
options,
|
|
442
|
+
),
|
|
409
443
|
});
|
|
410
444
|
}
|
|
411
445
|
|
|
@@ -420,7 +454,9 @@ export function serializeClientUrlPatterns(
|
|
|
420
454
|
patterns.routes.map((route) => serializeRoute(route, options, ordinals)),
|
|
421
455
|
),
|
|
422
456
|
intercepts: Object.freeze(
|
|
423
|
-
(patterns.intercepts ?? []).map(
|
|
457
|
+
(patterns.intercepts ?? []).map((record, index) =>
|
|
458
|
+
serializeIntercept(record, index, options),
|
|
459
|
+
),
|
|
424
460
|
),
|
|
425
461
|
});
|
|
426
462
|
}
|
|
@@ -536,28 +572,48 @@ export function clearClientUrlProjections(): void {
|
|
|
536
572
|
clientUrlProjections.clear();
|
|
537
573
|
}
|
|
538
574
|
|
|
575
|
+
/** The projected loaders of a route or an intercept. */
|
|
576
|
+
interface ProjectedLoaders {
|
|
577
|
+
readonly projected: Pick<
|
|
578
|
+
ClientUrlProjectionRoute,
|
|
579
|
+
| "loaderIds"
|
|
580
|
+
| "loaderRevalidate"
|
|
581
|
+
| "awaitedLoaderIndices"
|
|
582
|
+
| "deferrableLoaderIndices"
|
|
583
|
+
>;
|
|
584
|
+
/** Names the owner in an error: `route "/a"`, `intercept ".b"`. */
|
|
585
|
+
readonly label: string;
|
|
586
|
+
/** The owner's loader records in a definition object. */
|
|
587
|
+
readonly inline: (
|
|
588
|
+
patterns: ClientUrlPatterns,
|
|
589
|
+
) => readonly ClientUrlLoaderRecord[] | undefined;
|
|
590
|
+
}
|
|
591
|
+
|
|
539
592
|
/**
|
|
540
|
-
* The revalidate() predicates of one materialized loader
|
|
541
|
-
* object (unit tests, the testing
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
* registration fails loudly here, at
|
|
593
|
+
* The revalidate() predicates of one materialized loader, a boolean as its
|
|
594
|
+
* constant predicate. A definition object (unit tests, the testing
|
|
595
|
+
* primitives) carries its functions. A client reference resolves the
|
|
596
|
+
* projection's content keys in the registry the group's hoisted module
|
|
597
|
+
* filled when it loaded; a key with no registration fails loudly here, at
|
|
598
|
+
* materialization, never at a request.
|
|
545
599
|
*/
|
|
546
600
|
function loaderRevalidators(
|
|
547
601
|
reference: ClientUrlDefinitionSource,
|
|
548
|
-
|
|
602
|
+
{ projected, label, inline }: ProjectedLoaders,
|
|
549
603
|
loaderIndex: number,
|
|
550
604
|
): ShouldRevalidateFn<any, any>[] {
|
|
551
605
|
const fromRegistry = (key: string): ShouldRevalidateFn<any, any> => {
|
|
552
606
|
if (!getClientUrlRevalidator(key)) {
|
|
553
607
|
const id = isClientUrlReference(reference) ? reference.$$id : "(inline)";
|
|
554
608
|
throw new Error(
|
|
555
|
-
`clientUrls() revalidate() on loader "${
|
|
556
|
-
`of
|
|
609
|
+
`clientUrls() revalidate() on loader "${projected.loaderIds[loaderIndex]}" ` +
|
|
610
|
+
`of ${label} in "${id}" has no server ` +
|
|
557
611
|
`registration (key "${key}"). The rango Vite plugin registers ` +
|
|
558
612
|
"hoisted predicates when the group's module loads in the RSC " +
|
|
559
|
-
"environment
|
|
560
|
-
"in
|
|
613
|
+
"environment, with those of the modules route discovery found " +
|
|
614
|
+
"its keys in (a component's .use). An argument that evaluated to " +
|
|
615
|
+
"neither a function nor a boolean there, or a transform that " +
|
|
616
|
+
"changed its syntax in one environment only, causes this.",
|
|
561
617
|
);
|
|
562
618
|
}
|
|
563
619
|
// Looked up per call: a dev re-evaluation of the hoisted module
|
|
@@ -565,14 +621,17 @@ function loaderRevalidators(
|
|
|
565
621
|
return (args) => getClientUrlRevalidator(key)!(args);
|
|
566
622
|
};
|
|
567
623
|
if (isClientUrlPatterns(reference)) {
|
|
568
|
-
|
|
569
|
-
return (record?.loaders[loaderIndex]?.revalidate ?? []).map((entry) =>
|
|
624
|
+
return (inline(reference)?.[loaderIndex]?.revalidate ?? []).map((entry) =>
|
|
570
625
|
isHoistedRevalidateRef(entry)
|
|
571
626
|
? fromRegistry(entry.$$rangoRevalidate)
|
|
572
|
-
: entry,
|
|
627
|
+
: toShouldRevalidateFn(entry),
|
|
573
628
|
);
|
|
574
629
|
}
|
|
575
|
-
return (
|
|
630
|
+
return (projected.loaderRevalidate?.[loaderIndex] ?? []).map((entry) =>
|
|
631
|
+
toShouldRevalidateFn(
|
|
632
|
+
typeof entry === "boolean" ? entry : fromRegistry(entry),
|
|
633
|
+
),
|
|
634
|
+
);
|
|
576
635
|
}
|
|
577
636
|
|
|
578
637
|
function createLoaderStub(id: string): LoaderDefinition<unknown> {
|
|
@@ -755,6 +814,26 @@ function materializeRouteItems(
|
|
|
755
814
|
return foreign.length ? { foreign } : {};
|
|
756
815
|
};
|
|
757
816
|
|
|
817
|
+
// The loader() items of a route's or an intercept's projected loaders: each
|
|
818
|
+
// stub with its `ssr: false` and `prefetch: false` flags and its
|
|
819
|
+
// revalidate() predicates.
|
|
820
|
+
const materializeLoaders = (owner: ProjectedLoaders) =>
|
|
821
|
+
owner.projected.loaderIds.map((id, loaderIndex) => {
|
|
822
|
+
const revalidators = loaderRevalidators(reference, owner, loaderIndex);
|
|
823
|
+
return loader(
|
|
824
|
+
createLoaderStub(id),
|
|
825
|
+
{
|
|
826
|
+
...(owner.projected.awaitedLoaderIndices?.includes(loaderIndex) && {
|
|
827
|
+
ssr: false,
|
|
828
|
+
}),
|
|
829
|
+
...(owner.projected.deferrableLoaderIndices?.includes(
|
|
830
|
+
loaderIndex,
|
|
831
|
+
) && { prefetch: false }),
|
|
832
|
+
},
|
|
833
|
+
() => revalidators.map((fn) => revalidate(fn)),
|
|
834
|
+
);
|
|
835
|
+
});
|
|
836
|
+
|
|
758
837
|
// Helper calls attach to the CURRENT ctx.parent as they execute, so these
|
|
759
838
|
// builders must run where the items belong: at the module top level for the
|
|
760
839
|
// flat (no-intercept) shape, or inside the group layout's children callback
|
|
@@ -803,24 +882,11 @@ function materializeRouteItems(
|
|
|
803
882
|
}),
|
|
804
883
|
materializedPathOptions(route, clientGroup),
|
|
805
884
|
recordingCopies(routeIndex, () => [
|
|
806
|
-
...
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
);
|
|
812
|
-
return loader(
|
|
813
|
-
createLoaderStub(id),
|
|
814
|
-
{
|
|
815
|
-
...(route.awaitedLoaderIndices?.includes(loaderIndex) && {
|
|
816
|
-
ssr: false,
|
|
817
|
-
}),
|
|
818
|
-
...(route.deferrableLoaderIndices?.includes(loaderIndex) && {
|
|
819
|
-
prefetch: false,
|
|
820
|
-
}),
|
|
821
|
-
},
|
|
822
|
-
() => revalidators.map((fn) => revalidate(fn)),
|
|
823
|
-
);
|
|
885
|
+
...materializeLoaders({
|
|
886
|
+
projected: route,
|
|
887
|
+
label: `route "${route.pattern}"`,
|
|
888
|
+
inline: (patterns) =>
|
|
889
|
+
patterns.routes.find(({ id }) => id === route.id)?.loaders,
|
|
824
890
|
}),
|
|
825
891
|
...(route.hasLoading
|
|
826
892
|
? [
|
|
@@ -907,7 +973,12 @@ function materializeRouteItems(
|
|
|
907
973
|
}),
|
|
908
974
|
{ when: isInGroupOrigin },
|
|
909
975
|
() => [
|
|
910
|
-
...
|
|
976
|
+
...materializeLoaders({
|
|
977
|
+
projected: record,
|
|
978
|
+
label: `intercept ".${record.targetName}"`,
|
|
979
|
+
inline: (patterns) =>
|
|
980
|
+
patterns.intercepts?.[interceptIndex]?.loaders,
|
|
981
|
+
}),
|
|
911
982
|
...(record.hasLoading
|
|
912
983
|
? [
|
|
913
984
|
loading(
|
package/src/client-urls/types.ts
CHANGED
|
@@ -28,6 +28,18 @@ export type ClientUrlItems = readonly ClientUrlItemInput[];
|
|
|
28
28
|
|
|
29
29
|
export type ClientUrlUse = () => ClientUrlItems;
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* A component path(), layout() or intercept() mounts. Its optional `use`
|
|
33
|
+
* carries its own items, as a server handler's `.use` does, built with the
|
|
34
|
+
* helpers `@rangojs/router/client` exports: they come before the mount
|
|
35
|
+
* site's explicit items (an explicit loading() or transition() wins, loaders
|
|
36
|
+
* accumulate). A page component may carry loader(), loading() and
|
|
37
|
+
* transition(); a layout or intercept component loader() and loading().
|
|
38
|
+
*/
|
|
39
|
+
export type ClientUrlComponent = ComponentType & {
|
|
40
|
+
readonly use?: ClientUrlUse;
|
|
41
|
+
};
|
|
42
|
+
|
|
31
43
|
export type ClientPathOptions<
|
|
32
44
|
TName extends string = string,
|
|
33
45
|
TSearch extends SearchSchema = SearchSchema,
|
|
@@ -42,13 +54,13 @@ export type ClientPathFn = <
|
|
|
42
54
|
const TSearch extends SearchSchema = {},
|
|
43
55
|
>(
|
|
44
56
|
pattern: TPattern,
|
|
45
|
-
component:
|
|
57
|
+
component: ClientUrlComponent,
|
|
46
58
|
optionsOrUse?: ClientPathOptions<TName, TSearch> | ClientUrlUse,
|
|
47
59
|
use?: ClientUrlUse,
|
|
48
60
|
) => ClientUrlItem & TypedRouteItem<TName, TPattern, unknown, TSearch>;
|
|
49
61
|
|
|
50
62
|
export type ClientLayoutFn = <const TItems extends ClientUrlItems>(
|
|
51
|
-
component:
|
|
63
|
+
component: ClientUrlComponent,
|
|
52
64
|
children: () => TItems,
|
|
53
65
|
) => ClientUrlItem & TypedLayoutItem<ExtractRoutes<TItems>>;
|
|
54
66
|
|
|
@@ -57,11 +69,13 @@ export interface ClientUrlLoaderRecord {
|
|
|
57
69
|
/**
|
|
58
70
|
* Per-loader revalidate() predicates, run on the server; empty = the
|
|
59
71
|
* router's defaults. Under the rango Vite plugin the browser and SSR copies
|
|
60
|
-
* hold the marker the build left in place of each hoisted predicate
|
|
72
|
+
* hold the marker the build left in place of each hoisted predicate; a
|
|
73
|
+
* boolean literal stays in place.
|
|
61
74
|
*/
|
|
62
75
|
readonly revalidate: readonly (
|
|
63
76
|
| ShouldRevalidateFn<any, DefaultEnv>
|
|
64
77
|
| HoistedRevalidateRef
|
|
78
|
+
| boolean
|
|
65
79
|
)[];
|
|
66
80
|
/**
|
|
67
81
|
* loader(Def, { ssr: false }): document renders await this loader before
|
|
@@ -71,11 +85,10 @@ export interface ClientUrlLoaderRecord {
|
|
|
71
85
|
*/
|
|
72
86
|
readonly ssr?: false;
|
|
73
87
|
/**
|
|
74
|
-
* loader(Def, { prefetch: false }): a prefetch of the route
|
|
75
|
-
*
|
|
76
|
-
* {@link LoaderOptions}).
|
|
77
|
-
*
|
|
78
|
-
* urls().
|
|
88
|
+
* loader(Def, { prefetch: false }): a prefetch of the route, or of an
|
|
89
|
+
* intercept's target from its source, defers this loader and the click
|
|
90
|
+
* that adopts it runs it in its fill request (see {@link LoaderOptions}).
|
|
91
|
+
* Projected onto the materialized stub.
|
|
79
92
|
*/
|
|
80
93
|
readonly prefetch?: false;
|
|
81
94
|
}
|
|
@@ -126,7 +139,7 @@ export interface ClientUrlRouteRecord {
|
|
|
126
139
|
|
|
127
140
|
/**
|
|
128
141
|
* A restricted intercept declared inside clientUrls(). Compared to the server
|
|
129
|
-
* intercept() there is no `when` selector,
|
|
142
|
+
* intercept() there is no `when` selector, and the target must
|
|
130
143
|
* be a dot-local NAMED route in the same definition — every field is
|
|
131
144
|
* JSON-projectable, which is what makes the declaration legal in a
|
|
132
145
|
* "use client" module. `targetName` is stored bare (no leading dot).
|
|
@@ -160,8 +173,8 @@ export interface ClientUrlHelpers {
|
|
|
160
173
|
* the destination at once, commits the adopted prefetch with the other
|
|
161
174
|
* loaders' data, and runs only the flagged loaders in its one fill
|
|
162
175
|
* request. A `layout()` loader with the flag is flagged on every route
|
|
163
|
-
* below it. Inside intercept() the
|
|
164
|
-
* urls().
|
|
176
|
+
* below it. Inside intercept() it defers the loader in a prefetch of the
|
|
177
|
+
* intercept from its source, as in urls().
|
|
165
178
|
*/
|
|
166
179
|
readonly loader: <TData>(
|
|
167
180
|
definition: LoaderDefinition<TData>,
|
|
@@ -176,10 +189,11 @@ export interface ClientUrlHelpers {
|
|
|
176
189
|
* names, ...): the build moves it out of this "use client" module. It may
|
|
177
190
|
* reference imports and globals only; a binding of this module, `this`,
|
|
178
191
|
* `import()`, a browser global, an `async` function or an import from a
|
|
179
|
-
* "use client" module fails the build with its file and line.
|
|
192
|
+
* "use client" module fails the build with its file and line. A boolean
|
|
193
|
+
* is shorthand for `() => true` / `() => false`.
|
|
180
194
|
*/
|
|
181
195
|
readonly revalidate: (
|
|
182
|
-
fn: ShouldRevalidateFn<any, DefaultEnv
|
|
196
|
+
fn: ShouldRevalidateFn<any, DefaultEnv> | boolean,
|
|
183
197
|
) => ClientUrlItem;
|
|
184
198
|
/**
|
|
185
199
|
* Declare an intercept for a named route in THIS definition. The target is
|
|
@@ -190,7 +204,7 @@ export interface ClientUrlHelpers {
|
|
|
190
204
|
readonly intercept: (
|
|
191
205
|
slotName: `@${string}`,
|
|
192
206
|
targetName: `.${string}`,
|
|
193
|
-
component:
|
|
207
|
+
component: ClientUrlComponent,
|
|
194
208
|
use?: ClientUrlUse,
|
|
195
209
|
) => ClientUrlItem;
|
|
196
210
|
/**
|
|
@@ -198,7 +212,8 @@ export interface ClientUrlHelpers {
|
|
|
198
212
|
* holds previous content instead of re-streaming the loading() fallback,
|
|
199
213
|
* and on experimental React the config's ViewTransition classes apply.
|
|
200
214
|
* Data-only — no `when` gate (server-executed; declare it in the server
|
|
201
|
-
* tree). Valid inside a path() use callback
|
|
215
|
+
* tree). Valid inside a path() use callback, or a page component's
|
|
216
|
+
* `.use`.
|
|
202
217
|
*/
|
|
203
218
|
readonly transition: (config: ClientTransitionConfig) => ClientUrlItem;
|
|
204
219
|
}
|
package/src/client.rsc.tsx
CHANGED
|
@@ -36,10 +36,20 @@ export {
|
|
|
36
36
|
|
|
37
37
|
export { createLoader } from "./route-definition.js";
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
// A module that is not "use client" and declares a component's `.use` can
|
|
40
|
+
// evaluate in the RSC realm too.
|
|
41
|
+
export {
|
|
42
|
+
clientUrls,
|
|
43
|
+
loader,
|
|
44
|
+
loading,
|
|
45
|
+
revalidate,
|
|
46
|
+
transition,
|
|
47
|
+
} from "./client-urls/client-urls.js";
|
|
40
48
|
export type {
|
|
49
|
+
ClientUrlComponent,
|
|
41
50
|
ClientUrlPatterns,
|
|
42
51
|
ClientUrlRouteRecord,
|
|
52
|
+
ClientUrlUse,
|
|
43
53
|
} from "./client-urls/client-urls.js";
|
|
44
54
|
|
|
45
55
|
// "use client" hooks the default ./client entry exports. They are client
|
package/src/client.tsx
CHANGED
|
@@ -309,10 +309,18 @@ export {
|
|
|
309
309
|
type UseLoaderOptions,
|
|
310
310
|
} from "./use-loader.js";
|
|
311
311
|
|
|
312
|
-
export {
|
|
312
|
+
export {
|
|
313
|
+
clientUrls,
|
|
314
|
+
loader,
|
|
315
|
+
loading,
|
|
316
|
+
revalidate,
|
|
317
|
+
transition,
|
|
318
|
+
} from "./client-urls/client-urls.js";
|
|
313
319
|
export type {
|
|
320
|
+
ClientUrlComponent,
|
|
314
321
|
ClientUrlPatterns,
|
|
315
322
|
ClientUrlRouteRecord,
|
|
323
|
+
ClientUrlUse,
|
|
316
324
|
} from "./client-urls/client-urls.js";
|
|
317
325
|
|
|
318
326
|
/**
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import { createElement } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
isLoaderDataResult,
|
|
5
|
+
type ErrorInfo,
|
|
6
|
+
type ResolvedSegment,
|
|
7
|
+
} from "./types.js";
|
|
8
|
+
import { isPlaceholder } from "./types/segments.js";
|
|
4
9
|
import { LoaderRedirect } from "./loader-redirect.js";
|
|
5
10
|
|
|
6
11
|
/**
|
|
@@ -15,13 +20,13 @@ import { LoaderRedirect } from "./loader-redirect.js";
|
|
|
15
20
|
* Only decodeLoaderEntry (the read-site path — client components, so the
|
|
16
21
|
* throw lands in the router-owned boundary; during document SSR, Fizz emits
|
|
17
22
|
* the Suspense fallback and replays the throw at hydration) throws these.
|
|
18
|
-
* decodeLoaderResults runs during the
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* decodeLoaderResults runs during the tree build on settled results, where a
|
|
24
|
+
* throw would collapse the whole payload — it routes signals through the
|
|
25
|
+
* errorFallback slot instead (same visual: the slot replaces the children
|
|
26
|
+
* under OutletProvider). The document lane's `{ ssr: false }` results take
|
|
27
|
+
* this route (segment-system buildLoaderStreams): they are settled before the
|
|
28
|
+
* tree is built, and the read-site throw would land in the Fizz shell with no
|
|
29
|
+
* boundary to absorb it.
|
|
25
30
|
*/
|
|
26
31
|
export const LOADER_NOT_FOUND_FALLBACK: unique symbol = Symbol(
|
|
27
32
|
"rango.loaderNotFound",
|
|
@@ -65,6 +70,52 @@ function signalFallback(result: any): ReactNode | undefined {
|
|
|
65
70
|
return undefined;
|
|
66
71
|
}
|
|
67
72
|
|
|
73
|
+
/**
|
|
74
|
+
* A failed loader entry's error, rebuilt with the ErrorInfo identity
|
|
75
|
+
* (name/stack/code/cause) instead of a stripped generic Error.
|
|
76
|
+
*/
|
|
77
|
+
export function loaderResultError(result: { error: ErrorInfo }): Error {
|
|
78
|
+
const info = result.error;
|
|
79
|
+
const err = new Error(
|
|
80
|
+
info.message,
|
|
81
|
+
info.cause !== undefined ? { cause: info.cause } : undefined,
|
|
82
|
+
);
|
|
83
|
+
if (info.name) err.name = info.name;
|
|
84
|
+
if (info.stack) err.stack = info.stack;
|
|
85
|
+
if (info.code !== undefined) (err as { code?: string }).code = info.code;
|
|
86
|
+
return err;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** A loader that threw: not ok, and not a notFound()/redirect() signal. */
|
|
90
|
+
export function isLoaderError(
|
|
91
|
+
result: unknown,
|
|
92
|
+
): result is { error: ErrorInfo; fallback: ReactNode | null } {
|
|
93
|
+
return (
|
|
94
|
+
isLoaderDataResult(result) &&
|
|
95
|
+
!result.ok &&
|
|
96
|
+
!result.notFound &&
|
|
97
|
+
!result.redirect
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The error of the first loader that threw while it re-ran in an action's
|
|
103
|
+
* revalidation, for useAction()'s `revalidationError`. The loader itself
|
|
104
|
+
* throws at its read (segment-system buildLoaderStreams), and the action
|
|
105
|
+
* resolves. A stale revalidation rejects with it instead and commits nothing
|
|
106
|
+
* (partial-update.ts). A notFound()/redirect() is a signal, not an error.
|
|
107
|
+
*/
|
|
108
|
+
export async function revalidationErrorOf(
|
|
109
|
+
segments: ResolvedSegment[] = [],
|
|
110
|
+
): Promise<Error | undefined> {
|
|
111
|
+
for (const s of segments) {
|
|
112
|
+
if (s.type !== "loader" || isPlaceholder(s)) continue;
|
|
113
|
+
const entry = await s.loaderData;
|
|
114
|
+
if (isLoaderError(entry)) return loaderResultError(entry);
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
68
119
|
// Shared by segment-system (server) and LoaderResolver (client) so the
|
|
69
120
|
// legacy/ok/error-fallback/throw decode of resolved loader values lives once.
|
|
70
121
|
// Last failing loader wins errorFallback; an error without a fallback throws.
|
|
@@ -90,10 +141,9 @@ export function decodeLoaderResults(
|
|
|
90
141
|
}
|
|
91
142
|
|
|
92
143
|
// Authority signals take the errorFallback slot (this decode runs during
|
|
93
|
-
// the
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
// mounts LoaderRedirect which navigates.
|
|
144
|
+
// the tree build — throwing here would collapse the payload). A signal
|
|
145
|
+
// outranks a plain error fallback and stops the scan: notFound renders the
|
|
146
|
+
// server-rendered 404 UI, redirect mounts LoaderRedirect which navigates.
|
|
97
147
|
const signal = signalFallback(result);
|
|
98
148
|
if (signal !== undefined) {
|
|
99
149
|
return { loaderData, errorFallback: signal };
|
|
@@ -108,17 +158,7 @@ export function decodeLoaderResults(
|
|
|
108
158
|
if (result.fallback != null) {
|
|
109
159
|
errorFallback = result.fallback;
|
|
110
160
|
} else {
|
|
111
|
-
|
|
112
|
-
// code/cause) instead of a stripped generic Error.
|
|
113
|
-
const info = result.error;
|
|
114
|
-
const err = new Error(
|
|
115
|
-
info.message,
|
|
116
|
-
info.cause !== undefined ? { cause: info.cause } : undefined,
|
|
117
|
-
);
|
|
118
|
-
if (info.name) err.name = info.name;
|
|
119
|
-
if (info.stack) err.stack = info.stack;
|
|
120
|
-
if (info.code !== undefined) (err as { code?: string }).code = info.code;
|
|
121
|
-
throw err;
|
|
161
|
+
throw loaderResultError(result);
|
|
122
162
|
}
|
|
123
163
|
}
|
|
124
164
|
|
|
@@ -153,14 +193,7 @@ export function decodeLoaderEntry(result: any): any {
|
|
|
153
193
|
return result.data;
|
|
154
194
|
}
|
|
155
195
|
throwSignal(result);
|
|
156
|
-
const
|
|
157
|
-
const err = new Error(
|
|
158
|
-
info.message,
|
|
159
|
-
info.cause !== undefined ? { cause: info.cause } : undefined,
|
|
160
|
-
);
|
|
161
|
-
if (info.name) err.name = info.name;
|
|
162
|
-
if (info.stack) err.stack = info.stack;
|
|
163
|
-
if (info.code !== undefined) (err as { code?: string }).code = info.code;
|
|
196
|
+
const err = loaderResultError(result);
|
|
164
197
|
if (result.fallback != null) {
|
|
165
198
|
(err as any)[LOADER_ERROR_FALLBACK] = result.fallback;
|
|
166
199
|
}
|
package/src/href-client.ts
CHANGED
|
@@ -274,7 +274,9 @@ declare global {
|
|
|
274
274
|
* Type-safe href function for client-side use
|
|
275
275
|
*
|
|
276
276
|
* Without mount: identity function, validates absolute paths at compile time.
|
|
277
|
-
* With mount: prepends mount
|
|
277
|
+
* With mount: prepends the mount as given. `useMount()` under a parameterized
|
|
278
|
+
* include returns its pattern (`/:accountId`), which this does not fill: use
|
|
279
|
+
* `useHref()` there, which substitutes the mount params.
|
|
278
280
|
*
|
|
279
281
|
* @param path - A valid path matching one of the registered route patterns
|
|
280
282
|
* @param mount - Optional mount prefix from useMount() for include-scoped paths
|
package/src/index.rsc.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -134,6 +134,14 @@ function serverOnlyStubError(name: string): Error {
|
|
|
134
134
|
);
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
/** A stub whose name `@rangojs/router/client` exports for clientUrls(). */
|
|
138
|
+
function clientUrlsHelperStubError(name: string): Error {
|
|
139
|
+
return new Error(
|
|
140
|
+
`${name}() is only available from "@rangojs/router" in a react-server/RSC environment. ` +
|
|
141
|
+
`In a "use client" module (a clientUrls() component's .use), import ${name}() from "@rangojs/router/client".`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
137
145
|
/**
|
|
138
146
|
* Error-throwing stub for server-only `urls` function.
|
|
139
147
|
*/
|
|
@@ -276,7 +284,7 @@ export function middleware(): never {
|
|
|
276
284
|
throw serverOnlyStubError("middleware");
|
|
277
285
|
}
|
|
278
286
|
export function revalidate(): never {
|
|
279
|
-
throw
|
|
287
|
+
throw clientUrlsHelperStubError("revalidate");
|
|
280
288
|
}
|
|
281
289
|
// Cache tag APIs are server-only (real implementations in index.rsc.ts). These
|
|
282
290
|
// stubs keep the named-export shape identical under the default/non-react-server
|
|
@@ -291,10 +299,10 @@ export function revalidateTag(): never {
|
|
|
291
299
|
throw serverOnlyStubError("revalidateTag");
|
|
292
300
|
}
|
|
293
301
|
export function loader(): never {
|
|
294
|
-
throw
|
|
302
|
+
throw clientUrlsHelperStubError("loader");
|
|
295
303
|
}
|
|
296
304
|
export function loading(): never {
|
|
297
|
-
throw
|
|
305
|
+
throw clientUrlsHelperStubError("loading");
|
|
298
306
|
}
|
|
299
307
|
export function parallel(): never {
|
|
300
308
|
throw serverOnlyStubError("parallel");
|
|
@@ -309,7 +317,7 @@ export function notFoundBoundary(): never {
|
|
|
309
317
|
throw serverOnlyStubError("notFoundBoundary");
|
|
310
318
|
}
|
|
311
319
|
export function transition(): never {
|
|
312
|
-
throw
|
|
320
|
+
throw clientUrlsHelperStubError("transition");
|
|
313
321
|
}
|
|
314
322
|
|
|
315
323
|
// Request context type (safe for client)
|