@rangojs/router 0.18.0 → 0.19.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/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 +31 -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 +24 -4
- 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 +89 -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 +960 -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 +64 -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 +48 -1
- package/src/cache/cache-key-utils.ts +56 -16
- package/src/cache/cache-policy.ts +9 -4
- package/src/cache/cache-runtime.ts +197 -144
- package/src/cache/cache-scope.ts +304 -112
- package/src/cache/cache-tag.ts +105 -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 +28 -3
- 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 +150 -25
- 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 +189 -124
- package/src/server/cookie-store.ts +153 -127
- package/src/server/handle-store.ts +245 -27
- package/src/server/request-context.ts +123 -84
- 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
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { findEnclosingLoaderBody, isInsideLoaderScope } from "./context.js";
|
|
2
|
+
import { holdsThenable } from "../router/segment-resolution/mask-nested.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Handle data structure: handleName -> segmentId -> entries[]
|
|
@@ -60,6 +61,10 @@ interface SlotTag {
|
|
|
60
61
|
liveOf?: string;
|
|
61
62
|
/** Pushed by this loader's body and kept in records (push `owner`). */
|
|
62
63
|
owner?: string;
|
|
64
|
+
/** A document HIT's restore of this loader's record copy (pushRestored). */
|
|
65
|
+
restoredOf?: string;
|
|
66
|
+
/** A restored slot a dropped settled push of its loader stood in for. */
|
|
67
|
+
matched?: true;
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
// Shared by every plain DSL-loader push; tags are never mutated in place.
|
|
@@ -157,7 +162,11 @@ export interface HandleStore {
|
|
|
157
162
|
* body (loader-cache.ts replayLoaderHandles). If that loader then runs live
|
|
158
163
|
* in this request, its first push removes every value replayed for it and
|
|
159
164
|
* takes the first one's position (later live pushes follow it), so the
|
|
160
|
-
* handle output stays live without a duplicate.
|
|
165
|
+
* handle output stays live without a duplicate. A push counts as that
|
|
166
|
+
* loader's when it is made in its body, or in a body it entered that has
|
|
167
|
+
* no replayed values of its own (the capture credits a dependency's push
|
|
168
|
+
* under a live-lane loader to that loader); a hole's run that ends without
|
|
169
|
+
* one drops them (settleLoaderRun).
|
|
161
170
|
*/
|
|
162
171
|
pushReplayed(
|
|
163
172
|
handleName: string,
|
|
@@ -166,6 +175,62 @@ export interface HandleStore {
|
|
|
166
175
|
loaderId: string,
|
|
167
176
|
): void;
|
|
168
177
|
|
|
178
|
+
/**
|
|
179
|
+
* push() a document HIT's copy of `loaderId`'s settled push, restored from
|
|
180
|
+
* the shell's doc record (handle-snapshot.ts restoreHandles). The record is
|
|
181
|
+
* authoritative for that loader's settled pushes: the prelude was rendered
|
|
182
|
+
* from it, and a run of the loader on the HIT reads the store, not the
|
|
183
|
+
* capture's values. So from then on this request drops every settled,
|
|
184
|
+
* thenable-free push the loader makes (live, or replayed from a cache
|
|
185
|
+
* entry, anywhere inside its body), and keeps the ones holding a thenable
|
|
186
|
+
* (holdsThenable: what the record could not keep, `runs`), placed among the
|
|
187
|
+
* restored copies in the loader's push order. Only bake-lane owners are
|
|
188
|
+
* restored this way (withCacheLookup): a live-lane loader is a hole.
|
|
189
|
+
*/
|
|
190
|
+
pushRestored(
|
|
191
|
+
handleName: string,
|
|
192
|
+
segmentId: string,
|
|
193
|
+
data: unknown,
|
|
194
|
+
loaderId: string,
|
|
195
|
+
): void;
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The loaders the route runs on the live lane (liveLaneLoaderIds), from a
|
|
199
|
+
* PPR shell record's restore (handle-snapshot.ts restoreHandles): the
|
|
200
|
+
* holes. A push counts for a restored loader when that loader is the
|
|
201
|
+
* replayed one or the innermost enclosing loader body that restored
|
|
202
|
+
* copies; the walk out through enclosing bodies stops at a hole's body,
|
|
203
|
+
* and a replay of a hole's push counts for none. So a restored loader
|
|
204
|
+
* never takes a push made inside a hole it awaited, while a body inside the
|
|
205
|
+
* hole that restored copies of its own (a dependency the capture ran under
|
|
206
|
+
* a bake-lane loader) still takes its pushes.
|
|
207
|
+
*/
|
|
208
|
+
markLiveLane(loaderIds: ReadonlySet<string>): void;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* `loaderId`'s run in this request settled (resolved or rejected), its
|
|
212
|
+
* pushes made (loader-resolution.ts createLoaderExecutor). When the loader
|
|
213
|
+
* is a hole (markLiveLane), values replayed for it that no push of the run
|
|
214
|
+
* replaced are dropped: the run's output decides, so a hole that throws or
|
|
215
|
+
* skips its push shows none rather than the capture's. Any other loader's
|
|
216
|
+
* replayed values stay: a bake-lane loader's record is authoritative for
|
|
217
|
+
* its settled pushes, as its pin is for its data. Restored copies are not
|
|
218
|
+
* replays and stand.
|
|
219
|
+
*/
|
|
220
|
+
settleLoaderRun(loaderId: string): void;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* A hole's own cache() HIT (loader-cache.ts replayLoaderHandles, once it
|
|
224
|
+
* claims the hole): the values replayed for hole `loaderId` before (its
|
|
225
|
+
* shell copies, which restoreHandles leaves unclaimed, those of the
|
|
226
|
+
* dependencies credited to it included) give way to the pushes the entry
|
|
227
|
+
* recorded, none when it recorded none, so they match the entry's data.
|
|
228
|
+
* `deliver` makes the pushReplayed calls, which take the removed values'
|
|
229
|
+
* place in each array. Any other loader, or a hole with nothing replayed,
|
|
230
|
+
* delivers as a plain replay.
|
|
231
|
+
*/
|
|
232
|
+
redeliverReplays(loaderId: string, deliver: () => void): void;
|
|
233
|
+
|
|
169
234
|
/**
|
|
170
235
|
* Get all collected handle data after all handlers have settled.
|
|
171
236
|
* Waits for `settled` (handler lane), then returns the data at that point.
|
|
@@ -257,6 +322,42 @@ export function createHandleStore(): HandleStore {
|
|
|
257
322
|
const pendingReplays = new Map<string, Set<unknown[]>>();
|
|
258
323
|
// Replayed loaders whose live run already replaced those slots.
|
|
259
324
|
const replacedReplays = new Set<string>();
|
|
325
|
+
// Loaders a document HIT restored record copies for (pushRestored).
|
|
326
|
+
const restoredLoaders = new Set<string>();
|
|
327
|
+
const isRestored = (loaderId: string): boolean =>
|
|
328
|
+
restoredLoaders.has(loaderId);
|
|
329
|
+
// The route's live-lane loaders (markLiveLane): holes a restore never
|
|
330
|
+
// reaches into.
|
|
331
|
+
let liveLane: ReadonlySet<string> | undefined;
|
|
332
|
+
const isHole = (loaderId: string): boolean =>
|
|
333
|
+
liveLane?.has(loaderId) ?? false;
|
|
334
|
+
|
|
335
|
+
const hasReplays = (loaderId: string): boolean =>
|
|
336
|
+
pendingReplays.has(loaderId) || replacedReplays.has(loaderId);
|
|
337
|
+
|
|
338
|
+
// The restored loader a push counts for, as the capture credits it
|
|
339
|
+
// (shell-capture.ts deriveShellCaptureContext): the replayed loader when it
|
|
340
|
+
// restored one, else the innermost enclosing loader body that did. A
|
|
341
|
+
// "use cache" hit inside a restored body replays a dependency's push the
|
|
342
|
+
// capture recorded under that body. A hole's body, or a replay of a hole's
|
|
343
|
+
// push, ends the search: a live loader a restored loader awaits is still
|
|
344
|
+
// live.
|
|
345
|
+
function restoringLoader(replayOf: string | undefined): string | undefined {
|
|
346
|
+
if (replayOf !== undefined) {
|
|
347
|
+
if (restoredLoaders.has(replayOf)) return replayOf;
|
|
348
|
+
// Defensive: a hole's own cache() replay runs outside any loader body,
|
|
349
|
+
// so the walk below finds no restored loader either.
|
|
350
|
+
if (isHole(replayOf)) return undefined;
|
|
351
|
+
}
|
|
352
|
+
return findEnclosingLoaderBody(isRestored, isHole);
|
|
353
|
+
}
|
|
354
|
+
// The replay or restore whose this.push() is in flight, as the tag its
|
|
355
|
+
// slot takes. Set around the call, not passed, so a capture wrapping
|
|
356
|
+
// push() sees it as a push.
|
|
357
|
+
let replaying: Pick<SlotTag, "replayOf" | "restoredOf"> | undefined;
|
|
358
|
+
// A redeliverReplays delivery in flight: per array, where its next
|
|
359
|
+
// pushReplayed value goes (the removed values' place).
|
|
360
|
+
let redelivery: Map<unknown[], number> | undefined;
|
|
260
361
|
|
|
261
362
|
// Aligned to `values` so a mid-array insert or removal keeps positions.
|
|
262
363
|
function tagsFor(values: unknown[]): (SlotTag | undefined)[] {
|
|
@@ -331,6 +432,59 @@ export function createHandleStore(): HandleStore {
|
|
|
331
432
|
return undefined;
|
|
332
433
|
}
|
|
333
434
|
|
|
435
|
+
// A restored loader's settled push: the first of its restored slots here
|
|
436
|
+
// not yet matched stands in for it. Returns nothing; the push is dropped.
|
|
437
|
+
function matchRestoredSlot(values: unknown[], loaderId: string): void {
|
|
438
|
+
const tags = slotTags.get(values);
|
|
439
|
+
if (!tags) return;
|
|
440
|
+
for (let i = 0; i < values.length; i++) {
|
|
441
|
+
const tag = tags[i];
|
|
442
|
+
if (tag?.restoredOf === loaderId && !tag.matched) {
|
|
443
|
+
tags[i] = { ...tag, matched: true };
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Where a kept push by a restored loader lands: before its first unmatched
|
|
450
|
+
// restored slot here (it came before that push), else after its last slot
|
|
451
|
+
// here, else appended.
|
|
452
|
+
function restoredInsertIndex(
|
|
453
|
+
values: unknown[],
|
|
454
|
+
loaderId: string,
|
|
455
|
+
): number | undefined {
|
|
456
|
+
const tags = slotTags.get(values);
|
|
457
|
+
if (!tags) return undefined;
|
|
458
|
+
let after: number | undefined;
|
|
459
|
+
for (let i = 0; i < values.length; i++) {
|
|
460
|
+
const tag = tags[i];
|
|
461
|
+
if (tag?.restoredOf === loaderId && !tag.matched) return i;
|
|
462
|
+
if (
|
|
463
|
+
tag?.restoredOf === loaderId ||
|
|
464
|
+
tag?.liveOf === loaderId ||
|
|
465
|
+
tag?.replayOf === loaderId
|
|
466
|
+
) {
|
|
467
|
+
after = i + 1;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return after;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function replayPush(
|
|
474
|
+
store: HandleStore,
|
|
475
|
+
replay: NonNullable<typeof replaying>,
|
|
476
|
+
handleName: string,
|
|
477
|
+
segmentId: string,
|
|
478
|
+
value: unknown,
|
|
479
|
+
): void {
|
|
480
|
+
replaying = replay;
|
|
481
|
+
try {
|
|
482
|
+
store.push(handleName, segmentId, value);
|
|
483
|
+
} finally {
|
|
484
|
+
replaying = undefined;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
334
488
|
// Settlement barriers: `settled` (handler lane) resolves when sealed AND
|
|
335
489
|
// handler inflight === 0. `fullySettled` additionally waits for the
|
|
336
490
|
// auxiliary (loader) lane. seal() signals "no more track() calls"; each
|
|
@@ -477,6 +631,28 @@ export function createHandleStore(): HandleStore {
|
|
|
477
631
|
throw error;
|
|
478
632
|
}
|
|
479
633
|
|
|
634
|
+
const { replayOf, restoredOf } = replaying ?? {};
|
|
635
|
+
// A restore is no loader's push. Any other push, while a loader has
|
|
636
|
+
// replayed or restored slots to reconcile, is attributed as the capture
|
|
637
|
+
// credits it: to the innermost loader body it is made in that has
|
|
638
|
+
// replayed slots, up to a hole's body, so a replay made in a body
|
|
639
|
+
// replaces that body's replayed copies and a dependency's push replaces
|
|
640
|
+
// those of the live-lane loader it ran under. Its record copy stands
|
|
641
|
+
// when a restored loader counts it (restoringLoader).
|
|
642
|
+
let pusher: string | undefined;
|
|
643
|
+
let restoredBy: string | undefined;
|
|
644
|
+
if (!restoredOf) {
|
|
645
|
+
if (pendingReplays.size > 0 || replacedReplays.size > 0) {
|
|
646
|
+
pusher = findEnclosingLoaderBody(hasReplays, isHole);
|
|
647
|
+
}
|
|
648
|
+
if (restoredLoaders.size > 0) restoredBy = restoringLoader(replayOf);
|
|
649
|
+
}
|
|
650
|
+
if (restoredBy !== undefined && !holdsThenable(value)) {
|
|
651
|
+
const existing = data[handleName]?.[segmentId];
|
|
652
|
+
if (existing) matchRestoredSlot(existing, restoredBy);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
|
|
480
656
|
if (!data[handleName]) {
|
|
481
657
|
data[handleName] = {};
|
|
482
658
|
}
|
|
@@ -487,52 +663,94 @@ export function createHandleStore(): HandleStore {
|
|
|
487
663
|
const loader = (loaderPush ?? isInsideLoaderScope()) || undefined;
|
|
488
664
|
let at: number | undefined;
|
|
489
665
|
let liveOf: string | undefined;
|
|
490
|
-
if (
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
666
|
+
if (restoredOf) {
|
|
667
|
+
restoredLoaders.add(restoredOf);
|
|
668
|
+
} else if (replayOf !== undefined && redelivery?.has(values)) {
|
|
669
|
+
at = redelivery.get(values)!;
|
|
670
|
+
redelivery.set(values, at + 1);
|
|
671
|
+
} else if (pusher !== undefined) {
|
|
672
|
+
at = liveReplacementIndex(pusher, values);
|
|
673
|
+
liveOf = pusher;
|
|
674
|
+
} else if (restoredBy !== undefined) {
|
|
675
|
+
at = restoredInsertIndex(values, restoredBy);
|
|
676
|
+
if (!replayOf) liveOf = restoredBy;
|
|
499
677
|
}
|
|
500
678
|
insertSlot(
|
|
501
679
|
values,
|
|
502
680
|
at,
|
|
503
681
|
value,
|
|
504
|
-
liveOf || owner
|
|
505
|
-
? { loader, liveOf, owner }
|
|
682
|
+
replayOf || liveOf || owner || restoredOf
|
|
683
|
+
? { loader, replayOf, liveOf, owner, restoredOf }
|
|
506
684
|
: loader
|
|
507
685
|
? LOADER_SLOT
|
|
508
686
|
: undefined,
|
|
509
687
|
);
|
|
688
|
+
if (replayOf) {
|
|
689
|
+
let arrays = pendingReplays.get(replayOf);
|
|
690
|
+
if (!arrays) pendingReplays.set(replayOf, (arrays = new Set()));
|
|
691
|
+
arrays.add(values);
|
|
692
|
+
}
|
|
510
693
|
|
|
511
694
|
// Bump the version; each consumer's cursor decides when to clone+yield.
|
|
512
695
|
version++;
|
|
513
696
|
signalEmission();
|
|
514
697
|
},
|
|
515
698
|
|
|
699
|
+
// Both go through this.push: active captures see a replay like a live
|
|
700
|
+
// push, and a diverting capture keeps it out of the store.
|
|
516
701
|
pushReplayed(
|
|
517
702
|
handleName: string,
|
|
518
703
|
segmentId: string,
|
|
519
704
|
value: unknown,
|
|
520
705
|
loaderId: string,
|
|
521
706
|
): void {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
707
|
+
replayPush(this, { replayOf: loaderId }, handleName, segmentId, value);
|
|
708
|
+
},
|
|
709
|
+
|
|
710
|
+
pushRestored(
|
|
711
|
+
handleName: string,
|
|
712
|
+
segmentId: string,
|
|
713
|
+
value: unknown,
|
|
714
|
+
loaderId: string,
|
|
715
|
+
): void {
|
|
716
|
+
replayPush(this, { restoredOf: loaderId }, handleName, segmentId, value);
|
|
717
|
+
},
|
|
718
|
+
|
|
719
|
+
markLiveLane(loaderIds: ReadonlySet<string>): void {
|
|
720
|
+
liveLane = loaderIds;
|
|
721
|
+
},
|
|
722
|
+
|
|
723
|
+
settleLoaderRun(loaderId: string): void {
|
|
724
|
+
const pending = pendingReplays.get(loaderId);
|
|
725
|
+
if (!pending || completed || !isHole(loaderId)) return;
|
|
726
|
+
pendingReplays.delete(loaderId);
|
|
727
|
+
replacedReplays.add(loaderId);
|
|
728
|
+
for (const values of pending) removeReplayedSlots(values, loaderId);
|
|
729
|
+
version++;
|
|
730
|
+
signalEmission();
|
|
731
|
+
},
|
|
732
|
+
|
|
733
|
+
redeliverReplays(loaderId: string, deliver: () => void): void {
|
|
734
|
+
const pending = pendingReplays.get(loaderId);
|
|
735
|
+
if (!pending || completed || !isHole(loaderId)) {
|
|
736
|
+
deliver();
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
pendingReplays.delete(loaderId);
|
|
740
|
+
const anchors = new Map<unknown[], number>();
|
|
741
|
+
for (const values of pending) {
|
|
742
|
+
const first = removeReplayedSlots(values, loaderId);
|
|
743
|
+
if (first !== undefined) anchors.set(values, first);
|
|
744
|
+
}
|
|
745
|
+
const outer = redelivery;
|
|
746
|
+
redelivery = anchors;
|
|
747
|
+
try {
|
|
748
|
+
deliver();
|
|
749
|
+
} finally {
|
|
750
|
+
redelivery = outer;
|
|
751
|
+
}
|
|
752
|
+
version++;
|
|
753
|
+
signalEmission();
|
|
536
754
|
},
|
|
537
755
|
|
|
538
756
|
getData(): Promise<HandleData> {
|