@rangojs/router 0.0.0-experimental.77 → 0.0.0-experimental.77ed8945
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 +120 -25
- package/dist/bin/rango.js +147 -57
- package/dist/vite/index.js +2103 -861
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +13 -8
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +3 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +220 -30
- package/skills/caching/SKILL.md +116 -8
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +3 -1
- package/skills/hooks/SKILL.md +229 -20
- package/skills/host-router/SKILL.md +66 -20
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +26 -4
- package/skills/layout/SKILL.md +6 -7
- package/skills/links/SKILL.md +247 -17
- package/skills/loader/SKILL.md +219 -9
- package/skills/middleware/SKILL.md +47 -12
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +12 -6
- package/skills/prerender/SKILL.md +14 -33
- package/skills/rango/SKILL.md +238 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +122 -47
- package/skills/route/SKILL.md +33 -4
- package/skills/router-setup/SKILL.md +3 -3
- package/skills/server-actions/SKILL.md +751 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/typesafety/SKILL.md +319 -27
- package/skills/use-cache/SKILL.md +34 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/event-controller.ts +86 -70
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/navigation-bridge.ts +29 -9
- package/src/browser/navigation-client.ts +99 -77
- package/src/browser/navigation-store.ts +7 -8
- package/src/browser/navigation-transaction.ts +10 -28
- package/src/browser/partial-update.ts +60 -40
- package/src/browser/prefetch/cache.ts +196 -49
- package/src/browser/prefetch/fetch.ts +203 -59
- package/src/browser/prefetch/queue.ts +36 -5
- package/src/browser/rango-state.ts +37 -13
- package/src/browser/react/Link.tsx +18 -13
- package/src/browser/react/NavigationProvider.tsx +75 -31
- package/src/browser/react/filter-segment-order.ts +51 -7
- package/src/browser/react/index.ts +3 -0
- package/src/browser/react/location-state-shared.ts +175 -4
- package/src/browser/react/location-state.ts +39 -13
- package/src/browser/react/use-handle.ts +17 -9
- package/src/browser/react/use-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +20 -8
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +23 -2
- package/src/browser/react/use-segments.ts +11 -8
- package/src/browser/response-adapter.ts +52 -1
- package/src/browser/rsc-router.tsx +71 -22
- package/src/browser/scroll-restoration.ts +22 -14
- package/src/browser/segment-reconciler.ts +10 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +44 -30
- package/src/browser/types.ts +12 -2
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +60 -35
- package/src/build/generate-route-types.ts +2 -0
- package/src/build/index.ts +8 -1
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +45 -1
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +1 -1
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +55 -14
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-runtime.ts +17 -5
- package/src/cache/cache-scope.ts +51 -49
- package/src/cache/cf/cf-cache-store.ts +502 -32
- package/src/cache/cf/index.ts +3 -0
- package/src/cache/handle-snapshot.ts +103 -0
- package/src/cache/index.ts +3 -0
- package/src/cache/memory-segment-store.ts +3 -2
- package/src/cache/types.ts +10 -6
- package/src/client.rsc.tsx +3 -0
- package/src/client.tsx +96 -205
- package/src/context-var.ts +5 -5
- package/src/decode-loader-results.ts +36 -0
- package/src/errors.ts +30 -4
- package/src/handle.ts +4 -6
- package/src/host/index.ts +2 -2
- package/src/host/router.ts +129 -57
- package/src/host/types.ts +31 -2
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +140 -21
- package/src/index.rsc.ts +10 -6
- package/src/index.ts +17 -8
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +2 -5
- package/src/loader.ts +3 -10
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/prerender/store.ts +9 -7
- package/src/prerender.ts +4 -4
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -39
- package/src/route-content-wrapper.tsx +6 -28
- package/src/route-definition/dsl-helpers.ts +253 -265
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +43 -15
- package/src/route-definition/resolve-handler-use.ts +6 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-types.ts +26 -41
- package/src/router/content-negotiation.ts +15 -2
- package/src/router/error-handling.ts +1 -1
- package/src/router/find-match.ts +54 -6
- package/src/router/handler-context.ts +21 -41
- package/src/router/intercept-resolution.ts +4 -18
- package/src/router/lazy-includes.ts +41 -22
- package/src/router/loader-resolution.ts +82 -36
- package/src/router/manifest.ts +41 -19
- package/src/router/match-api.ts +4 -3
- package/src/router/match-handlers.ts +1 -0
- package/src/router/match-middleware/cache-lookup.ts +57 -95
- package/src/router/match-middleware/cache-store.ts +3 -2
- package/src/router/match-result.ts +53 -32
- package/src/router/metrics.ts +1 -1
- package/src/router/middleware-types.ts +15 -26
- package/src/router/middleware.ts +99 -84
- package/src/router/pattern-matching.ts +116 -19
- package/src/router/prerender-match.ts +40 -15
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +40 -37
- package/src/router/revalidation.ts +58 -2
- package/src/router/router-interfaces.ts +51 -35
- package/src/router/router-options.ts +25 -1
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +27 -6
- package/src/router/segment-resolution/revalidation.ts +147 -106
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/trie-matching.ts +40 -16
- package/src/router/types.ts +8 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +37 -25
- package/src/rsc/handler-context.ts +2 -2
- package/src/rsc/handler.ts +58 -77
- package/src/rsc/helpers.ts +72 -43
- package/src/rsc/index.ts +1 -1
- package/src/rsc/manifest-init.ts +28 -41
- package/src/rsc/origin-guard.ts +30 -10
- package/src/rsc/progressive-enhancement.ts +4 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +76 -61
- package/src/rsc/rsc-rendering.ts +45 -51
- package/src/rsc/runtime-warnings.ts +9 -10
- package/src/rsc/server-action.ts +33 -39
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/types.ts +8 -2
- package/src/search-params.ts +4 -4
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +132 -116
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +175 -53
- package/src/server/cookie-store.ts +28 -4
- package/src/server/request-context.ts +57 -51
- package/src/ssr/index.tsx +5 -1
- package/src/static-handler.ts +1 -1
- package/src/types/global-namespace.ts +39 -26
- package/src/types/handler-context.ts +68 -50
- package/src/types/index.ts +1 -0
- package/src/types/loader-types.ts +11 -9
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-entry.ts +11 -0
- package/src/types/segments.ts +35 -2
- package/src/urls/include-helper.ts +34 -67
- package/src/urls/index.ts +1 -5
- package/src/urls/path-helper-types.ts +17 -3
- package/src/urls/path-helper.ts +17 -52
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +22 -29
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -5
- package/src/use-loader.tsx +413 -42
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +6 -6
- package/src/vite/discovery/discover-routers.ts +106 -75
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +72 -31
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +33 -0
- package/src/vite/discovery/virtual-module-codegen.ts +13 -23
- package/src/vite/index.ts +2 -0
- package/src/vite/plugin-types.ts +67 -0
- package/src/vite/plugins/cjs-to-esm.ts +8 -7
- package/src/vite/plugins/client-ref-dedup.ts +16 -0
- package/src/vite/plugins/client-ref-hashing.ts +28 -5
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/plugins/expose-action-id.ts +54 -30
- package/src/vite/plugins/expose-id-utils.ts +12 -8
- package/src/vite/plugins/expose-ids/export-analysis.ts +100 -20
- package/src/vite/plugins/expose-ids/handler-transform.ts +8 -61
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -5
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
- package/src/vite/plugins/expose-internal-ids.ts +496 -486
- package/src/vite/plugins/performance-tracks.ts +29 -25
- package/src/vite/plugins/use-cache-transform.ts +65 -50
- package/src/vite/plugins/version-injector.ts +39 -23
- package/src/vite/plugins/version-plugin.ts +59 -2
- package/src/vite/plugins/virtual-entries.ts +2 -2
- package/src/vite/rango.ts +116 -29
- package/src/vite/router-discovery.ts +753 -104
- package/src/vite/utils/ast-handler-extract.ts +15 -15
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +4 -2
- package/src/vite/utils/client-chunks.ts +190 -0
- package/src/vite/utils/forward-user-plugins.ts +193 -0
- package/src/vite/utils/manifest-utils.ts +8 -59
- package/src/vite/utils/package-resolution.ts +41 -1
- package/src/vite/utils/prerender-utils.ts +5 -4
- package/src/vite/utils/shared-utils.ts +107 -26
- package/src/browser/action-response-classifier.ts +0 -99
|
@@ -114,6 +114,9 @@ let _deserializeSegments:
|
|
|
114
114
|
let _restoreHandles:
|
|
115
115
|
| typeof import("../../cache/handle-snapshot.js").restoreHandles
|
|
116
116
|
| undefined;
|
|
117
|
+
let _decodeHandles:
|
|
118
|
+
| typeof import("../../cache/handle-snapshot.js").decodeHandles
|
|
119
|
+
| undefined;
|
|
117
120
|
let _hashParams:
|
|
118
121
|
| typeof import("../../prerender/param-hash.js").hashParams
|
|
119
122
|
| undefined;
|
|
@@ -148,6 +151,7 @@ async function ensurePrerenderDeps() {
|
|
|
148
151
|
]);
|
|
149
152
|
_deserializeSegments = codec.deserializeSegments;
|
|
150
153
|
_restoreHandles = snapshot.restoreHandles;
|
|
154
|
+
_decodeHandles = snapshot.decodeHandles;
|
|
151
155
|
_hashParams = paramHash.hashParams;
|
|
152
156
|
_lazyGetRequestContext = reqCtx.getRequestContext;
|
|
153
157
|
if (prerenderStoreInstance === undefined) {
|
|
@@ -174,6 +178,7 @@ async function* yieldFromStore<TEnv>(
|
|
|
174
178
|
if (
|
|
175
179
|
!_deserializeSegments ||
|
|
176
180
|
!_restoreHandles ||
|
|
181
|
+
!_decodeHandles ||
|
|
177
182
|
!_hashParams ||
|
|
178
183
|
!_lazyGetRequestContext
|
|
179
184
|
) {
|
|
@@ -182,11 +187,15 @@ async function* yieldFromStore<TEnv>(
|
|
|
182
187
|
|
|
183
188
|
const segments = await _deserializeSegments(entry.segments);
|
|
184
189
|
|
|
185
|
-
// Replay handle data (same as runtime cache hit path).
|
|
186
|
-
//
|
|
190
|
+
// Replay handle data (same as runtime cache hit path). entry.handles is a
|
|
191
|
+
// Flight-encoded string ("" when none) — decode before restore so
|
|
192
|
+
// Promise/ReactNode handle values are revived, not the corrupted JSON form.
|
|
187
193
|
const handleStore = handleStoreRef ?? _lazyGetRequestContext()?._handleStore;
|
|
188
|
-
if (handleStore) {
|
|
189
|
-
|
|
194
|
+
if (handleStore && entry.handles) {
|
|
195
|
+
const handlesRecord = await _decodeHandles(entry.handles);
|
|
196
|
+
if (handlesRecord) {
|
|
197
|
+
_restoreHandles(handlesRecord, handleStore);
|
|
198
|
+
}
|
|
190
199
|
}
|
|
191
200
|
|
|
192
201
|
state.cacheHit = true;
|
|
@@ -282,6 +291,38 @@ async function* yieldFromStore<TEnv>(
|
|
|
282
291
|
}
|
|
283
292
|
}
|
|
284
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Look up a prerendered (build-time cached) entry for the current route and, on
|
|
296
|
+
* a hit, yield its segments. Returns true when an entry was served (the caller
|
|
297
|
+
* should stop the pipeline) and false on a miss. Intercept navigations consult
|
|
298
|
+
* only the intercept-specific entry (`paramHash + "/i"`); a miss there falls
|
|
299
|
+
* through to the normal pipeline so intercept-resolution can run. Callers must
|
|
300
|
+
* guard on `prerenderStoreInstance` after `ensurePrerenderDeps()`.
|
|
301
|
+
*/
|
|
302
|
+
async function* tryPrerenderLookup<TEnv>(
|
|
303
|
+
ctx: MatchContext<TEnv>,
|
|
304
|
+
state: MatchPipelineState,
|
|
305
|
+
pipelineStart: number,
|
|
306
|
+
handleStoreRef?: HandleStore,
|
|
307
|
+
): AsyncGenerator<ResolvedSegment, boolean> {
|
|
308
|
+
const paramHash = _hashParams!(ctx.matched.params);
|
|
309
|
+
const isPassthroughPrerenderRoute = ctx.entries.some(
|
|
310
|
+
(entry) => entry.type === "route" && entry.isPassthrough === true,
|
|
311
|
+
);
|
|
312
|
+
const lookupHash = ctx.isIntercept ? paramHash + "/i" : paramHash;
|
|
313
|
+
const entry = await prerenderStoreInstance!.get(
|
|
314
|
+
ctx.matched.routeKey,
|
|
315
|
+
lookupHash,
|
|
316
|
+
{
|
|
317
|
+
pathname: ctx.pathname,
|
|
318
|
+
isPassthroughRoute: isPassthroughPrerenderRoute,
|
|
319
|
+
},
|
|
320
|
+
);
|
|
321
|
+
if (!entry) return false;
|
|
322
|
+
yield* yieldFromStore(entry, ctx, state, pipelineStart, handleStoreRef);
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
|
|
285
326
|
/**
|
|
286
327
|
* Async generator middleware type
|
|
287
328
|
*/
|
|
@@ -334,54 +375,13 @@ export function withCacheLookup<TEnv>(
|
|
|
334
375
|
if (!ctx.isAction && !isHmr && ctx.matched.pr) {
|
|
335
376
|
await ensurePrerenderDeps();
|
|
336
377
|
if (prerenderStoreInstance) {
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
378
|
+
const served = yield* tryPrerenderLookup(
|
|
379
|
+
ctx,
|
|
380
|
+
state,
|
|
381
|
+
pipelineStart,
|
|
382
|
+
handleStoreRef,
|
|
340
383
|
);
|
|
341
|
-
|
|
342
|
-
if (ctx.isIntercept) {
|
|
343
|
-
// Intercept navigation: try intercept-specific prerender entry
|
|
344
|
-
const entry = await prerenderStoreInstance.get(
|
|
345
|
-
ctx.matched.routeKey,
|
|
346
|
-
paramHash + "/i",
|
|
347
|
-
{
|
|
348
|
-
pathname: ctx.pathname,
|
|
349
|
-
isPassthroughRoute: isPassthroughPrerenderRoute,
|
|
350
|
-
},
|
|
351
|
-
);
|
|
352
|
-
if (entry) {
|
|
353
|
-
yield* yieldFromStore(
|
|
354
|
-
entry,
|
|
355
|
-
ctx,
|
|
356
|
-
state,
|
|
357
|
-
pipelineStart,
|
|
358
|
-
handleStoreRef,
|
|
359
|
-
);
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
// No intercept prerender -- fall through to normal pipeline
|
|
363
|
-
// (skip non-intercept prerender to let intercept-resolution run)
|
|
364
|
-
} else {
|
|
365
|
-
// Normal navigation: existing behavior
|
|
366
|
-
const entry = await prerenderStoreInstance.get(
|
|
367
|
-
ctx.matched.routeKey,
|
|
368
|
-
paramHash,
|
|
369
|
-
{
|
|
370
|
-
pathname: ctx.pathname,
|
|
371
|
-
isPassthroughRoute: isPassthroughPrerenderRoute,
|
|
372
|
-
},
|
|
373
|
-
);
|
|
374
|
-
if (entry) {
|
|
375
|
-
yield* yieldFromStore(
|
|
376
|
-
entry,
|
|
377
|
-
ctx,
|
|
378
|
-
state,
|
|
379
|
-
pipelineStart,
|
|
380
|
-
handleStoreRef,
|
|
381
|
-
);
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
384
|
+
if (served) return;
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
387
|
|
|
@@ -404,51 +404,13 @@ export function withCacheLookup<TEnv>(
|
|
|
404
404
|
if (hasStatic) {
|
|
405
405
|
await ensurePrerenderDeps();
|
|
406
406
|
if (prerenderStoreInstance) {
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
407
|
+
const served = yield* tryPrerenderLookup(
|
|
408
|
+
ctx,
|
|
409
|
+
state,
|
|
410
|
+
pipelineStart,
|
|
411
|
+
handleStoreRef,
|
|
410
412
|
);
|
|
411
|
-
|
|
412
|
-
if (ctx.isIntercept) {
|
|
413
|
-
const entry = await prerenderStoreInstance.get(
|
|
414
|
-
ctx.matched.routeKey,
|
|
415
|
-
paramHash + "/i",
|
|
416
|
-
{
|
|
417
|
-
pathname: ctx.pathname,
|
|
418
|
-
isPassthroughRoute: isPassthroughPrerenderRoute,
|
|
419
|
-
},
|
|
420
|
-
);
|
|
421
|
-
if (entry) {
|
|
422
|
-
yield* yieldFromStore(
|
|
423
|
-
entry,
|
|
424
|
-
ctx,
|
|
425
|
-
state,
|
|
426
|
-
pipelineStart,
|
|
427
|
-
handleStoreRef,
|
|
428
|
-
);
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
// No intercept prerender -- fall through to normal pipeline
|
|
432
|
-
} else {
|
|
433
|
-
const entry = await prerenderStoreInstance.get(
|
|
434
|
-
ctx.matched.routeKey,
|
|
435
|
-
paramHash,
|
|
436
|
-
{
|
|
437
|
-
pathname: ctx.pathname,
|
|
438
|
-
isPassthroughRoute: isPassthroughPrerenderRoute,
|
|
439
|
-
},
|
|
440
|
-
);
|
|
441
|
-
if (entry) {
|
|
442
|
-
yield* yieldFromStore(
|
|
443
|
-
entry,
|
|
444
|
-
ctx,
|
|
445
|
-
state,
|
|
446
|
-
pipelineStart,
|
|
447
|
-
handleStoreRef,
|
|
448
|
-
);
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
413
|
+
if (served) return;
|
|
452
414
|
}
|
|
453
415
|
}
|
|
454
416
|
}
|
|
@@ -169,10 +169,11 @@ export function withCacheStore<TEnv>(
|
|
|
169
169
|
// skip (client already had them). Segments where the handler intentionally
|
|
170
170
|
// returned null are not revalidation skips — re-rendering them will still
|
|
171
171
|
// produce null, so proactive caching would be wasted work.
|
|
172
|
-
const clientIdSet = new Set(ctx.clientSegmentIds);
|
|
173
172
|
const hasNullComponents = allSegmentsToCache.some(
|
|
174
173
|
(s) =>
|
|
175
|
-
s.component === null &&
|
|
174
|
+
s.component === null &&
|
|
175
|
+
s.type !== "loader" &&
|
|
176
|
+
ctx.clientSegmentSet.has(s.id),
|
|
176
177
|
);
|
|
177
178
|
|
|
178
179
|
const requestCtx = getRequestContext();
|
|
@@ -138,34 +138,38 @@ export async function collectSegments(
|
|
|
138
138
|
function deduplicateLoaderSegments(
|
|
139
139
|
segments: ResolvedSegment[],
|
|
140
140
|
logPrefix: string,
|
|
141
|
-
): ResolvedSegment[] {
|
|
142
|
-
//
|
|
143
|
-
// and
|
|
141
|
+
): { segments: ResolvedSegment[]; removedIds: Set<string> } {
|
|
142
|
+
// Single pass: original (non-inherited) loaderIds, all loaderIds grouped by
|
|
143
|
+
// namespace, and namespaces of segments that declare loading().
|
|
144
144
|
const originalLoaders = new Set<string>();
|
|
145
|
-
const
|
|
145
|
+
const loaderIdsByNamespace = new Map<string, string[]>();
|
|
146
|
+
const namespacesWithLoading = new Set<string>();
|
|
146
147
|
for (const s of segments) {
|
|
147
|
-
if (s.type === "loader" && s.loaderId
|
|
148
|
-
originalLoaders.add(s.loaderId);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
if (s.type === "loader" && s.loaderId) {
|
|
149
|
+
if (!s._inherited) originalLoaders.add(s.loaderId);
|
|
150
|
+
const ids = loaderIdsByNamespace.get(s.namespace);
|
|
151
|
+
if (ids) ids.push(s.loaderId);
|
|
152
|
+
else loaderIdsByNamespace.set(s.namespace, [s.loaderId]);
|
|
153
|
+
} else if (
|
|
154
|
+
s.type !== "loader" &&
|
|
155
|
+
s.loading !== undefined &&
|
|
156
|
+
s.loading !== false
|
|
157
|
+
) {
|
|
158
|
+
namespacesWithLoading.add(s.namespace);
|
|
152
159
|
}
|
|
153
160
|
}
|
|
154
|
-
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
loadersWithLoading.add(l.loaderId);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
161
|
+
|
|
162
|
+
// An inherited loader is needed when it shares a namespace with a
|
|
163
|
+
// loading-bearing segment (its data sits behind that LoaderBoundary).
|
|
164
|
+
const loadersWithLoading = new Set<string>();
|
|
165
|
+
for (const ns of namespacesWithLoading) {
|
|
166
|
+
for (const id of loaderIdsByNamespace.get(ns) ?? []) {
|
|
167
|
+
loadersWithLoading.add(id);
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
170
|
|
|
167
171
|
const result: ResolvedSegment[] = [];
|
|
168
|
-
|
|
172
|
+
const removedIds = new Set<string>();
|
|
169
173
|
|
|
170
174
|
for (const s of segments) {
|
|
171
175
|
if (
|
|
@@ -175,17 +179,20 @@ function deduplicateLoaderSegments(
|
|
|
175
179
|
originalLoaders.has(s.loaderId) &&
|
|
176
180
|
!loadersWithLoading.has(s.loaderId)
|
|
177
181
|
) {
|
|
178
|
-
|
|
182
|
+
removedIds.add(s.id);
|
|
179
183
|
continue;
|
|
180
184
|
}
|
|
181
185
|
result.push(s);
|
|
182
186
|
}
|
|
183
187
|
|
|
184
|
-
if (
|
|
185
|
-
debugLog(
|
|
188
|
+
if (removedIds.size > 0) {
|
|
189
|
+
debugLog(
|
|
190
|
+
logPrefix,
|
|
191
|
+
`deduped ${removedIds.size} inherited loader segment(s)`,
|
|
192
|
+
);
|
|
186
193
|
}
|
|
187
194
|
|
|
188
|
-
return result;
|
|
195
|
+
return { segments: result, removedIds };
|
|
189
196
|
}
|
|
190
197
|
|
|
191
198
|
/**
|
|
@@ -244,7 +251,7 @@ export function buildMatchResult<TEnv>(
|
|
|
244
251
|
);
|
|
245
252
|
}
|
|
246
253
|
|
|
247
|
-
const dedupedSegments = deduplicateLoaderSegments(
|
|
254
|
+
const { segments: dedupedSegments, removedIds } = deduplicateLoaderSegments(
|
|
248
255
|
segmentsToRender,
|
|
249
256
|
logPrefix,
|
|
250
257
|
);
|
|
@@ -262,18 +269,32 @@ export function buildMatchResult<TEnv>(
|
|
|
262
269
|
|
|
263
270
|
// Remove deduped loader IDs from matched so the client doesn't treat
|
|
264
271
|
// them as missing segments and trigger a fallback refetch.
|
|
265
|
-
const removedIds = new Set(
|
|
266
|
-
segmentsToRender
|
|
267
|
-
.filter((s) => !dedupedSegments.includes(s))
|
|
268
|
-
.map((s) => s.id),
|
|
269
|
-
);
|
|
270
272
|
const matchedIds =
|
|
271
273
|
removedIds.size > 0 ? allIds.filter((id) => !removedIds.has(id)) : allIds;
|
|
272
274
|
|
|
275
|
+
// resolvedIds: every segment whose handler actually ran this request.
|
|
276
|
+
// For full-match every segment is fresh; for partial-match we filter by
|
|
277
|
+
// the internal `_handlerRan` flag set in revalidation.ts. Drives the
|
|
278
|
+
// client's handle-bucket cleanup — a slot that re-resolved and pushed
|
|
279
|
+
// nothing must have its previous handle data cleared, but `diff` won't
|
|
280
|
+
// carry it because the segment payload skips null-component cached
|
|
281
|
+
// segments to save bytes.
|
|
282
|
+
const resolvedIds = ctx.isFullMatch
|
|
283
|
+
? allSegments.map((s) => s.id)
|
|
284
|
+
: allSegments.filter((s) => s._handlerRan).map((s) => s.id);
|
|
285
|
+
|
|
286
|
+
// Strip internal-only fields from the segments going on the wire.
|
|
287
|
+
const cleanedSegments = dedupedSegments.map((s) => {
|
|
288
|
+
if (s._handlerRan === undefined) return s;
|
|
289
|
+
const { _handlerRan: _drop, ...rest } = s;
|
|
290
|
+
return rest as ResolvedSegment;
|
|
291
|
+
});
|
|
292
|
+
|
|
273
293
|
return {
|
|
274
|
-
segments:
|
|
294
|
+
segments: cleanedSegments,
|
|
275
295
|
matched: matchedIds,
|
|
276
|
-
diff:
|
|
296
|
+
diff: cleanedSegments.map((s) => s.id),
|
|
297
|
+
resolvedIds,
|
|
277
298
|
params: ctx.matched.params,
|
|
278
299
|
routeName: ctx.routeKey,
|
|
279
300
|
slots: Object.keys(state.slots).length > 0 ? state.slots : undefined,
|
package/src/router/metrics.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
import type { ScopedReverseFunction } from "../reverse.js";
|
|
15
15
|
import type { Theme } from "../theme/types.js";
|
|
16
16
|
import type { LocationStateEntry } from "../browser/react/location-state-shared.js";
|
|
17
|
+
import type { RequestScope } from "../types/request-scope.js";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Get variable function type
|
|
@@ -52,33 +53,15 @@ export interface CookieOptions {
|
|
|
52
53
|
* Context passed to middleware
|
|
53
54
|
*
|
|
54
55
|
* @template TEnv - Environment type (bindings, variables) - defaults to any for internal flexibility
|
|
55
|
-
* @template TParams - URL params type (typed for route middleware,
|
|
56
|
+
* @template TParams - URL params type (typed for route middleware,
|
|
57
|
+
* `Record<string, string | undefined>` for global middleware — absent
|
|
58
|
+
* optional segments are omitted from the params record at runtime, so
|
|
59
|
+
* the index signature must include `undefined`)
|
|
56
60
|
*/
|
|
57
61
|
export interface MiddlewareContext<
|
|
58
62
|
TEnv = any,
|
|
59
|
-
TParams = Record<string, string>,
|
|
60
|
-
> {
|
|
61
|
-
/** Original request */
|
|
62
|
-
request: Request;
|
|
63
|
-
|
|
64
|
-
/** Parsed URL (with internal `_rsc*` params stripped) */
|
|
65
|
-
url: URL;
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* The original request URL with all parameters intact, including
|
|
69
|
-
* internal `_rsc*` transport params.
|
|
70
|
-
*/
|
|
71
|
-
originalUrl: URL;
|
|
72
|
-
|
|
73
|
-
/** URL pathname */
|
|
74
|
-
pathname: string;
|
|
75
|
-
|
|
76
|
-
/** URL search params */
|
|
77
|
-
searchParams: URLSearchParams;
|
|
78
|
-
|
|
79
|
-
/** Platform bindings (Cloudflare, etc.) */
|
|
80
|
-
env: TEnv;
|
|
81
|
-
|
|
63
|
+
TParams = Record<string, string | undefined>,
|
|
64
|
+
> extends RequestScope<TEnv> {
|
|
82
65
|
/** URL params extracted from route/middleware pattern */
|
|
83
66
|
params: TParams;
|
|
84
67
|
|
|
@@ -157,7 +140,7 @@ export interface MiddlewareContext<
|
|
|
157
140
|
* @template TEnv - Environment type - defaults to any for internal flexibility
|
|
158
141
|
* @template TParams - URL params type (typed for route middleware)
|
|
159
142
|
*
|
|
160
|
-
* When using middleware with global augmentation (
|
|
143
|
+
* When using middleware with global augmentation (Rango.Env), explicitly
|
|
161
144
|
* annotate your middleware functions, or the types will be inferred from context:
|
|
162
145
|
*
|
|
163
146
|
* @example
|
|
@@ -169,7 +152,10 @@ export interface MiddlewareContext<
|
|
|
169
152
|
* router.use((ctx, next) => {...}) // ctx is typed from router's TEnv
|
|
170
153
|
* ```
|
|
171
154
|
*/
|
|
172
|
-
export type MiddlewareFn<
|
|
155
|
+
export type MiddlewareFn<
|
|
156
|
+
TEnv = any,
|
|
157
|
+
TParams = Record<string, string | undefined>,
|
|
158
|
+
> = (
|
|
173
159
|
ctx: MiddlewareContext<TEnv, TParams>,
|
|
174
160
|
next: () => Promise<Response>,
|
|
175
161
|
) => Response | void | Promise<Response | void>;
|
|
@@ -216,5 +202,8 @@ export interface MiddlewareCollectableEntry {
|
|
|
216
202
|
*/
|
|
217
203
|
export interface CollectedMiddleware {
|
|
218
204
|
handler: MiddlewareFn<any, any>;
|
|
205
|
+
// Internal shape only. The user-facing `MiddlewareContext.params` is
|
|
206
|
+
// typed `Record<string, string | undefined>` to reflect that absent
|
|
207
|
+
// optional segments are omitted from the params record at runtime.
|
|
219
208
|
params: Record<string, string>;
|
|
220
209
|
}
|