@rangojs/router 0.18.0 → 0.19.1
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/browser/event-controller.d.ts +3 -3
- package/dist/types/browser/history-state.d.ts +2 -0
- package/dist/types/browser/navigation-transaction.d.ts +20 -2
- package/dist/types/browser/partial-update.d.ts +16 -1
- package/dist/types/browser/prefetch/cache.d.ts +6 -6
- package/dist/types/browser/react/Link.d.ts +19 -9
- package/dist/types/browser/react/context.d.ts +2 -2
- package/dist/types/browser/react/location-state-shared.d.ts +95 -56
- package/dist/types/browser/react/location-state.d.ts +1 -1
- package/dist/types/browser/transition-when.d.ts +80 -0
- package/dist/types/browser/types.d.ts +104 -9
- package/dist/types/build/generate-manifest.d.ts +8 -0
- package/dist/types/cache/background-task.d.ts +24 -2
- package/dist/types/cache/cache-exec-scope.d.ts +37 -0
- package/dist/types/cache/cache-key-utils.d.ts +44 -13
- package/dist/types/cache/cache-policy.d.ts +1 -1
- package/dist/types/cache/cache-runtime.d.ts +7 -0
- package/dist/types/cache/cache-scope.d.ts +117 -35
- package/dist/types/cache/cache-tag.d.ts +48 -5
- package/dist/types/cache/cf/cf-cache-store.d.ts +55 -44
- package/dist/types/cache/cf/cf-shell-frame.d.ts +1 -1
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +0 -15
- package/dist/types/cache/document-cache.d.ts +10 -0
- package/dist/types/cache/handle-snapshot.d.ts +40 -8
- package/dist/types/cache/invalidation-order.d.ts +23 -0
- package/dist/types/cache/request-tag-mask.d.ts +54 -0
- package/dist/types/cache/shell-snapshot.d.ts +88 -141
- package/dist/types/cache/tag-invalidation.d.ts +38 -14
- package/dist/types/cache/types.d.ts +68 -61
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +23 -16
- package/dist/types/client-urls/navigation.d.ts +28 -2
- package/dist/types/client-urls/server-projection.d.ts +5 -2
- package/dist/types/client-urls/types.d.ts +5 -5
- package/dist/types/client.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/route-definition/helpers-types.d.ts +11 -11
- package/dist/types/route-name.d.ts +7 -0
- package/dist/types/router/error-handling.d.ts +1 -1
- package/dist/types/router/intercept-resolution.d.ts +2 -0
- package/dist/types/router/match-context.d.ts +5 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +0 -4
- package/dist/types/router/router-options.d.ts +4 -1
- package/dist/types/router/segment-resolution/loader-cache.d.ts +18 -1
- package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +14 -40
- package/dist/types/router/segment-resolution/mask-nested.d.ts +11 -3
- package/dist/types/router/segment-resolution/view-transition-default.d.ts +7 -5
- package/dist/types/router/transition-when.d.ts +24 -10
- package/dist/types/rsc/attach-transition-when.d.ts +18 -0
- package/dist/types/rsc/full-payload.d.ts +14 -7
- package/dist/types/rsc/response-cache-serve.d.ts +15 -12
- package/dist/types/rsc/shell-build-manifest.d.ts +4 -3
- package/dist/types/rsc/shell-capture-constants.d.ts +77 -20
- package/dist/types/rsc/shell-capture.d.ts +78 -81
- package/dist/types/rsc/shell-serve.d.ts +19 -48
- package/dist/types/rsc/types.d.ts +2 -0
- package/dist/types/segment-system.d.ts +7 -0
- package/dist/types/server/context.d.ts +126 -61
- package/dist/types/server/cookie-store.d.ts +30 -18
- package/dist/types/server/handle-store.d.ts +52 -1
- package/dist/types/server/request-context.d.ts +66 -45
- package/dist/types/server/request-headers.d.ts +44 -0
- package/dist/types/server.d.ts +1 -0
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/index.d.ts +3 -2
- package/dist/types/testing/internal/action-id.d.ts +7 -0
- package/dist/types/testing/location-state-key.d.ts +24 -0
- package/dist/types/testing/render-route.d.ts +17 -1
- package/dist/types/testing/run-transition-when.d.ts +44 -69
- package/dist/types/testing/serve-shell-request.d.ts +7 -6
- package/dist/types/testing/shell-status.d.ts +34 -22
- package/dist/types/transition-when-ref.d.ts +79 -0
- package/dist/types/types/cache-types.d.ts +15 -3
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/segments.d.ts +86 -71
- package/dist/types/urls/path-helper-types.d.ts +3 -4
- package/dist/types/urls/pattern-types.d.ts +3 -4
- package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
- package/dist/vite/index.js +962 -97
- package/package.json +1 -1
- package/skills/cache-guide/SKILL.md +76 -49
- package/skills/caching/SKILL.md +167 -69
- package/skills/client-urls/SKILL.md +1 -1
- package/skills/comparison/references/framework-comparison.md +8 -5
- package/skills/document-cache/SKILL.md +24 -0
- package/skills/hooks/SKILL.md +3 -1
- package/skills/hooks/navigation.md +23 -2
- package/skills/hooks/state.md +98 -6
- package/skills/intercept/SKILL.md +29 -12
- package/skills/loader/SKILL.md +52 -21
- package/skills/middleware/SKILL.md +18 -0
- package/skills/migrate-nextjs/SKILL.md +3 -3
- package/skills/observability/SKILL.md +6 -5
- package/skills/parallel/SKILL.md +1 -1
- package/skills/ppr/SKILL.md +129 -77
- package/skills/prerender/SKILL.md +3 -3
- package/skills/rango/SKILL.md +25 -6
- package/skills/route/SKILL.md +2 -0
- package/skills/testing/SKILL.md +27 -25
- package/skills/testing/cache-prerender.md +9 -9
- package/skills/testing/client-components.md +45 -3
- package/skills/testing/loader.md +3 -2
- package/skills/testing/middleware.md +1 -0
- package/skills/testing/predicates.md +49 -49
- package/skills/testing/render-handler.md +1 -0
- package/skills/testing/serve-shell-request.md +14 -0
- package/skills/testing/server-actions.md +9 -3
- package/skills/theme/SKILL.md +8 -4
- package/skills/typesafety/env-and-bindings.md +8 -0
- package/skills/use-cache/SKILL.md +67 -19
- package/skills/view-transitions/SKILL.md +150 -29
- package/src/__augment-tests__/augmented.check.ts +23 -0
- package/src/browser/event-controller.ts +4 -4
- package/src/browser/history-state.ts +52 -1
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +77 -18
- package/src/browser/navigation-client.ts +4 -3
- package/src/browser/navigation-store.ts +43 -1
- package/src/browser/navigation-transaction.ts +31 -11
- package/src/browser/partial-update.ts +146 -35
- package/src/browser/prefetch/cache.ts +6 -6
- package/src/browser/prefetch/fetch.ts +2 -2
- package/src/browser/react/Link.tsx +38 -33
- package/src/browser/react/NavigationProvider.tsx +2 -2
- package/src/browser/react/context.ts +2 -2
- package/src/browser/react/location-state-shared.ts +241 -49
- package/src/browser/react/location-state.ts +5 -2
- package/src/browser/rsc-router.tsx +4 -0
- package/src/browser/server-action-bridge.ts +84 -15
- package/src/browser/transition-when.ts +205 -0
- package/src/browser/types.ts +124 -10
- package/src/build/generate-manifest.ts +77 -1
- package/src/cache/background-task.ts +55 -2
- package/src/cache/cache-exec-scope.ts +54 -1
- package/src/cache/cache-key-utils.ts +56 -16
- package/src/cache/cache-policy.ts +9 -4
- package/src/cache/cache-runtime.ts +202 -144
- package/src/cache/cache-scope.ts +304 -112
- package/src/cache/cache-tag.ts +209 -13
- package/src/cache/cf/cf-cache-store.ts +152 -76
- package/src/cache/cf/cf-shell-frame.ts +2 -2
- package/src/cache/cf/cf-tag-marker-memo.ts +11 -100
- package/src/cache/document-cache.ts +53 -7
- package/src/cache/handle-snapshot.ts +52 -9
- package/src/cache/invalidation-order.ts +84 -0
- package/src/cache/request-tag-mask.ts +147 -0
- package/src/cache/shell-snapshot.ts +130 -348
- package/src/cache/tag-invalidation.ts +57 -51
- package/src/cache/types.ts +71 -67
- package/src/cache/vercel/vercel-cache-store.ts +61 -58
- package/src/client-urls/client-root.tsx +8 -4
- package/src/client-urls/client-urls.ts +8 -6
- package/src/client-urls/navigation.ts +55 -4
- package/src/client-urls/server-projection.ts +31 -10
- package/src/client-urls/types.ts +12 -5
- package/src/client.tsx +2 -0
- package/src/host/cookie-handler.ts +2 -1
- package/src/index.rsc.ts +3 -0
- package/src/index.ts +3 -0
- package/src/loader-redirect.tsx +7 -3
- package/src/prerender/build-shell-capture.ts +155 -146
- package/src/route-definition/dsl-helpers.ts +2 -0
- package/src/route-definition/helpers-types.ts +11 -11
- package/src/route-name.ts +12 -0
- package/src/router/content-negotiation.ts +4 -1
- package/src/router/error-handling.ts +5 -2
- package/src/router/handler-context.ts +10 -7
- package/src/router/intercept-resolution.ts +11 -1
- package/src/router/loader-resolution.ts +42 -5
- package/src/router/logging.ts +5 -3
- package/src/router/match-api.ts +62 -34
- package/src/router/match-context.ts +5 -0
- package/src/router/match-handlers.ts +9 -29
- package/src/router/match-middleware/background-revalidation.ts +3 -5
- package/src/router/match-middleware/cache-lookup.ts +35 -19
- package/src/router/match-middleware/cache-store.ts +15 -9
- package/src/router/match-result.ts +1 -0
- package/src/router/middleware.ts +2 -5
- package/src/router/navigation-snapshot.ts +6 -5
- package/src/router/prerender-match.ts +4 -0
- package/src/router/request-classification.ts +7 -4
- package/src/router/router-options.ts +4 -1
- package/src/router/segment-resolution/fresh.ts +0 -4
- package/src/router/segment-resolution/loader-cache.ts +152 -26
- package/src/router/segment-resolution/loader-mask.ts +3 -4
- package/src/router/segment-resolution/loader-snapshot.ts +15 -78
- package/src/router/segment-resolution/mask-nested.ts +47 -22
- package/src/router/segment-resolution/revalidation.ts +0 -3
- package/src/router/segment-resolution/view-transition-default.ts +6 -21
- package/src/router/telemetry.ts +6 -3
- package/src/router/transition-when.ts +34 -52
- package/src/router.ts +2 -1
- package/src/rsc/attach-transition-when.ts +47 -0
- package/src/rsc/full-payload.ts +30 -10
- package/src/rsc/handler.ts +12 -10
- package/src/rsc/loader-fetch.ts +2 -1
- package/src/rsc/manifest-init.ts +4 -0
- package/src/rsc/origin-guard.ts +8 -5
- package/src/rsc/progressive-enhancement.ts +7 -30
- package/src/rsc/response-cache-serve.ts +68 -80
- package/src/rsc/response-route-handler.ts +2 -2
- package/src/rsc/rsc-rendering.ts +206 -252
- package/src/rsc/server-action.ts +8 -20
- package/src/rsc/shell-build-manifest.ts +22 -20
- package/src/rsc/shell-capture-constants.ts +136 -20
- package/src/rsc/shell-capture.ts +352 -589
- package/src/rsc/shell-serve.ts +73 -109
- package/src/rsc/ssr-setup.ts +3 -2
- package/src/rsc/types.ts +2 -0
- package/src/segment-system.tsx +40 -7
- package/src/server/context.ts +263 -124
- package/src/server/cookie-store.ts +153 -127
- package/src/server/handle-store.ts +245 -27
- package/src/server/request-context.ts +134 -87
- package/src/server/request-headers.ts +134 -0
- package/src/server.ts +4 -0
- package/src/testing/dispatch.ts +4 -7
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/internal/action-id.ts +25 -0
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/location-state-key.ts +45 -0
- package/src/testing/render-handler.ts +1 -1
- package/src/testing/render-route.tsx +109 -16
- package/src/testing/run-client-revalidate.ts +3 -19
- package/src/testing/run-loader.ts +5 -1
- package/src/testing/run-transition-when.ts +109 -172
- package/src/testing/serve-shell-request.ts +10 -7
- package/src/testing/shell-status.ts +60 -56
- package/src/transition-when-ref.ts +220 -0
- package/src/types/cache-types.ts +15 -3
- package/src/types/index.ts +3 -0
- package/src/types/segments.ts +94 -91
- package/src/urls/path-helper-types.ts +3 -4
- package/src/urls/pattern-types.ts +3 -4
- package/src/vite/discovery/discover-routers.ts +3 -0
- package/src/vite/discovery/shell-prerender-phase.ts +0 -2
- package/src/vite/discovery/virtual-module-codegen.ts +16 -6
- package/src/vite/plugins/transition-when-hoist.ts +1064 -0
- package/src/vite/rango.ts +2 -0
- package/src/vite/router-discovery.ts +75 -5
- package/dist/types/rsc/transition-gate.d.ts +0 -23
- package/src/rsc/transition-gate.ts +0 -86
package/src/cache/cache-tag.ts
CHANGED
|
@@ -17,14 +17,46 @@ import {
|
|
|
17
17
|
} from "../server/request-context.js";
|
|
18
18
|
import {
|
|
19
19
|
getCurrentLoaderBodyId,
|
|
20
|
+
getLoaderRunIdentity,
|
|
20
21
|
getLoaderBodyTags,
|
|
21
22
|
isInsideLoaderScope,
|
|
23
|
+
useCacheLoaderIdentityError,
|
|
22
24
|
type LoaderIdentityRead,
|
|
23
25
|
type LoaderIdentityReadVerb,
|
|
26
|
+
type LoaderRunIdentity,
|
|
24
27
|
} from "../server/context.js";
|
|
28
|
+
import {
|
|
29
|
+
getCacheExecScope,
|
|
30
|
+
isInsideIdentityExempt,
|
|
31
|
+
type CacheExecScope,
|
|
32
|
+
} from "./cache-exec-scope.js";
|
|
33
|
+
import type { ExecutionStart } from "./tag-invalidation.js";
|
|
25
34
|
|
|
26
35
|
const cacheTagStorage = new AsyncLocalStorage<Set<string>>();
|
|
27
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Each "use cache" tag scope's enclosing scope when it opened (#980). A tag
|
|
39
|
+
* recorded in a scope goes to every scope above it: an intermediate call can
|
|
40
|
+
* return a value holding a nested call still running, whose tags land after
|
|
41
|
+
* the intermediate call reported its own to its caller. Its caller's encode
|
|
42
|
+
* awaits the same promise, so the tags are in before it reads its own.
|
|
43
|
+
*/
|
|
44
|
+
const scopeParents = new WeakMap<Set<string>, Set<string>>();
|
|
45
|
+
|
|
46
|
+
/** Add `tag` to `scope` and every scope enclosing it. */
|
|
47
|
+
function addToScopes(scope: Set<string> | undefined, tag: string): void {
|
|
48
|
+
for (let s = scope; s; s = scopeParents.get(s)) s.add(tag);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Call `fn` outside any "use cache" tag scope: detached work a cached body
|
|
53
|
+
* schedules (a stale entry's background refresh) records nothing onto the
|
|
54
|
+
* entry being filled.
|
|
55
|
+
*/
|
|
56
|
+
export function outsideCacheTagScope<T>(fn: () => T): T {
|
|
57
|
+
return cacheTagStorage.exit(fn);
|
|
58
|
+
}
|
|
59
|
+
|
|
28
60
|
/**
|
|
29
61
|
* Tag owners (#957). A route cache() record replays its segments without
|
|
30
62
|
* running their handlers or re-rendering their server components, so the
|
|
@@ -287,7 +319,7 @@ export function cacheTag(...tags: string[]): void {
|
|
|
287
319
|
}
|
|
288
320
|
continue;
|
|
289
321
|
}
|
|
290
|
-
store
|
|
322
|
+
addToScopes(store, normalized);
|
|
291
323
|
}
|
|
292
324
|
return;
|
|
293
325
|
}
|
|
@@ -457,21 +489,172 @@ export function recordedIdentityRead(
|
|
|
457
489
|
return undefined;
|
|
458
490
|
}
|
|
459
491
|
|
|
460
|
-
/**
|
|
461
|
-
|
|
462
|
-
|
|
492
|
+
/**
|
|
493
|
+
* Loader value identity (#1011). Each loader execution's value promise maps to
|
|
494
|
+
* its LoaderRunIdentity (server/context.ts); a loader-cache binding's value
|
|
495
|
+
* carries its reads on its recorded-tag set instead (tagLoaderValue,
|
|
496
|
+
* markIdentityRead). On globalThis like identityReads: the executor that
|
|
497
|
+
* tracks a value and the reader that checks it must share the map.
|
|
498
|
+
*/
|
|
499
|
+
const loaderRuns: WeakMap<object, LoaderRunIdentity> = ((globalThis as any)[
|
|
500
|
+
Symbol.for("rangojs-router:loader-runs")
|
|
501
|
+
] ??= new WeakMap());
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* `value` is the promise of the execution `run` records, started by the
|
|
505
|
+
* current execution (if any), which reads it. Both loader runners call it
|
|
506
|
+
* (loader-resolution.ts createLoaderExecutor, request-context.ts
|
|
507
|
+
* createUseFunction).
|
|
508
|
+
*/
|
|
509
|
+
export function trackLoaderRun(value: object, run: LoaderRunIdentity): void {
|
|
510
|
+
loaderRuns.set(value, run);
|
|
511
|
+
linkLoaderRun(value);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/** The current loader execution reads `value`: its record links the value's. */
|
|
515
|
+
function linkLoaderRun(value: object): void {
|
|
516
|
+
const reader = getLoaderRunIdentity();
|
|
517
|
+
const run = loaderRuns.get(value);
|
|
518
|
+
if (reader && run && reader !== run) (reader.reads ??= new Set()).add(run);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* The identity read behind a loader value: its execution's or one it read
|
|
523
|
+
* (nearest first), or the one its recorded-tag set links to.
|
|
524
|
+
*/
|
|
525
|
+
function loaderValueIdentityRead(
|
|
526
|
+
value: object,
|
|
527
|
+
): LoaderIdentityRead | undefined {
|
|
528
|
+
const root = loaderRuns.get(value);
|
|
529
|
+
if (root) {
|
|
530
|
+
const queue = [root];
|
|
531
|
+
const seen = new Set<LoaderRunIdentity>();
|
|
532
|
+
for (let i = 0; i < queue.length; i++) {
|
|
533
|
+
const run = queue[i]!;
|
|
534
|
+
if (seen.has(run)) continue;
|
|
535
|
+
seen.add(run);
|
|
536
|
+
if (run.read) return run.read;
|
|
537
|
+
for (const linked of run.reads ?? []) queue.push(linked);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
const tags = loaderValueTags.get(value);
|
|
541
|
+
return tags && recordedIdentityRead(tags);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* A ctx.use(Loader) or getRequestContext().use(Loader) read that returns an
|
|
546
|
+
* already-started value (the request memo, a loader cache() binding's value).
|
|
547
|
+
* The current loader execution, if any, links it (it reads the value).
|
|
548
|
+
* Inside a "use cache" body the value is refused when its execution read
|
|
549
|
+
* request identity, as a body entered there would have been
|
|
550
|
+
* (guardIdentityRead). The check runs once the value settles, so a read the
|
|
551
|
+
* body makes after an await counts. Each enclosing execution also notes the
|
|
552
|
+
* value, and checks it again after encoding its result
|
|
553
|
+
* (assertLoaderReadsClean): a read that settles after the value (a nested
|
|
554
|
+
* promise in it) then fails the write instead.
|
|
555
|
+
*/
|
|
556
|
+
export function readStartedLoaderValue<T>(
|
|
557
|
+
value: Promise<T>,
|
|
558
|
+
loaderId: string,
|
|
559
|
+
): Promise<T> {
|
|
560
|
+
linkLoaderRun(value);
|
|
561
|
+
const scope = getCacheExecScope();
|
|
562
|
+
if (!scope || isInsideIdentityExempt()) return value;
|
|
563
|
+
for (let s: CacheExecScope | undefined = scope; s; s = s.parent) {
|
|
564
|
+
(s.loaderReads ??= new Map()).set(value, loaderId);
|
|
565
|
+
}
|
|
566
|
+
const checked = value.then((resolved) => {
|
|
567
|
+
assertLoaderValueClean(value, loaderId);
|
|
568
|
+
return resolved;
|
|
569
|
+
});
|
|
570
|
+
// A read the body never awaits must not crash the process as an unhandled
|
|
571
|
+
// rejection; the write check (assertLoaderReadsClean) still refuses it.
|
|
572
|
+
checked.catch(() => {});
|
|
573
|
+
return checked;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/** Throw when a loader value `scope` read carries an identity read. */
|
|
577
|
+
export function assertLoaderReadsClean(scope: CacheExecScope): void {
|
|
578
|
+
for (const [value, loaderId] of scope.loaderReads ?? []) {
|
|
579
|
+
assertLoaderValueClean(value, loaderId);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function assertLoaderValueClean(value: object, loaderId: string): void {
|
|
584
|
+
const read = loaderValueIdentityRead(value);
|
|
585
|
+
if (read) throw useCacheLoaderIdentityError(read, loaderId);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/** `root` and every set it links to, transitively. */
|
|
589
|
+
function linkedSets(root: Set<string>): Set<Set<string>> {
|
|
463
590
|
const seen = new Set<Set<string>>();
|
|
464
591
|
const pending = [root];
|
|
465
592
|
for (let set = pending.pop(); set !== undefined; set = pending.pop()) {
|
|
466
593
|
if (seen.has(set)) continue;
|
|
467
594
|
seen.add(set);
|
|
468
|
-
for (const tag of set) out.add(tag);
|
|
469
595
|
for (const linked of tagLinks.get(set) ?? []) pending.push(linked);
|
|
470
596
|
}
|
|
597
|
+
return seen;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/** `root`'s tags plus those of every set it links to, transitively. */
|
|
601
|
+
export function flattenRecordedTags(root: Set<string>): Set<string> {
|
|
602
|
+
const out = new Set<string>();
|
|
603
|
+
for (const set of linkedSets(root)) {
|
|
604
|
+
for (const tag of set) out.add(tag);
|
|
605
|
+
}
|
|
471
606
|
return out;
|
|
472
607
|
}
|
|
473
608
|
|
|
474
|
-
/**
|
|
609
|
+
/**
|
|
610
|
+
* Where each execution that records into a set started (#977): a loader
|
|
611
|
+
* execution (createLoaderExecutor), a loader-cache MISS or refresh, a
|
|
612
|
+
* loader-cache binding's value.
|
|
613
|
+
*/
|
|
614
|
+
const tagSetStarts = new WeakMap<Set<string>, ExecutionStart>();
|
|
615
|
+
|
|
616
|
+
/** Record where the execution recording into `set` started. */
|
|
617
|
+
export function markTagSetStart(set: Set<string>, start: ExecutionStart): void {
|
|
618
|
+
tagSetStarts.set(set, start);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* The earliest start among `root` and every set it links to: a value built
|
|
623
|
+
* from another execution's value is as old as the oldest of them. A
|
|
624
|
+
* loader-cache write gates on it (loader-cache.ts), because the loader value
|
|
625
|
+
* it stores can be a run a reader started before the binding executed, and
|
|
626
|
+
* the values that run read can be older still.
|
|
627
|
+
*/
|
|
628
|
+
export function earliestRecordedStart(
|
|
629
|
+
root: Set<string>,
|
|
630
|
+
): ExecutionStart | undefined {
|
|
631
|
+
let earliest: ExecutionStart | undefined;
|
|
632
|
+
for (const set of linkedSets(root)) {
|
|
633
|
+
const start = tagSetStarts.get(set);
|
|
634
|
+
if (!start) continue;
|
|
635
|
+
earliest = earliest
|
|
636
|
+
? {
|
|
637
|
+
seq: Math.min(earliest.seq, start.seq),
|
|
638
|
+
at: Math.min(earliest.at, start.at),
|
|
639
|
+
}
|
|
640
|
+
: start;
|
|
641
|
+
}
|
|
642
|
+
return earliest;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* recordRequestTags, also recording onto `owner`'s set (#957), the active
|
|
647
|
+
* loader tag set (#964), and the enclosing "use cache" execution's tag
|
|
648
|
+
* scope (#980).
|
|
649
|
+
*
|
|
650
|
+
* The last one is how a nested "use cache" call's tags reach the entry that
|
|
651
|
+
* bakes its value: the inner wrapper records its tags (a miss's, a hit's
|
|
652
|
+
* stored ones, a joined execution's) after its own scope closed, so the
|
|
653
|
+
* scope current then is the caller's. Without it an outer entry kept the
|
|
654
|
+
* inner value past updateTag() of an inner tag, and the same-request gate
|
|
655
|
+
* (tag-invalidation.ts invalidatedSince) did not see the outer execution
|
|
656
|
+
* as tagged by it.
|
|
657
|
+
*/
|
|
475
658
|
function recordOwnedTags(
|
|
476
659
|
tags: Iterable<string> | undefined,
|
|
477
660
|
ctx: RequestContext | undefined,
|
|
@@ -480,12 +663,14 @@ function recordOwnedTags(
|
|
|
480
663
|
if (!tags || !ctx?._requestTags) return;
|
|
481
664
|
const set = ctx._requestTags;
|
|
482
665
|
const captured = activeTagSet();
|
|
666
|
+
const enclosing = cacheTagStorage.getStore();
|
|
483
667
|
let owned: Set<string> | undefined;
|
|
484
668
|
for (const tag of tags) {
|
|
485
669
|
const normalized = normalizeTag(tag);
|
|
486
670
|
if (normalized === null) continue;
|
|
487
671
|
set.add(normalized);
|
|
488
672
|
captured?.add(normalized);
|
|
673
|
+
addToScopes(enclosing, normalized);
|
|
489
674
|
if (owner !== undefined) {
|
|
490
675
|
owned ??= setFor(ownersFor(ctx).tags, owner);
|
|
491
676
|
owned.add(normalized);
|
|
@@ -494,20 +679,31 @@ function recordOwnedTags(
|
|
|
494
679
|
}
|
|
495
680
|
|
|
496
681
|
/**
|
|
497
|
-
* Run a function within a cache tag scope. Any cacheTag() calls inside `fn
|
|
498
|
-
*
|
|
682
|
+
* Run a function within a cache tag scope. Any cacheTag() calls inside `fn`,
|
|
683
|
+
* and the tags a nested "use cache" call records (recordOwnedTags, #980),
|
|
684
|
+
* accumulate into the returned Set and every scope enclosing it
|
|
685
|
+
* (scopeParents).
|
|
499
686
|
*
|
|
500
687
|
* The returned Set is the LIVE reference - the caller must await `result`
|
|
501
688
|
* before reading `tags`, because an async cached function may call cacheTag()
|
|
502
|
-
* after an await boundary.
|
|
689
|
+
* after an await boundary. Pass `tagSet` to re-enter an execution's scope
|
|
690
|
+
* (its result's Flight encode, #980); it keeps the parent it opened under.
|
|
503
691
|
*
|
|
504
692
|
* @internal Used by cache-runtime.ts to wrap "use cache" execution.
|
|
505
693
|
*/
|
|
506
|
-
export function runWithCacheTagScope<T>(
|
|
694
|
+
export function runWithCacheTagScope<T>(
|
|
695
|
+
fn: () => T,
|
|
696
|
+
tagSet?: Set<string>,
|
|
697
|
+
): {
|
|
507
698
|
result: T;
|
|
508
699
|
tags: Set<string>;
|
|
509
700
|
} {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
701
|
+
let scope = tagSet;
|
|
702
|
+
if (!scope) {
|
|
703
|
+
scope = new Set<string>();
|
|
704
|
+
const parent = cacheTagStorage.getStore();
|
|
705
|
+
if (parent) scopeParents.set(scope, parent);
|
|
706
|
+
}
|
|
707
|
+
const result = cacheTagStorage.run(scope, fn);
|
|
708
|
+
return { result, tags: scope };
|
|
513
709
|
}
|