@rangojs/router 0.24.2 → 0.24.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/types/client-urls/client-root.d.ts +4 -1
- package/dist/types/client-urls/client-urls.d.ts +22 -0
- package/dist/types/client-urls/layout-loader-copies.d.ts +11 -0
- package/dist/types/client-urls/navigation.d.ts +6 -8
- package/dist/types/client-urls/server-projection.d.ts +16 -0
- package/dist/types/client-urls/types.d.ts +6 -1
- package/dist/types/handles/pushed-by.d.ts +7 -0
- package/dist/types/router/substitute-pattern-params.d.ts +16 -0
- package/dist/types/server/context.d.ts +8 -0
- package/dist/types/urls/path-helper.d.ts +5 -0
- package/dist/vite/index.js +9 -3
- package/package.json +1 -1
- package/skills/client-urls/SKILL.md +40 -7
- package/skills/loader/SKILL.md +7 -4
- package/src/browser/navigation-bridge.ts +7 -14
- package/src/browser/react/use-reverse.ts +4 -17
- package/src/browser/segment-reconciler.ts +15 -1
- package/src/client-urls/client-root.tsx +10 -5
- package/src/client-urls/client-urls.ts +44 -13
- package/src/client-urls/layout-loader-copies.ts +56 -0
- package/src/client-urls/navigation.ts +45 -38
- package/src/client-urls/server-projection.ts +205 -15
- package/src/client-urls/types.ts +10 -1
- package/src/handles/pushed-by.ts +29 -4
- package/src/router/match-api.ts +11 -3
- package/src/router/segment-resolution/revalidation.ts +4 -1
- package/src/router/substitute-pattern-params.ts +34 -0
- package/src/server/context.ts +48 -23
- package/src/ssr/index.tsx +63 -7
- package/src/urls/path-helper.ts +16 -3
package/src/server/context.ts
CHANGED
|
@@ -445,6 +445,53 @@ function bumpCounter(store: HelperContext, key: string): number {
|
|
|
445
445
|
return store.counters[key]++;
|
|
446
446
|
}
|
|
447
447
|
|
|
448
|
+
type ShortCodeType = "layout" | "parallel" | "route" | "loader" | "cache";
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* The next shortCode of `type` under the store's parent, which `take`
|
|
452
|
+
* consumes; a peek may look `ahead` past it. A root entry is prefixed with the
|
|
453
|
+
* mount index and counted per mount. A child is counted per parent with
|
|
454
|
+
* includeScope appended: while a lazy include's direct children evaluate,
|
|
455
|
+
* includeScope is a per-include token like "I0" / "I1I0" that partitions the
|
|
456
|
+
* parent's counter namespace so routes inside one include cannot collide with
|
|
457
|
+
* siblings declared outside it.
|
|
458
|
+
*/
|
|
459
|
+
function nextShortCode(
|
|
460
|
+
store: HelperContext,
|
|
461
|
+
type: ShortCodeType,
|
|
462
|
+
take: boolean,
|
|
463
|
+
ahead = 0,
|
|
464
|
+
): string {
|
|
465
|
+
const parent = store.parent;
|
|
466
|
+
const prefix = SHORT_CODE_PREFIX[type];
|
|
467
|
+
const mountPrefix =
|
|
468
|
+
store.mountIndex !== undefined ? `M${store.mountIndex}` : "";
|
|
469
|
+
const includeScope = store.includeScope ?? "";
|
|
470
|
+
const counterKey = parent
|
|
471
|
+
? `${parent.shortCode}${includeScope}_${type}`
|
|
472
|
+
: mountPrefix
|
|
473
|
+
? `${mountPrefix}_root_${type}`
|
|
474
|
+
: `root_${type}`;
|
|
475
|
+
const index = take
|
|
476
|
+
? bumpCounter(store, counterKey)
|
|
477
|
+
: (store.counters[counterKey] ?? 0) + ahead;
|
|
478
|
+
return parent
|
|
479
|
+
? `${parent.shortCode}${includeScope}${prefix}${index}`
|
|
480
|
+
: `${mountPrefix}${prefix}${index}`;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* The shortCode the `ahead`-th next `getShortCode(type)` call in this DSL
|
|
485
|
+
* context returns, without taking it. clientUrls() materialization reads the
|
|
486
|
+
* code of every group route this way, including the routes an evaluation for
|
|
487
|
+
* another route skips (client-urls/layout-loader-copies.ts).
|
|
488
|
+
*/
|
|
489
|
+
export function peekShortCode(type: ShortCodeType, ahead: number): string {
|
|
490
|
+
const store = RangoContext.getStore();
|
|
491
|
+
invariant(store, "No context RangoContext available");
|
|
492
|
+
return nextShortCode(store, type, false, ahead);
|
|
493
|
+
}
|
|
494
|
+
|
|
448
495
|
export const getContext = (): {
|
|
449
496
|
context: AsyncLocalStorage<HelperContext>;
|
|
450
497
|
getStore: () => HelperContext;
|
|
@@ -522,29 +569,7 @@ export const getContext = (): {
|
|
|
522
569
|
) => {
|
|
523
570
|
const store = context.getStore();
|
|
524
571
|
invariant(store, "No context RangoContext available");
|
|
525
|
-
|
|
526
|
-
const parent = store.parent;
|
|
527
|
-
const prefix = SHORT_CODE_PREFIX[type];
|
|
528
|
-
const mountPrefix =
|
|
529
|
-
store.mountIndex !== undefined ? `M${store.mountIndex}` : "";
|
|
530
|
-
|
|
531
|
-
const includeScope = store.includeScope ?? "";
|
|
532
|
-
|
|
533
|
-
if (!parent) {
|
|
534
|
-
// Root entry: prefix with mount index and use mount-scoped counter
|
|
535
|
-
const counterKey = mountPrefix
|
|
536
|
-
? `${mountPrefix}_root_${type}`
|
|
537
|
-
: `root_${type}`;
|
|
538
|
-
return `${mountPrefix}${prefix}${bumpCounter(store, counterKey)}`;
|
|
539
|
-
} else {
|
|
540
|
-
// Child entry: use parent-scoped counter with includeScope appended.
|
|
541
|
-
// When we're evaluating a lazy include's direct children, includeScope
|
|
542
|
-
// is a per-include token like "I0" / "I1I0" that partitions the
|
|
543
|
-
// parent's counter namespace so routes inside one include cannot
|
|
544
|
-
// collide with siblings declared outside it.
|
|
545
|
-
const counterKey = `${parent.shortCode}${includeScope}_${type}`;
|
|
546
|
-
return `${parent.shortCode}${includeScope}${prefix}${bumpCounter(store, counterKey)}`;
|
|
547
|
-
}
|
|
572
|
+
return nextShortCode(store, type, true);
|
|
548
573
|
},
|
|
549
574
|
runWithStore: <T>(
|
|
550
575
|
store: HelperContext,
|
package/src/ssr/index.tsx
CHANGED
|
@@ -403,6 +403,62 @@ function trackDocumentClientLoads<T>(decode: () => T): {
|
|
|
403
403
|
};
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
+
/**
|
|
407
|
+
* Cap on settleSsrFalseFirstFlush's turns. Each turn retries one generation of
|
|
408
|
+
* tasks parked on thenables that settle in microtasks. A useActionState form
|
|
409
|
+
* is one generation (the Flight client caches the encoding, so the retry
|
|
410
|
+
* encodes synchronously); a formAction button inside it renders on that retry
|
|
411
|
+
* and is a second; the same form behind `lazy()` adds a third. ssr-handler
|
|
412
|
+
* tests pin all three. A deeper chain flushes its fallback as before.
|
|
413
|
+
*/
|
|
414
|
+
const SSR_FALSE_FIRST_FLUSH_TURNS = 3;
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* True when the matched chain has a `loader(Def, { ssr: false })` entry: its
|
|
418
|
+
* payload segments carry `awaitBeforeFlush` (fresh.ts).
|
|
419
|
+
*/
|
|
420
|
+
function hasAwaitBeforeFlush(payload: {
|
|
421
|
+
metadata?: { segments?: readonly { awaitBeforeFlush?: true }[] };
|
|
422
|
+
}): boolean {
|
|
423
|
+
return payload.metadata?.segments?.some((s) => s.awaitBeforeFlush) === true;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* ssr: false documents only: before the first flush, give Fizz the turns a
|
|
428
|
+
* task waiting on an already-settling thenable needs, so a boundary that
|
|
429
|
+
* waits on nothing else completes in place (#1138).
|
|
430
|
+
*
|
|
431
|
+
* Fizz's useActionState binds its state onto the action, and the
|
|
432
|
+
* `<form action>` encode of that bound server reference throws the Flight
|
|
433
|
+
* client's encodeFormData thenable once (defaultEncodeFormAction). It settles
|
|
434
|
+
* in microtasks, but once the request has left its opening status, Fizz's
|
|
435
|
+
* pingTask retries the task on a setTimeout(0) turn. renderToReadableStream
|
|
436
|
+
* resolves at shell-ready, so the first read would flush the boundary's
|
|
437
|
+
* fallback before that turn, which a browser without JavaScript keeps (the
|
|
438
|
+
* form lands in `<div hidden id="S:n">`).
|
|
439
|
+
*
|
|
440
|
+
* A document already whole (allReady settled) returns with no macrotask.
|
|
441
|
+
* Otherwise one setTimeout(0) at a time until allReady, at most
|
|
442
|
+
* SSR_FALSE_FIRST_FLUSH_TURNS: a timer set after Fizz's ping fires after it.
|
|
443
|
+
* A boundary waiting on I/O (an unflagged loader, a streamed promise) still
|
|
444
|
+
* streams its fallback, after at most that many turns.
|
|
445
|
+
*/
|
|
446
|
+
async function settleSsrFalseFirstFlush(
|
|
447
|
+
allReady: Promise<void>,
|
|
448
|
+
): Promise<void> {
|
|
449
|
+
let whole = false;
|
|
450
|
+
const settle = (): void => {
|
|
451
|
+
whole = true;
|
|
452
|
+
};
|
|
453
|
+
// Promise.resolve: a stub renderer's stream may carry no allReady.
|
|
454
|
+
Promise.resolve(allReady).then(settle, settle);
|
|
455
|
+
// A settled allReady's reaction was queued before this continuation's.
|
|
456
|
+
await Promise.resolve();
|
|
457
|
+
for (let turn = 0; !whole && turn < SSR_FALSE_FIRST_FLUSH_TURNS; turn++) {
|
|
458
|
+
await macrotask();
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
406
462
|
/**
|
|
407
463
|
* Route an SSR error through the deps.onError notification callback with the
|
|
408
464
|
* "rendering" phase. Swallows callback failures so a broken reporter never
|
|
@@ -832,13 +888,10 @@ export function createSSRHandler<TEnv = unknown>(deps: SSRDependencies<TEnv>) {
|
|
|
832
888
|
// Awaiting the payload here is latency-neutral: fizz cannot emit even
|
|
833
889
|
// <html> until this same promise settles. A rejection lands in the
|
|
834
890
|
// catch below — the same reportRenderError path a shell error takes.
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
progressiveChunkSize = Number.MAX_SAFE_INTEGER;
|
|
840
|
-
}
|
|
841
|
-
}
|
|
891
|
+
const awaitsBeforeFlush = hasAwaitBeforeFlush(await payload);
|
|
892
|
+
const progressiveChunkSize =
|
|
893
|
+
deps.progressiveChunkSize ??
|
|
894
|
+
(awaitsBeforeFlush ? Number.MAX_SAFE_INTEGER : undefined);
|
|
842
895
|
|
|
843
896
|
// Render React tree to HTML stream
|
|
844
897
|
// Pass formState for useActionState progressive enhancement if provided
|
|
@@ -870,6 +923,9 @@ export function createSSRHandler<TEnv = unknown>(deps: SSRDependencies<TEnv>) {
|
|
|
870
923
|
// cannot process streamed HTML.
|
|
871
924
|
if (streamMode === "allReady") {
|
|
872
925
|
await htmlStream.allReady;
|
|
926
|
+
} else if (awaitsBeforeFlush) {
|
|
927
|
+
// The shell rendered from the payload, so it has settled.
|
|
928
|
+
await settleSsrFalseFirstFlush(htmlStream.allReady);
|
|
873
929
|
}
|
|
874
930
|
|
|
875
931
|
// The entry's Fizz hint at default priority, after the head chunk
|
package/src/urls/path-helper.ts
CHANGED
|
@@ -54,6 +54,21 @@ function applyNamePrefix(prefix: string | undefined, name: string): string {
|
|
|
54
54
|
return `${prefix}.${name}`;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* The key path() registers a route under: its name, or a `$path_` name built
|
|
59
|
+
* from its pattern, behind the include's name prefix.
|
|
60
|
+
*/
|
|
61
|
+
export function routeKeyOf(
|
|
62
|
+
namePrefix: string | undefined,
|
|
63
|
+
pattern: string,
|
|
64
|
+
name: string | undefined,
|
|
65
|
+
): string {
|
|
66
|
+
return applyNamePrefix(
|
|
67
|
+
namePrefix,
|
|
68
|
+
name || `$path_${pattern.replace(/[/:*?]/g, "_")}`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
57
72
|
function resolveResponseType(
|
|
58
73
|
options: PathOptions | undefined,
|
|
59
74
|
): string | undefined {
|
|
@@ -143,12 +158,10 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
143
158
|
|
|
144
159
|
const prefixedPattern = applyUrlPrefix(urlPrefix, pattern);
|
|
145
160
|
|
|
146
|
-
const localName =
|
|
147
|
-
options?.name || `$path_${pattern.replace(/[/:*?]/g, "_")}`;
|
|
148
161
|
if (options?.name) {
|
|
149
162
|
validateUserRouteName(options.name);
|
|
150
163
|
}
|
|
151
|
-
const routeName =
|
|
164
|
+
const routeName = routeKeyOf(namePrefix, pattern, options?.name);
|
|
152
165
|
|
|
153
166
|
const namespace = `${ctx.namespace}.${store.getNextIndex("route")}.${routeName}`;
|
|
154
167
|
|