@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
|
@@ -16,11 +16,6 @@ import type {
|
|
|
16
16
|
InternalHandlerContext,
|
|
17
17
|
ResolvedSegment,
|
|
18
18
|
} from "../types";
|
|
19
|
-
import { evaluateRevalidation } from "./revalidation.js";
|
|
20
|
-
import { getRequestContext } from "../server/request-context.js";
|
|
21
|
-
import { executeInterceptMiddleware } from "./middleware.js";
|
|
22
|
-
import { createReverseFunction } from "./handler-context.js";
|
|
23
|
-
import { getGlobalRouteMap } from "../route-map-builder.js";
|
|
24
19
|
import {
|
|
25
20
|
handleHandlerResult,
|
|
26
21
|
warnOnStreamedResponse,
|
|
@@ -29,15 +24,21 @@ import {
|
|
|
29
24
|
import { catchSegmentError } from "./segment-resolution/helpers.js";
|
|
30
25
|
import { trackStreamedHandler } from "./segment-resolution/streamed-handler-boundary.js";
|
|
31
26
|
import { findInOrphans } from "./error-handling.js";
|
|
32
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
deliverStartedLoaders,
|
|
29
|
+
resolveOwnerLoaders,
|
|
30
|
+
} from "./segment-resolution/revalidation.js";
|
|
31
|
+
import {
|
|
32
|
+
fillSends,
|
|
33
|
+
NOTHING_HELD,
|
|
34
|
+
} from "./segment-resolution/prefetch-deferral.js";
|
|
33
35
|
import type { SegmentResolutionDeps } from "./types.js";
|
|
34
|
-
import { debugLog } from "./logging.js";
|
|
35
36
|
import { getRouterContext } from "./router-context.js";
|
|
37
|
+
import type { RevalidationContext } from "./router-context.js";
|
|
36
38
|
import { runInSegmentTagScope } from "../cache/cache-tag.js";
|
|
37
39
|
import {
|
|
38
40
|
RangoContext,
|
|
39
41
|
latchPprHeaderScopeForEntries,
|
|
40
|
-
runInsideLoaderScope,
|
|
41
42
|
} from "../server/context.js";
|
|
42
43
|
|
|
43
44
|
/**
|
|
@@ -48,7 +49,6 @@ import {
|
|
|
48
49
|
* intercept over a ppr-declared target route, in its own store scope where
|
|
49
50
|
* the main funnel's latch does not apply. Latch by declaration, off the
|
|
50
51
|
* target route's manifest entry (same leaf input as the segment funnels).
|
|
51
|
-
* Called AFTER intercept middleware runs — middleware stays the live lane.
|
|
52
52
|
*/
|
|
53
53
|
function latchPprHeaderScopeForInterceptTarget(
|
|
54
54
|
interceptEntry: InterceptEntry,
|
|
@@ -172,7 +172,7 @@ export function findInterceptForRoute(
|
|
|
172
172
|
* An intercept handler's throw or notFound() renders the declaring entry's
|
|
173
173
|
* boundary as the slot component (catchSegmentError, as for route handlers on
|
|
174
174
|
* partial navigations: 500/404 status, onError "handler"). Responses and every
|
|
175
|
-
* error on the background re-render (
|
|
175
|
+
* error on the background re-render (`background`) rethrow, so a failed
|
|
176
176
|
* re-render aborts its cache write instead of storing the fallback.
|
|
177
177
|
*/
|
|
178
178
|
function renderInterceptHandlerError<TEnv>(
|
|
@@ -181,9 +181,9 @@ function renderInterceptHandlerError<TEnv>(
|
|
|
181
181
|
params: Record<string, string>,
|
|
182
182
|
context: HandlerContext<any, TEnv>,
|
|
183
183
|
deps: SegmentResolutionDeps<TEnv>,
|
|
184
|
-
|
|
184
|
+
background: boolean | undefined,
|
|
185
185
|
): ReactNode {
|
|
186
|
-
if (error instanceof Response ||
|
|
186
|
+
if (error instanceof Response || background) throw error;
|
|
187
187
|
return catchSegmentError(
|
|
188
188
|
error,
|
|
189
189
|
parentEntry,
|
|
@@ -198,8 +198,80 @@ function renderInterceptHandlerError<TEnv>(
|
|
|
198
198
|
).component;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
/** The segment id of `intercept`'s slot on `parentEntry`. */
|
|
202
|
+
export function interceptSlotId(
|
|
203
|
+
parentEntry: EntryData,
|
|
204
|
+
intercept: InterceptEntry,
|
|
205
|
+
): string {
|
|
206
|
+
return `${parentEntry.shortCode}.${intercept.slotName}`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Every segment id an intercept's slot owns, in order: the slot, then one
|
|
211
|
+
* loader segment per loader in declaration order, run or not. A request that
|
|
212
|
+
* resolves the intercept lists them in `matched` (match-result.ts).
|
|
213
|
+
*/
|
|
214
|
+
export function interceptSegmentIds(
|
|
215
|
+
parentEntry: EntryData,
|
|
216
|
+
intercept: InterceptEntry,
|
|
217
|
+
): string[] {
|
|
218
|
+
const slotId = interceptSlotId(parentEntry, intercept);
|
|
219
|
+
return [
|
|
220
|
+
slotId,
|
|
221
|
+
...intercept.loader.map((l, i) => `${slotId}D${i}.${l.loader.$$id}`),
|
|
222
|
+
];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* An intercept's loaders are loader segments of its slot, resolved through
|
|
227
|
+
* the partial lane's funnel (revalidation.ts resolveOwnerLoaders): each one
|
|
228
|
+
* revalidated on its own, kept in a fill, deferred by a prefetch when flagged.
|
|
229
|
+
*/
|
|
230
|
+
function resolveInterceptLoaders<TEnv>(
|
|
231
|
+
interceptEntry: InterceptEntry,
|
|
232
|
+
parentEntry: EntryData,
|
|
233
|
+
context: HandlerContext<any, TEnv>,
|
|
234
|
+
belongsToRoute: boolean,
|
|
235
|
+
deps: SegmentResolutionDeps<TEnv>,
|
|
236
|
+
rc: RevalidationContext,
|
|
237
|
+
): Promise<{ segments: ResolvedSegment[] }> {
|
|
238
|
+
return resolveOwnerLoaders(
|
|
239
|
+
{
|
|
240
|
+
loaders: interceptEntry.loader,
|
|
241
|
+
namespace: `intercept:${interceptEntry.routeName}`,
|
|
242
|
+
segmentPrefix: interceptSlotId(parentEntry, interceptEntry),
|
|
243
|
+
boundary: parentEntry,
|
|
244
|
+
bakeLane: false,
|
|
245
|
+
traceSource: "intercept-loader",
|
|
246
|
+
},
|
|
247
|
+
context,
|
|
248
|
+
belongsToRoute,
|
|
249
|
+
rc.clientSegmentIds,
|
|
250
|
+
rc.prevParams,
|
|
251
|
+
rc.request,
|
|
252
|
+
rc.prevUrl,
|
|
253
|
+
rc.nextUrl,
|
|
254
|
+
rc.routeKey,
|
|
255
|
+
deps,
|
|
256
|
+
rc.actionContext,
|
|
257
|
+
rc.stale,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
201
261
|
/**
|
|
202
|
-
*
|
|
262
|
+
* An intercept slot with no loading() waits for its loaders that are not
|
|
263
|
+
* deferred; the browser's side is foldInterceptLoaders (segment-system.tsx).
|
|
264
|
+
*/
|
|
265
|
+
async function settleLoaderSegments(
|
|
266
|
+
segments: readonly ResolvedSegment[],
|
|
267
|
+
): Promise<void> {
|
|
268
|
+
await Promise.all(
|
|
269
|
+
segments.filter((s) => !s.deferred).map((s) => s.loaderData),
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Resolve an intercept entry: its slot segment, then its loader segments.
|
|
203
275
|
*/
|
|
204
276
|
export async function resolveInterceptEntry<TEnv>(
|
|
205
277
|
interceptEntry: InterceptEntry,
|
|
@@ -208,150 +280,68 @@ export async function resolveInterceptEntry<TEnv>(
|
|
|
208
280
|
context: HandlerContext<any, TEnv>,
|
|
209
281
|
belongsToRoute: boolean,
|
|
210
282
|
deps: SegmentResolutionDeps<TEnv>,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
actionId?: string;
|
|
220
|
-
actionUrl?: URL;
|
|
221
|
-
actionResult?: any;
|
|
222
|
-
formData?: FormData;
|
|
223
|
-
};
|
|
224
|
-
stale?: boolean;
|
|
283
|
+
// The background re-render passes none: nothing held, rendered fresh.
|
|
284
|
+
revalidationContext: RevalidationContext = {
|
|
285
|
+
clientSegmentIds: NOTHING_HELD,
|
|
286
|
+
prevParams: {},
|
|
287
|
+
request: context.request,
|
|
288
|
+
prevUrl: context.url,
|
|
289
|
+
nextUrl: context.url,
|
|
290
|
+
routeKey: interceptEntry.routeName,
|
|
225
291
|
},
|
|
226
292
|
options?: {
|
|
227
293
|
/**
|
|
228
|
-
*
|
|
229
|
-
* background re-render paths (proactive caching, stale background
|
|
294
|
+
* Set ONLY by the post-response background re-render (stale background
|
|
230
295
|
* revalidation), whose sole purpose is to re-render the segment tree to
|
|
231
|
-
* populate the cache
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
* which the background render reuses. Re-running middleware here would fire
|
|
235
|
-
* its side effects a SECOND time, and a middleware that short-circuits with
|
|
236
|
-
* a Response would `throw` and silently abort the cache write. Never set on
|
|
237
|
-
* the foreground path.
|
|
296
|
+
* populate the cache: a handler error rejects instead of rendering the
|
|
297
|
+
* error boundary, so a failure is never cached. Never set on the
|
|
298
|
+
* foreground path.
|
|
238
299
|
*/
|
|
239
|
-
|
|
300
|
+
background?: boolean;
|
|
240
301
|
},
|
|
241
302
|
): Promise<ResolvedSegment[]> {
|
|
242
|
-
const segments: ResolvedSegment[] = [];
|
|
243
|
-
|
|
244
|
-
if (!options?.skipMiddleware && interceptEntry.middleware.length > 0) {
|
|
245
|
-
const requestCtx = getRequestContext();
|
|
246
|
-
if (!requestCtx?.res) {
|
|
247
|
-
throw new Error(
|
|
248
|
-
"Request context with stubResponse is required for intercept middleware",
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
const middlewareResponse = await executeInterceptMiddleware(
|
|
252
|
-
interceptEntry.middleware,
|
|
253
|
-
context.request,
|
|
254
|
-
context.env,
|
|
255
|
-
params,
|
|
256
|
-
(context as InternalHandlerContext<any, TEnv>)._variables,
|
|
257
|
-
requestCtx.res,
|
|
258
|
-
createReverseFunction(getGlobalRouteMap()),
|
|
259
|
-
);
|
|
260
|
-
if (middlewareResponse) throw middlewareResponse;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
303
|
latchPprHeaderScopeForInterceptTarget(interceptEntry);
|
|
264
304
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
305
|
+
const slotSegmentId = interceptSlotId(parentEntry, interceptEntry);
|
|
306
|
+
const { segments: loaderSegments } = await resolveInterceptLoaders(
|
|
307
|
+
interceptEntry,
|
|
308
|
+
parentEntry,
|
|
309
|
+
context,
|
|
310
|
+
belongsToRoute,
|
|
311
|
+
deps,
|
|
312
|
+
revalidationContext,
|
|
313
|
+
);
|
|
314
|
+
// The slot's tag scope (#957, cache-tag.ts runInSegmentTagScope) and the
|
|
315
|
+
// owner of its handler's handle pushes (a handler's ctx.use(Handle) reads
|
|
316
|
+
// it when called).
|
|
271
317
|
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
272
318
|
slotSegmentId;
|
|
273
|
-
const hasLoaders = interceptEntry.loader.length > 0;
|
|
274
|
-
|
|
275
|
-
const loaderPromises: Promise<any>[] = [];
|
|
276
|
-
const loaderIds: string[] = [];
|
|
277
|
-
|
|
278
|
-
for (let i = 0; i < interceptEntry.loader.length; i++) {
|
|
279
|
-
const loaderEntry = interceptEntry.loader[i];
|
|
280
|
-
const { loader, revalidate: loaderRevalidateFns } = loaderEntry;
|
|
281
|
-
const segmentId = `${slotSegmentId}D${i}.${loader.$$id}`;
|
|
282
|
-
|
|
283
|
-
if (revalidationContext) {
|
|
284
|
-
const {
|
|
285
|
-
clientSegmentIds,
|
|
286
|
-
prevParams,
|
|
287
|
-
request,
|
|
288
|
-
prevUrl,
|
|
289
|
-
nextUrl,
|
|
290
|
-
routeKey,
|
|
291
|
-
actionContext,
|
|
292
|
-
stale,
|
|
293
|
-
} = revalidationContext;
|
|
294
|
-
|
|
295
|
-
if (clientSegmentIds.has(slotSegmentId)) {
|
|
296
|
-
const dummySegment: ResolvedSegment = {
|
|
297
|
-
id: segmentId,
|
|
298
|
-
namespace: `intercept:${interceptEntry.routeName}`,
|
|
299
|
-
type: "loader",
|
|
300
|
-
index: i,
|
|
301
|
-
component: null,
|
|
302
|
-
params,
|
|
303
|
-
loaderId: loader.$$id,
|
|
304
|
-
belongsToRoute,
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
const shouldRevalidate = await evaluateRevalidation({
|
|
308
|
-
segment: dummySegment,
|
|
309
|
-
prevParams,
|
|
310
|
-
getPrevSegment: null,
|
|
311
|
-
request,
|
|
312
|
-
prevUrl,
|
|
313
|
-
nextUrl,
|
|
314
|
-
revalidations: loaderRevalidateFns.map((fn, j) => ({
|
|
315
|
-
name: `intercept-loader-revalidate${j}`,
|
|
316
|
-
fn,
|
|
317
|
-
})),
|
|
318
|
-
routeKey,
|
|
319
|
-
context,
|
|
320
|
-
actionContext,
|
|
321
|
-
stale,
|
|
322
|
-
traceSource: "intercept-loader",
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
if (!shouldRevalidate) {
|
|
326
|
-
debugLog("intercept.loader", "skipped revalidation", {
|
|
327
|
-
loaderId: loader.$$id,
|
|
328
|
-
});
|
|
329
|
-
continue;
|
|
330
|
-
}
|
|
331
|
-
debugLog("intercept.loader", "revalidating", {
|
|
332
|
-
loaderId: loader.$$id,
|
|
333
|
-
stale,
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
319
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
320
|
+
const slotSegment = (
|
|
321
|
+
component: ReactNode,
|
|
322
|
+
layout: ReactNode | undefined,
|
|
323
|
+
): ResolvedSegment => ({
|
|
324
|
+
id: slotSegmentId,
|
|
325
|
+
namespace: `intercept:${interceptEntry.routeName}`,
|
|
326
|
+
type: "parallel",
|
|
327
|
+
index: 0,
|
|
328
|
+
component,
|
|
329
|
+
loading: interceptEntry.loading === false ? null : interceptEntry.loading,
|
|
330
|
+
layout,
|
|
331
|
+
params,
|
|
332
|
+
slot: interceptEntry.slotName,
|
|
333
|
+
belongsToRoute,
|
|
334
|
+
parallelName: `intercept:${interceptEntry.routeName}.${interceptEntry.slotName}`,
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// A fill keeps a slot the client holds: neither its handler nor its layout
|
|
338
|
+
// runs, only the loaders the client lacks (match-result.ts leaves the null
|
|
339
|
+
// slot out of the response).
|
|
340
|
+
if (
|
|
341
|
+
fillSends(context, slotSegmentId, revalidationContext.clientSegmentIds) ===
|
|
342
|
+
false
|
|
343
|
+
) {
|
|
344
|
+
return [slotSegment(null, undefined), ...loaderSegments];
|
|
355
345
|
}
|
|
356
346
|
|
|
357
347
|
let handlerResult: ReactNode;
|
|
@@ -370,7 +360,7 @@ export async function resolveInterceptEntry<TEnv>(
|
|
|
370
360
|
params,
|
|
371
361
|
context,
|
|
372
362
|
deps,
|
|
373
|
-
options?.
|
|
363
|
+
options?.background,
|
|
374
364
|
);
|
|
375
365
|
}
|
|
376
366
|
// Consumed only after the layout and loader awaits below; a rejection in that
|
|
@@ -397,11 +387,10 @@ export async function resolveInterceptEntry<TEnv>(
|
|
|
397
387
|
}
|
|
398
388
|
|
|
399
389
|
let component: ReactNode;
|
|
400
|
-
let loaderDataPromise: Promise<any[]> | any[] | undefined;
|
|
401
390
|
// A streamed (loading()) handler resolves after the 200 has started: tracked
|
|
402
391
|
// like route handlers (onError report, handle-store completion) and
|
|
403
|
-
// recovered into the declared boundary, except on the
|
|
404
|
-
//
|
|
392
|
+
// recovered into the declared boundary, except on the background
|
|
393
|
+
// re-render, which must reject so a failure is never cached.
|
|
405
394
|
const streamedComponent = (promise: Promise<unknown>): ReactNode =>
|
|
406
395
|
trackStreamedHandler(deps, promise, {
|
|
407
396
|
boundaryEntry: parentEntry,
|
|
@@ -410,10 +399,10 @@ export async function resolveInterceptEntry<TEnv>(
|
|
|
410
399
|
context,
|
|
411
400
|
routeKey: interceptEntry.routeName,
|
|
412
401
|
params,
|
|
413
|
-
recover: !options?.
|
|
402
|
+
recover: !options?.background,
|
|
414
403
|
});
|
|
415
404
|
|
|
416
|
-
if (interceptEntry.loading &&
|
|
405
|
+
if (interceptEntry.loading && loaderSegments.length > 0) {
|
|
417
406
|
if (handlerResult instanceof Promise) {
|
|
418
407
|
warnOnStreamedResponse(
|
|
419
408
|
handlerResult,
|
|
@@ -424,13 +413,10 @@ export async function resolveInterceptEntry<TEnv>(
|
|
|
424
413
|
handlerResult instanceof Promise
|
|
425
414
|
? streamedComponent(handlerResult)
|
|
426
415
|
: (Promise.resolve(handlerResult) as ReactNode);
|
|
427
|
-
loaderDataPromise = Promise.all(loaderPromises);
|
|
428
416
|
} else if (interceptEntry.loading && handlerResult instanceof Promise) {
|
|
429
417
|
component = streamedComponent(handlerResult);
|
|
430
418
|
} else {
|
|
431
|
-
if (
|
|
432
|
-
loaderDataPromise = await Promise.all(loaderPromises);
|
|
433
|
-
}
|
|
419
|
+
if (!interceptEntry.loading) await settleLoaderSegments(loaderSegments);
|
|
434
420
|
component = handlerResult;
|
|
435
421
|
if (handlerResult instanceof Promise) {
|
|
436
422
|
try {
|
|
@@ -442,172 +428,40 @@ export async function resolveInterceptEntry<TEnv>(
|
|
|
442
428
|
params,
|
|
443
429
|
context,
|
|
444
430
|
deps,
|
|
445
|
-
options?.
|
|
431
|
+
options?.background,
|
|
446
432
|
);
|
|
447
433
|
}
|
|
448
434
|
}
|
|
449
435
|
}
|
|
436
|
+
// After the handler is awaited, or at once when it streams: the loaders it
|
|
437
|
+
// started with ctx.use() by now are delivered from this response.
|
|
438
|
+
deliverStartedLoaders(context, loaderSegments);
|
|
450
439
|
|
|
451
|
-
|
|
452
|
-
// client holds the slot and all skipped. Absent replaces the client's
|
|
453
|
-
// aggregate, empty merges into it (merge-segment-loaders.ts).
|
|
454
|
-
if (hasLoaders && loaderPromises.length === 0) {
|
|
455
|
-
loaderDataPromise = [];
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
const interceptSegment = {
|
|
459
|
-
id: slotSegmentId,
|
|
460
|
-
namespace: `intercept:${interceptEntry.routeName}`,
|
|
461
|
-
type: "parallel" as const,
|
|
462
|
-
index: 0,
|
|
463
|
-
component,
|
|
464
|
-
loading: interceptEntry.loading === false ? null : interceptEntry.loading,
|
|
465
|
-
layout: layoutElement,
|
|
466
|
-
params,
|
|
467
|
-
slot: interceptEntry.slotName,
|
|
468
|
-
belongsToRoute,
|
|
469
|
-
parallelName: `intercept:${interceptEntry.routeName}.${interceptEntry.slotName}`,
|
|
470
|
-
loaderDataPromise,
|
|
471
|
-
loaderIds: hasLoaders ? loaderIds : undefined,
|
|
472
|
-
};
|
|
473
|
-
segments.push(interceptSegment);
|
|
474
|
-
|
|
475
|
-
return segments;
|
|
440
|
+
return [slotSegment(component, layoutElement), ...loaderSegments];
|
|
476
441
|
}
|
|
477
442
|
|
|
478
443
|
/**
|
|
479
|
-
*
|
|
480
|
-
*
|
|
444
|
+
* The loader segments of a cached intercept slot, on a cache hit: the slot
|
|
445
|
+
* keeps its cached component and layout, its loaders are never cached.
|
|
481
446
|
*/
|
|
482
447
|
export async function resolveInterceptLoadersOnly<TEnv>(
|
|
483
448
|
interceptEntry: InterceptEntry,
|
|
484
449
|
parentEntry: EntryData,
|
|
485
|
-
params: Record<string, string>,
|
|
486
450
|
context: HandlerContext<any, TEnv>,
|
|
487
451
|
belongsToRoute: boolean,
|
|
488
452
|
deps: SegmentResolutionDeps<TEnv>,
|
|
489
|
-
revalidationContext:
|
|
490
|
-
|
|
491
|
-
prevParams: Record<string, string>;
|
|
492
|
-
request: Request;
|
|
493
|
-
prevUrl: URL;
|
|
494
|
-
nextUrl: URL;
|
|
495
|
-
routeKey: string;
|
|
496
|
-
actionContext?: {
|
|
497
|
-
actionId?: string;
|
|
498
|
-
actionUrl?: URL;
|
|
499
|
-
actionResult?: any;
|
|
500
|
-
formData?: FormData;
|
|
501
|
-
};
|
|
502
|
-
stale?: boolean;
|
|
503
|
-
},
|
|
504
|
-
): Promise<{
|
|
505
|
-
loaderDataPromise: Promise<any[]> | any[];
|
|
506
|
-
loaderIds: string[];
|
|
507
|
-
} | null> {
|
|
508
|
-
if (interceptEntry.loader.length === 0) {
|
|
509
|
-
return null;
|
|
510
|
-
}
|
|
511
|
-
|
|
453
|
+
revalidationContext: RevalidationContext,
|
|
454
|
+
): Promise<ResolvedSegment[]> {
|
|
512
455
|
latchPprHeaderScopeForInterceptTarget(interceptEntry);
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
request,
|
|
525
|
-
prevUrl,
|
|
526
|
-
nextUrl,
|
|
527
|
-
routeKey,
|
|
528
|
-
actionContext,
|
|
529
|
-
stale,
|
|
530
|
-
} = revalidationContext;
|
|
531
|
-
|
|
532
|
-
for (let i = 0; i < interceptEntry.loader.length; i++) {
|
|
533
|
-
const loaderEntry = interceptEntry.loader[i];
|
|
534
|
-
const { loader, revalidate: loaderRevalidateFns } = loaderEntry;
|
|
535
|
-
const segmentId = `${slotSegmentId}D${i}.${loader.$$id}`;
|
|
536
|
-
|
|
537
|
-
if (clientSegmentIds.has(slotSegmentId)) {
|
|
538
|
-
const dummySegment: ResolvedSegment = {
|
|
539
|
-
id: segmentId,
|
|
540
|
-
namespace: `intercept:${interceptEntry.routeName}`,
|
|
541
|
-
type: "loader",
|
|
542
|
-
index: i,
|
|
543
|
-
component: null,
|
|
544
|
-
params,
|
|
545
|
-
loaderId: loader.$$id,
|
|
546
|
-
belongsToRoute,
|
|
547
|
-
};
|
|
548
|
-
|
|
549
|
-
const shouldRevalidate = await evaluateRevalidation({
|
|
550
|
-
segment: dummySegment,
|
|
551
|
-
prevParams,
|
|
552
|
-
getPrevSegment: null,
|
|
553
|
-
request,
|
|
554
|
-
prevUrl,
|
|
555
|
-
nextUrl,
|
|
556
|
-
revalidations: loaderRevalidateFns.map((fn, j) => ({
|
|
557
|
-
name: `intercept-loader-revalidate${j}`,
|
|
558
|
-
fn,
|
|
559
|
-
})),
|
|
560
|
-
routeKey,
|
|
561
|
-
context,
|
|
562
|
-
actionContext,
|
|
563
|
-
stale,
|
|
564
|
-
traceSource: "intercept-loader",
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
if (!shouldRevalidate) {
|
|
568
|
-
debugLog("intercept.loader", "skipped on cache hit", {
|
|
569
|
-
loaderId: loader.$$id,
|
|
570
|
-
});
|
|
571
|
-
continue;
|
|
572
|
-
}
|
|
573
|
-
debugLog("intercept.loader", "revalidating on cache hit", {
|
|
574
|
-
loaderId: loader.$$id,
|
|
575
|
-
stale,
|
|
576
|
-
});
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
loaderIds.push(loader.$$id);
|
|
580
|
-
loaderPromises.push(
|
|
581
|
-
deps.wrapLoaderPromise(
|
|
582
|
-
// The DSL binding's funnel, as for route loaders: a loader's own
|
|
583
|
-
// cache() applies here too.
|
|
584
|
-
runInsideLoaderScope(
|
|
585
|
-
() => resolveLoaderData(loaderEntry, context, context.pathname),
|
|
586
|
-
loader.$$id,
|
|
587
|
-
),
|
|
588
|
-
parentEntry,
|
|
589
|
-
segmentId,
|
|
590
|
-
context.pathname,
|
|
591
|
-
// Report a throwing intercept loader to onError + loader.error telemetry,
|
|
592
|
-
// matching the fresh/revalidation paths. resolveInterceptLoadersOnly is
|
|
593
|
-
// only called on the cache-hit partial-update path (handleCacheHitIntercept),
|
|
594
|
-
// so flag isPartial:true exactly like revalidation.ts's partial path.
|
|
595
|
-
{ ...buildLoaderErrorContext(context), isPartial: true },
|
|
596
|
-
),
|
|
597
|
-
);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// Every loader held: the empty aggregate (see resolveInterceptEntry).
|
|
601
|
-
if (loaderPromises.length === 0) {
|
|
602
|
-
return { loaderDataPromise: [], loaderIds };
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// Match fresh-path semantics: only defer (no await) when loading is truthy.
|
|
606
|
-
// `loading: false` means "no loading UI, await loaders before render" —
|
|
607
|
-
// same as the fresh path's `if (interceptEntry.loading && ...)` check.
|
|
608
|
-
const loaderDataPromise = interceptEntry.loading
|
|
609
|
-
? Promise.all(loaderPromises)
|
|
610
|
-
: await Promise.all(loaderPromises);
|
|
611
|
-
|
|
612
|
-
return { loaderDataPromise, loaderIds };
|
|
456
|
+
const { segments } = await resolveInterceptLoaders(
|
|
457
|
+
interceptEntry,
|
|
458
|
+
parentEntry,
|
|
459
|
+
context,
|
|
460
|
+
belongsToRoute,
|
|
461
|
+
deps,
|
|
462
|
+
revalidationContext,
|
|
463
|
+
);
|
|
464
|
+
if (!interceptEntry.loading) await settleLoaderSegments(segments);
|
|
465
|
+
deliverStartedLoaders(context, segments);
|
|
466
|
+
return segments;
|
|
613
467
|
}
|
|
@@ -102,36 +102,51 @@ export function evaluateLazyEntry<TEnv = any>(
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// Async provider (`() => import("./routes")`): the route module is evaluated
|
|
105
|
-
// off the startup path, on the first request reaching this prefix.
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
// per-entry match loop pays no microtask for normal includes.
|
|
105
|
+
// off the startup path, on the first request reaching this prefix. The eager
|
|
106
|
+
// path below stays fully synchronous (no Promise), so the per-entry match
|
|
107
|
+
// loop pays no microtask for normal includes.
|
|
109
108
|
const lazyPatterns = entry.lazyPatterns;
|
|
110
109
|
if (isIncludeProvider(lazyPatterns)) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
await lazyPatterns(),
|
|
116
|
-
entry.staticPrefix,
|
|
117
|
-
);
|
|
118
|
-
// Cache the resolved patterns: any later re-entry expands synchronously.
|
|
119
|
-
entry.lazyPatterns = resolved as unknown as UrlPatterns<TEnv>;
|
|
120
|
-
runExpansion(entry, deps, resolved as UrlPatterns<TEnv>);
|
|
121
|
-
})();
|
|
122
|
-
(entry as { _lazyInflight?: Promise<void> })._lazyInflight = work;
|
|
123
|
-
const clear = () => {
|
|
124
|
-
(entry as { _lazyInflight?: Promise<void> })._lazyInflight = undefined;
|
|
125
|
-
};
|
|
126
|
-
// On failure, clear the flag (lazyEvaluated stays false) so a later request
|
|
127
|
-
// can retry the import rather than wedging the route permanently.
|
|
128
|
-
work.then(clear, clear);
|
|
129
|
-
return work;
|
|
110
|
+
// Concurrent first-hits share the import; the first to resume expands.
|
|
111
|
+
return resolveEntryPatterns(entry).then((resolved) => {
|
|
112
|
+
if (!entry.lazyEvaluated) runExpansion(entry, deps, resolved);
|
|
113
|
+
});
|
|
130
114
|
}
|
|
131
115
|
|
|
132
116
|
runExpansion(entry, deps, lazyPatterns as UrlPatterns<TEnv>);
|
|
133
117
|
}
|
|
134
118
|
|
|
119
|
+
type ProviderEntry<TEnv> = RouteEntry<TEnv> & {
|
|
120
|
+
_providerInflight?: Promise<UrlPatterns<TEnv>>;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Resolve an async include entry's provider once and cache the patterns on
|
|
125
|
+
* `entry.lazyPatterns`. Concurrent callers (match-time expansion and
|
|
126
|
+
* render-time loadManifest) share one in-flight import; it clears when it
|
|
127
|
+
* settles, so a rejected import is retried by the next request.
|
|
128
|
+
*/
|
|
129
|
+
export function resolveEntryPatterns<TEnv = any>(
|
|
130
|
+
entry: RouteEntry<TEnv>,
|
|
131
|
+
): Promise<UrlPatterns<TEnv>> {
|
|
132
|
+
const target = entry as ProviderEntry<TEnv>;
|
|
133
|
+
const lazyPatterns = target.lazyPatterns;
|
|
134
|
+
if (!isIncludeProvider(lazyPatterns)) {
|
|
135
|
+
return Promise.resolve(lazyPatterns as UrlPatterns<TEnv>);
|
|
136
|
+
}
|
|
137
|
+
target._providerInflight ??= (async () => {
|
|
138
|
+
const resolved = resolveIncludeModule(
|
|
139
|
+
await lazyPatterns(),
|
|
140
|
+
target.staticPrefix,
|
|
141
|
+
) as UrlPatterns<TEnv>;
|
|
142
|
+
target.lazyPatterns = resolved;
|
|
143
|
+
return resolved;
|
|
144
|
+
})().finally(() => {
|
|
145
|
+
target._providerInflight = undefined;
|
|
146
|
+
});
|
|
147
|
+
return target._providerInflight;
|
|
148
|
+
}
|
|
149
|
+
|
|
135
150
|
/**
|
|
136
151
|
* Synchronously expand a lazy entry's (already-resolved) patterns into routes
|
|
137
152
|
* and splice any nested lazy includes as new entries. Runs once per entry.
|
|
@@ -143,10 +158,9 @@ function runExpansion<TEnv = any>(
|
|
|
143
158
|
): void {
|
|
144
159
|
// lazyEvaluated is set at the END, only after the handler ran and the routes
|
|
145
160
|
// (and any nested includes) were spliced. Setting it up-front would mark the
|
|
146
|
-
// entry done even if the handler throws mid-expansion
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
// (line ~77) forever, wedging the route at 404 until the isolate restarts.
|
|
161
|
+
// entry done even if the handler throws mid-expansion, so a later request
|
|
162
|
+
// could not retry: evaluateLazyEntry would short-circuit forever, wedging the
|
|
163
|
+
// route at 404 until the isolate restarts.
|
|
150
164
|
const lazyContext = entry.lazyContext;
|
|
151
165
|
|
|
152
166
|
const manifest = new Map<string, EntryData>();
|
|
@@ -173,6 +187,8 @@ function runExpansion<TEnv = any>(
|
|
|
173
187
|
rootScoped: lazyContext?.rootScoped,
|
|
174
188
|
routerId: lazyContext?.routerId,
|
|
175
189
|
includeScope: lazyContext?.includeScope,
|
|
190
|
+
scopePath: lazyContext?.scopePath,
|
|
191
|
+
hiddenScopePath: lazyContext?.hiddenScopePath,
|
|
176
192
|
},
|
|
177
193
|
() => {
|
|
178
194
|
const includePrefix = (entry as any)._lazyPrefix || "";
|