@rangojs/router 0.0.0-experimental.1b930379 → 0.0.0-experimental.1c0bdfad
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/AGENTS.md +12 -0
- package/README.md +245 -49
- package/dist/bin/rango.js +441 -134
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3453 -1240
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +76 -21
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +64 -2
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +247 -23
- package/skills/caching/SKILL.md +318 -15
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +4 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +250 -30
- package/skills/host-router/SKILL.md +83 -23
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +50 -6
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +279 -53
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -0
- package/skills/migrate-react-router/SKILL.md +771 -0
- package/skills/mime-routes/SKILL.md +28 -1
- package/skills/observability/SKILL.md +172 -0
- package/skills/parallel/SKILL.md +203 -7
- package/skills/prerender/SKILL.md +153 -109
- package/skills/rango/SKILL.md +251 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +123 -48
- package/skills/route/SKILL.md +101 -5
- package/skills/router-setup/SKILL.md +116 -8
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +129 -0
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +121 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/typesafety/SKILL.md +332 -29
- package/skills/use-cache/SKILL.md +46 -14
- 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/__internal.ts +1 -66
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +197 -150
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/navigation-bridge.ts +111 -31
- package/src/browser/navigation-client.ts +201 -67
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +76 -67
- package/src/browser/navigation-transaction.ts +18 -66
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +187 -112
- package/src/browser/prefetch/cache.ts +230 -35
- package/src/browser/prefetch/fetch.ts +338 -39
- package/src/browser/prefetch/queue.ts +126 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +111 -16
- package/src/browser/react/NavigationProvider.tsx +135 -120
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +66 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +178 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +23 -69
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +32 -7
- package/src/browser/react/use-params.ts +20 -10
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +46 -11
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +80 -5
- package/src/browser/rsc-router.tsx +218 -76
- package/src/browser/scroll-restoration.ts +54 -42
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +222 -61
- package/src/browser/types.ts +91 -11
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +65 -40
- package/src/build/generate-route-types.ts +5 -1
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +165 -36
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +16 -5
- package/src/build/route-types/include-resolution.ts +125 -24
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +22 -6
- package/src/build/route-types/router-processing.ts +272 -96
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +29 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +214 -48
- package/src/cache/cache-scope.ts +236 -89
- package/src/cache/cache-tag.ts +103 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +2224 -171
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +11 -17
- package/src/cache/document-cache.ts +89 -27
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +11 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +31 -31
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +9 -17
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +55 -0
- package/src/cache/types.ts +37 -100
- package/src/client.rsc.tsx +44 -21
- package/src/client.tsx +119 -290
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +84 -2
- package/src/debug.ts +2 -2
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +30 -4
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +70 -22
- package/src/handles/MetaTags.tsx +56 -19
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +37 -8
- package/src/handles/is-thenable.ts +19 -0
- package/src/handles/meta.ts +51 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +93 -12
- package/src/index.ts +133 -15
- package/src/internal-debug.ts +11 -10
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +37 -41
- package/src/prerender.ts +198 -82
- package/src/redirect-origin.ts +100 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +62 -0
- package/src/reverse.ts +65 -15
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +7 -72
- package/src/route-definition/dsl-helpers.ts +469 -276
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +113 -37
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +53 -12
- package/src/route-definition/resolve-handler-use.ts +161 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +7 -17
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +164 -17
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +45 -22
- package/src/router/handler-context.ts +83 -39
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +50 -24
- package/src/router/lazy-includes.ts +19 -53
- package/src/router/loader-resolution.ts +274 -56
- package/src/router/logging.ts +5 -8
- package/src/router/manifest.ts +49 -45
- package/src/router/match-api.ts +120 -204
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +33 -6
- package/src/router/match-middleware/cache-lookup.ts +214 -263
- package/src/router/match-middleware/cache-store.ts +73 -33
- package/src/router/match-middleware/intercept-resolution.ts +8 -28
- package/src/router/match-middleware/segment-resolution.ts +52 -18
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +104 -40
- package/src/router/metrics.ts +5 -34
- package/src/router/middleware-types.ts +13 -142
- package/src/router/middleware.ts +270 -172
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +132 -90
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prerender-match.ts +195 -56
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +8 -28
- package/src/router/router-interfaces.ts +115 -35
- package/src/router/router-options.ts +172 -15
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +264 -77
- package/src/router/segment-resolution/helpers.ts +115 -30
- package/src/router/segment-resolution/loader-cache.ts +63 -37
- package/src/router/segment-resolution/revalidation.ts +474 -385
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +8 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +161 -199
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +163 -59
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +163 -55
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +658 -511
- package/src/rsc/helpers.ts +168 -46
- package/src/rsc/index.ts +2 -5
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +127 -31
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +77 -11
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +99 -189
- package/src/rsc/rsc-rendering.ts +105 -72
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +263 -112
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +32 -6
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +149 -0
- package/src/segment-system.tsx +281 -129
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +309 -61
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +40 -38
- package/src/server/loader-registry.ts +26 -46
- package/src/server/request-context.ts +398 -172
- package/src/ssr/index.tsx +25 -16
- package/src/static-handler.ts +27 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +701 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +257 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +99 -0
- package/src/testing/internal/context.ts +371 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +343 -0
- package/src/testing/render-route.tsx +581 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +20 -58
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +233 -81
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +44 -15
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +37 -14
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +58 -11
- package/src/urls/path-helper.ts +57 -111
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -18
- package/src/use-loader.tsx +346 -89
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/discover-routers.ts +130 -85
- 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 +214 -132
- 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 +57 -6
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +6 -0
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +155 -65
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- 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 +194 -0
- package/src/vite/plugins/expose-action-id.ts +49 -98
- package/src/vite/plugins/expose-id-utils.ts +96 -51
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
- package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
- package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
- package/src/vite/plugins/expose-internal-ids.ts +553 -317
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/version-injector.ts +21 -25
- package/src/vite/plugins/version-plugin.ts +46 -37
- package/src/vite/plugins/virtual-entries.ts +13 -18
- package/src/vite/rango.ts +238 -295
- package/src/vite/router-discovery.ts +940 -149
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +81 -34
- package/src/vite/utils/shared-utils.ts +92 -42
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -104,7 +104,8 @@ import type { ResolvedSegment } from "../../types.js";
|
|
|
104
104
|
import { getRequestContext } from "../../server/request-context.js";
|
|
105
105
|
import type { MatchContext, MatchPipelineState } from "../match-context.js";
|
|
106
106
|
import { getRouterContext } from "../router-context.js";
|
|
107
|
-
import { debugLog, debugWarn } from "../logging.js";
|
|
107
|
+
import { debugLog, debugWarn, getOrCreateRequestId } from "../logging.js";
|
|
108
|
+
import { INTERNAL_RANGO_DEBUG } from "../../internal-debug.js";
|
|
108
109
|
import type { GeneratorMiddleware } from "./cache-lookup.js";
|
|
109
110
|
|
|
110
111
|
/**
|
|
@@ -120,21 +121,16 @@ export function withCacheStore<TEnv>(
|
|
|
120
121
|
return async function* (
|
|
121
122
|
source: AsyncGenerator<ResolvedSegment>,
|
|
122
123
|
): AsyncGenerator<ResolvedSegment> {
|
|
123
|
-
const pipelineStart = performance.now();
|
|
124
124
|
const ms = ctx.metricsStore;
|
|
125
125
|
|
|
126
|
-
// Collect all segments while passing them through
|
|
127
126
|
const allSegments: ResolvedSegment[] = [];
|
|
128
127
|
for await (const segment of source) {
|
|
129
128
|
allSegments.push(segment);
|
|
130
129
|
yield segment;
|
|
131
130
|
}
|
|
132
131
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
// 2. This is an action (actions don't cache)
|
|
136
|
-
// 3. Cache was already hit (no need to re-cache)
|
|
137
|
-
// 4. Non-GET request (only cache GET requests)
|
|
132
|
+
const ownStart = performance.now();
|
|
133
|
+
|
|
138
134
|
if (
|
|
139
135
|
!ctx.cacheScope?.enabled ||
|
|
140
136
|
ctx.isAction ||
|
|
@@ -144,8 +140,8 @@ export function withCacheStore<TEnv>(
|
|
|
144
140
|
if (ms) {
|
|
145
141
|
ms.metrics.push({
|
|
146
142
|
label: "pipeline:cache-store",
|
|
147
|
-
duration: performance.now() -
|
|
148
|
-
startTime:
|
|
143
|
+
duration: performance.now() - ownStart,
|
|
144
|
+
startTime: ownStart - ms.requestStart,
|
|
149
145
|
});
|
|
150
146
|
}
|
|
151
147
|
return;
|
|
@@ -159,13 +155,22 @@ export function withCacheStore<TEnv>(
|
|
|
159
155
|
createHandleStore,
|
|
160
156
|
} = getRouterContext<TEnv>();
|
|
161
157
|
|
|
162
|
-
//
|
|
163
|
-
|
|
158
|
+
// On a fresh intercept miss the intercept slot segments flow through
|
|
159
|
+
// `source` into allSegments AND are also recorded on state.interceptSegments.
|
|
160
|
+
// Dedup by id so each segment is cached once — cacheRoute serializes the
|
|
161
|
+
// array verbatim (no id-dedup), so an un-deduped append doubles the intercept
|
|
162
|
+
// segment in the cached entry, re-rendering the slot twice on the next hit.
|
|
163
|
+
const seenSegmentIds = new Set(allSegments.map((s) => s.id));
|
|
164
|
+
const allSegmentsToCache = [
|
|
165
|
+
...allSegments,
|
|
166
|
+
...state.interceptSegments.filter((s) => !seenSegmentIds.has(s.id)),
|
|
167
|
+
];
|
|
164
168
|
|
|
165
|
-
// Check if any non-loader segments have null components
|
|
166
|
-
// This happens when client already had those segments (partial navigation)
|
|
167
169
|
const hasNullComponents = allSegmentsToCache.some(
|
|
168
|
-
(s) =>
|
|
170
|
+
(s) =>
|
|
171
|
+
s.component === null &&
|
|
172
|
+
s.type !== "loader" &&
|
|
173
|
+
ctx.clientSegmentSet.has(s.id),
|
|
169
174
|
);
|
|
170
175
|
|
|
171
176
|
const requestCtx = getRequestContext();
|
|
@@ -173,10 +178,22 @@ export function withCacheStore<TEnv>(
|
|
|
173
178
|
|
|
174
179
|
const cacheScope = ctx.cacheScope;
|
|
175
180
|
|
|
176
|
-
//
|
|
177
|
-
//
|
|
181
|
+
// Record the route's segment-DSL cache tags into the request tag union NOW,
|
|
182
|
+
// synchronously in the pipeline. The actual store write (cacheRoute) runs in
|
|
183
|
+
// requestCtx.waitUntil() below — and the proactive path re-resolves the whole
|
|
184
|
+
// tree first — so cacheRoute's own tag recording races the document cache's
|
|
185
|
+
// post-body-drain snapshot of _requestTags. On a first-write miss the document
|
|
186
|
+
// tag union could miss these tags and revalidateTag()/updateTag() would not
|
|
187
|
+
// invalidate the cached document. Recording here (before the snapshot) closes
|
|
188
|
+
// the window for both the direct and proactive write paths; the duplicate
|
|
189
|
+
// record inside cacheRoute is idempotent.
|
|
190
|
+
cacheScope.recordTags(requestCtx);
|
|
191
|
+
|
|
192
|
+
const reqId = INTERNAL_RANGO_DEBUG
|
|
193
|
+
? getOrCreateRequestId(ctx.request)
|
|
194
|
+
: undefined;
|
|
195
|
+
|
|
178
196
|
requestCtx.onResponse((response) => {
|
|
179
|
-
// Only cache successful responses
|
|
180
197
|
if (response.status !== 200) {
|
|
181
198
|
debugLog("cacheStore", "skipping cache for non-200 response", {
|
|
182
199
|
status: response.status,
|
|
@@ -186,21 +203,17 @@ export function withCacheStore<TEnv>(
|
|
|
186
203
|
}
|
|
187
204
|
|
|
188
205
|
if (hasNullComponents) {
|
|
189
|
-
// Proactive caching: render all segments fresh in background
|
|
190
|
-
// This ensures cache has complete components for future requests
|
|
191
206
|
requestCtx.waitUntil(async () => {
|
|
207
|
+
const savedMetrics = ctx.Store.metrics;
|
|
208
|
+
ctx.Store.metrics = undefined;
|
|
209
|
+
|
|
210
|
+
const start = performance.now();
|
|
192
211
|
debugLog("cacheStore", "proactive caching started", {
|
|
193
212
|
pathname: ctx.pathname,
|
|
194
213
|
});
|
|
195
|
-
// Swap to a fresh HandleStore so handle.push() calls from
|
|
196
|
-
// proactive resolution are captured (not silenced). The original
|
|
197
|
-
// store's stream is already sent by waitUntil time.
|
|
198
|
-
// cacheRoute reads from requestCtx._handleStore, so this ensures
|
|
199
|
-
// complete handle data (e.g. breadcrumbs) is cached.
|
|
200
214
|
const originalHandleStore = requestCtx._handleStore;
|
|
201
215
|
requestCtx._handleStore = createHandleStore();
|
|
202
216
|
try {
|
|
203
|
-
// Create fresh context for proactive caching
|
|
204
217
|
const proactiveHandlerContext = createHandlerContext(
|
|
205
218
|
ctx.matched.params,
|
|
206
219
|
ctx.request,
|
|
@@ -215,10 +228,8 @@ export function withCacheStore<TEnv>(
|
|
|
215
228
|
);
|
|
216
229
|
const proactiveLoaderPromises = new Map<string, Promise<any>>();
|
|
217
230
|
|
|
218
|
-
// Use normal loader access so handle data is captured
|
|
219
231
|
setupLoaderAccess(proactiveHandlerContext, proactiveLoaderPromises);
|
|
220
232
|
|
|
221
|
-
// Re-resolve ALL segments without revalidation
|
|
222
233
|
const Store = ctx.Store;
|
|
223
234
|
const freshSegments = await Store.run(() =>
|
|
224
235
|
resolveAllSegments(
|
|
@@ -227,10 +238,10 @@ export function withCacheStore<TEnv>(
|
|
|
227
238
|
ctx.matched.params,
|
|
228
239
|
proactiveHandlerContext,
|
|
229
240
|
proactiveLoaderPromises,
|
|
241
|
+
{ skipLoaders: true },
|
|
230
242
|
),
|
|
231
243
|
);
|
|
232
244
|
|
|
233
|
-
// Also resolve intercept segments fresh if applicable
|
|
234
245
|
let freshInterceptSegments: ResolvedSegment[] = [];
|
|
235
246
|
if (ctx.interceptResult) {
|
|
236
247
|
freshInterceptSegments = await Store.run(() =>
|
|
@@ -241,6 +252,12 @@ export function withCacheStore<TEnv>(
|
|
|
241
252
|
proactiveHandlerContext,
|
|
242
253
|
true, // belongsToRoute
|
|
243
254
|
// No revalidationContext = render fresh
|
|
255
|
+
undefined,
|
|
256
|
+
// Skip intercept middleware: the foreground already ran it
|
|
257
|
+
// before the response was sent. Re-running here (post-response,
|
|
258
|
+
// background) would fire side effects twice and a short-circuit
|
|
259
|
+
// Response would silently abort this cache write.
|
|
260
|
+
{ skipMiddleware: true },
|
|
244
261
|
),
|
|
245
262
|
);
|
|
246
263
|
}
|
|
@@ -256,28 +273,51 @@ export function withCacheStore<TEnv>(
|
|
|
256
273
|
completeSegments,
|
|
257
274
|
ctx.isIntercept,
|
|
258
275
|
);
|
|
276
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
277
|
+
const dur = performance.now() - start;
|
|
278
|
+
console.log(
|
|
279
|
+
`[RSC Background][req:${reqId}] Proactive cache ${ctx.pathname} (${dur.toFixed(2)}ms) segments=${completeSegments.length}`,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
259
282
|
debugLog("cacheStore", "proactive caching complete", {
|
|
260
283
|
pathname: ctx.pathname,
|
|
261
284
|
});
|
|
262
285
|
} catch (error) {
|
|
286
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
287
|
+
const dur = performance.now() - start;
|
|
288
|
+
console.log(
|
|
289
|
+
`[RSC Background][req:${reqId}] Proactive cache ${ctx.pathname} FAILED (${dur.toFixed(2)}ms) error=${String(error)}`,
|
|
290
|
+
);
|
|
291
|
+
}
|
|
263
292
|
debugWarn("cacheStore", "proactive caching failed", {
|
|
264
293
|
pathname: ctx.pathname,
|
|
265
294
|
error: String(error),
|
|
266
295
|
});
|
|
267
296
|
} finally {
|
|
268
297
|
requestCtx._handleStore = originalHandleStore;
|
|
298
|
+
ctx.Store.metrics = savedMetrics;
|
|
269
299
|
}
|
|
270
300
|
});
|
|
271
301
|
} else {
|
|
272
|
-
|
|
273
|
-
|
|
302
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
303
|
+
console.log(
|
|
304
|
+
`[RSC CacheStore][req:${reqId}] Direct cache path: scheduling cacheRoute for ${ctx.pathname} (${allSegmentsToCache.length} segments, hasNullComponents=${hasNullComponents})`,
|
|
305
|
+
);
|
|
306
|
+
}
|
|
274
307
|
requestCtx.waitUntil(async () => {
|
|
308
|
+
const start = performance.now();
|
|
275
309
|
await cacheScope.cacheRoute(
|
|
276
310
|
ctx.pathname,
|
|
277
311
|
ctx.matched.params,
|
|
278
312
|
allSegmentsToCache,
|
|
279
313
|
ctx.isIntercept,
|
|
280
314
|
);
|
|
315
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
316
|
+
const dur = performance.now() - start;
|
|
317
|
+
console.log(
|
|
318
|
+
`[RSC Background][req:${reqId}] Cache store ${ctx.pathname} (${dur.toFixed(2)}ms) segments=${allSegmentsToCache.length}`,
|
|
319
|
+
);
|
|
320
|
+
}
|
|
281
321
|
});
|
|
282
322
|
}
|
|
283
323
|
|
|
@@ -287,8 +327,8 @@ export function withCacheStore<TEnv>(
|
|
|
287
327
|
if (ms) {
|
|
288
328
|
ms.metrics.push({
|
|
289
329
|
label: "pipeline:cache-store",
|
|
290
|
-
duration: performance.now() -
|
|
291
|
-
startTime:
|
|
330
|
+
duration: performance.now() - ownStart,
|
|
331
|
+
startTime: ownStart - ms.requestStart,
|
|
292
332
|
});
|
|
293
333
|
}
|
|
294
334
|
};
|
|
@@ -123,54 +123,46 @@ export function withInterceptResolution<TEnv>(
|
|
|
123
123
|
return async function* (
|
|
124
124
|
source: AsyncGenerator<ResolvedSegment>,
|
|
125
125
|
): AsyncGenerator<ResolvedSegment> {
|
|
126
|
-
const pipelineStart = performance.now();
|
|
127
126
|
const ms = ctx.metricsStore;
|
|
128
127
|
|
|
129
|
-
// First, yield all segments from the source (main segment resolution or cache)
|
|
130
128
|
const segments: ResolvedSegment[] = [];
|
|
131
129
|
for await (const segment of source) {
|
|
132
130
|
segments.push(segment);
|
|
133
131
|
yield segment;
|
|
134
132
|
}
|
|
135
133
|
|
|
136
|
-
|
|
134
|
+
const ownStart = performance.now();
|
|
135
|
+
|
|
137
136
|
if (ctx.isFullMatch) {
|
|
138
137
|
if (ms) {
|
|
139
138
|
ms.metrics.push({
|
|
140
139
|
label: "pipeline:intercept",
|
|
141
|
-
duration: performance.now() -
|
|
142
|
-
startTime:
|
|
140
|
+
duration: performance.now() - ownStart,
|
|
141
|
+
startTime: ownStart - ms.requestStart,
|
|
143
142
|
});
|
|
144
143
|
}
|
|
145
144
|
return;
|
|
146
145
|
}
|
|
147
146
|
|
|
148
|
-
// Skip intercept resolution if:
|
|
149
|
-
// 1. No intercept result
|
|
150
|
-
// 2. Already have intercept segments (from cache hit with intercept key)
|
|
151
|
-
// 3. Cache hit with intercept key
|
|
152
147
|
const skipInterceptResolution =
|
|
153
148
|
!ctx.interceptResult ||
|
|
154
149
|
state.interceptSegments.length > 0 ||
|
|
155
150
|
(state.cacheHit && ctx.isIntercept);
|
|
156
151
|
|
|
157
152
|
if (skipInterceptResolution) {
|
|
158
|
-
// For cache hit with intercept, extract intercept segments from cached data for slots
|
|
159
|
-
// and re-resolve loaders for fresh data
|
|
160
153
|
if (ctx.interceptResult && state.cacheHit && ctx.isIntercept) {
|
|
161
154
|
await handleCacheHitIntercept(ctx, state, segments);
|
|
162
155
|
}
|
|
163
156
|
if (ms) {
|
|
164
157
|
ms.metrics.push({
|
|
165
158
|
label: "pipeline:intercept",
|
|
166
|
-
duration: performance.now() -
|
|
167
|
-
startTime:
|
|
159
|
+
duration: performance.now() - ownStart,
|
|
160
|
+
startTime: ownStart - ms.requestStart,
|
|
168
161
|
});
|
|
169
162
|
}
|
|
170
163
|
return;
|
|
171
164
|
}
|
|
172
165
|
|
|
173
|
-
// Resolve intercept segments
|
|
174
166
|
const { resolveInterceptEntry } = getRouterContext<TEnv>();
|
|
175
167
|
|
|
176
168
|
const slotName = ctx.interceptResult!.intercept.slotName;
|
|
@@ -179,7 +171,6 @@ export function withInterceptResolution<TEnv>(
|
|
|
179
171
|
slotName,
|
|
180
172
|
});
|
|
181
173
|
|
|
182
|
-
// Resolve intercept entry (middleware, loaders, handler)
|
|
183
174
|
const Store = ctx.Store;
|
|
184
175
|
const interceptSegments = await Store.run(() =>
|
|
185
176
|
resolveInterceptEntry(
|
|
@@ -201,14 +192,12 @@ export function withInterceptResolution<TEnv>(
|
|
|
201
192
|
),
|
|
202
193
|
);
|
|
203
194
|
|
|
204
|
-
// Update state
|
|
205
195
|
state.interceptSegments = interceptSegments;
|
|
206
196
|
state.slots[slotName] = {
|
|
207
197
|
active: true,
|
|
208
198
|
segments: interceptSegments,
|
|
209
199
|
};
|
|
210
200
|
|
|
211
|
-
// Yield intercept segments
|
|
212
201
|
for (const segment of interceptSegments) {
|
|
213
202
|
yield segment;
|
|
214
203
|
}
|
|
@@ -216,18 +205,13 @@ export function withInterceptResolution<TEnv>(
|
|
|
216
205
|
if (ms) {
|
|
217
206
|
ms.metrics.push({
|
|
218
207
|
label: "pipeline:intercept",
|
|
219
|
-
duration: performance.now() -
|
|
220
|
-
startTime:
|
|
208
|
+
duration: performance.now() - ownStart,
|
|
209
|
+
startTime: ownStart - ms.requestStart,
|
|
221
210
|
});
|
|
222
211
|
}
|
|
223
212
|
};
|
|
224
213
|
}
|
|
225
214
|
|
|
226
|
-
/**
|
|
227
|
-
* Handle cache hit with intercept scenario
|
|
228
|
-
*
|
|
229
|
-
* Extract intercept segments from cached data and re-resolve loaders for fresh data.
|
|
230
|
-
*/
|
|
231
215
|
async function handleCacheHitIntercept<TEnv>(
|
|
232
216
|
ctx: MatchContext<TEnv>,
|
|
233
217
|
state: MatchPipelineState,
|
|
@@ -239,14 +223,11 @@ async function handleCacheHitIntercept<TEnv>(
|
|
|
239
223
|
|
|
240
224
|
const slotName = ctx.interceptResult.intercept.slotName;
|
|
241
225
|
|
|
242
|
-
// Find intercept segments from cached segments (namespace starts with "intercept:")
|
|
243
226
|
const interceptSegments = segments.filter((s) =>
|
|
244
227
|
s.namespace?.startsWith("intercept:"),
|
|
245
228
|
);
|
|
246
229
|
state.interceptSegments = interceptSegments;
|
|
247
230
|
|
|
248
|
-
// Re-resolve intercept loaders for fresh data on cache hit
|
|
249
|
-
// This keeps cached component/layout but fetches fresh loader data
|
|
250
231
|
if (resolveInterceptLoadersOnly) {
|
|
251
232
|
const Store = ctx.Store;
|
|
252
233
|
const freshLoaderResult = await Store.run(() =>
|
|
@@ -269,7 +250,6 @@ async function handleCacheHitIntercept<TEnv>(
|
|
|
269
250
|
),
|
|
270
251
|
);
|
|
271
252
|
|
|
272
|
-
// Update intercept segment's loaderDataPromise with fresh data
|
|
273
253
|
if (freshLoaderResult) {
|
|
274
254
|
const interceptMainSegment = interceptSegments.find(
|
|
275
255
|
(s) => s.type === "parallel" && s.slot,
|
|
@@ -87,16 +87,43 @@
|
|
|
87
87
|
* if (state.cacheHit) return; // Now we can check
|
|
88
88
|
*/
|
|
89
89
|
import type { ResolvedSegment } from "../../types.js";
|
|
90
|
+
import type { EntryData } from "../../server/context.js";
|
|
91
|
+
import { _getRequestContext } from "../../server/request-context.js";
|
|
90
92
|
import type { MatchContext, MatchPipelineState } from "../match-context.js";
|
|
91
93
|
import { getRouterContext } from "../router-context.js";
|
|
92
94
|
import type { GeneratorMiddleware } from "./cache-lookup.js";
|
|
93
95
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
export function treeHasStreaming(entries: EntryData[]): boolean {
|
|
97
|
+
for (const entry of entries) {
|
|
98
|
+
if (
|
|
99
|
+
"loading" in entry &&
|
|
100
|
+
entry.loading !== undefined &&
|
|
101
|
+
entry.loading !== false
|
|
102
|
+
)
|
|
103
|
+
return true;
|
|
104
|
+
if (entry.layout) {
|
|
105
|
+
if (treeHasStreaming(entry.layout)) return true;
|
|
106
|
+
}
|
|
107
|
+
if (entry.parallel) {
|
|
108
|
+
for (const key in entry.parallel) {
|
|
109
|
+
const parallelEntry = entry.parallel[key as `@${string}`];
|
|
110
|
+
if (parallelEntry) {
|
|
111
|
+
if (
|
|
112
|
+
"loading" in parallelEntry &&
|
|
113
|
+
parallelEntry.loading !== undefined &&
|
|
114
|
+
parallelEntry.loading !== false
|
|
115
|
+
)
|
|
116
|
+
return true;
|
|
117
|
+
if (parallelEntry.layout) {
|
|
118
|
+
if (treeHasStreaming(parallelEntry.layout)) return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
100
127
|
export function withSegmentResolution<TEnv>(
|
|
101
128
|
ctx: MatchContext<TEnv>,
|
|
102
129
|
state: MatchPipelineState,
|
|
@@ -104,34 +131,36 @@ export function withSegmentResolution<TEnv>(
|
|
|
104
131
|
return async function* (
|
|
105
132
|
source: AsyncGenerator<ResolvedSegment>,
|
|
106
133
|
): AsyncGenerator<ResolvedSegment> {
|
|
107
|
-
const pipelineStart = performance.now();
|
|
108
134
|
const ms = ctx.metricsStore;
|
|
109
135
|
|
|
110
|
-
// IMPORTANT: Always iterate source first to give cache-lookup a chance
|
|
111
|
-
// to run and set state.cacheHit. Without this, cache-lookup never executes!
|
|
112
136
|
for await (const segment of source) {
|
|
113
137
|
yield segment;
|
|
114
138
|
}
|
|
115
139
|
|
|
116
|
-
|
|
140
|
+
const ownStart = performance.now();
|
|
141
|
+
|
|
117
142
|
if (state.cacheHit) {
|
|
118
143
|
if (ms) {
|
|
119
144
|
ms.metrics.push({
|
|
120
145
|
label: "pipeline:segment-resolve",
|
|
121
|
-
duration: performance.now() -
|
|
122
|
-
startTime:
|
|
146
|
+
duration: performance.now() - ownStart,
|
|
147
|
+
startTime: ownStart - ms.requestStart,
|
|
123
148
|
});
|
|
124
149
|
}
|
|
125
150
|
return;
|
|
126
151
|
}
|
|
127
152
|
|
|
153
|
+
const reqCtx = _getRequestContext();
|
|
154
|
+
if (reqCtx && reqCtx._treeHasStreaming === undefined) {
|
|
155
|
+
reqCtx._treeHasStreaming = treeHasStreaming(ctx.entries);
|
|
156
|
+
}
|
|
157
|
+
|
|
128
158
|
const { resolveAllSegmentsWithRevalidation, resolveAllSegments } =
|
|
129
159
|
getRouterContext<TEnv>();
|
|
130
160
|
|
|
131
161
|
const Store = ctx.Store;
|
|
132
162
|
|
|
133
163
|
if (ctx.isFullMatch) {
|
|
134
|
-
// Full match (document request) - simple resolution without revalidation
|
|
135
164
|
const segments = await Store.run(() =>
|
|
136
165
|
resolveAllSegments(
|
|
137
166
|
ctx.entries,
|
|
@@ -142,11 +171,12 @@ export function withSegmentResolution<TEnv>(
|
|
|
142
171
|
),
|
|
143
172
|
);
|
|
144
173
|
|
|
145
|
-
// Update state with resolved segments
|
|
146
174
|
state.segments = segments;
|
|
147
175
|
state.matchedIds = segments.map((s: { id: string }) => s.id);
|
|
148
176
|
|
|
149
|
-
|
|
177
|
+
if (reqCtx) {
|
|
178
|
+
reqCtx._resolveRenderBarrier(segments);
|
|
179
|
+
}
|
|
150
180
|
for (const segment of segments) {
|
|
151
181
|
yield segment;
|
|
152
182
|
}
|
|
@@ -163,11 +193,11 @@ export function withSegmentResolution<TEnv>(
|
|
|
163
193
|
ctx.request,
|
|
164
194
|
ctx.prevUrl,
|
|
165
195
|
ctx.url,
|
|
166
|
-
ctx.loaderPromises,
|
|
167
196
|
ctx.actionContext,
|
|
168
197
|
ctx.interceptResult,
|
|
169
198
|
ctx.localRouteName,
|
|
170
199
|
ctx.pathname,
|
|
200
|
+
ctx.stale,
|
|
171
201
|
),
|
|
172
202
|
);
|
|
173
203
|
|
|
@@ -175,6 +205,10 @@ export function withSegmentResolution<TEnv>(
|
|
|
175
205
|
state.segments = result.segments;
|
|
176
206
|
state.matchedIds = result.matchedIds;
|
|
177
207
|
|
|
208
|
+
if (reqCtx) {
|
|
209
|
+
reqCtx._resolveRenderBarrier(result.segments);
|
|
210
|
+
}
|
|
211
|
+
|
|
178
212
|
// Yield all resolved segments
|
|
179
213
|
for (const segment of result.segments) {
|
|
180
214
|
yield segment;
|
|
@@ -184,8 +218,8 @@ export function withSegmentResolution<TEnv>(
|
|
|
184
218
|
if (ms) {
|
|
185
219
|
ms.metrics.push({
|
|
186
220
|
label: "pipeline:segment-resolve",
|
|
187
|
-
duration: performance.now() -
|
|
188
|
-
startTime:
|
|
221
|
+
duration: performance.now() - ownStart,
|
|
222
|
+
startTime: ownStart - ms.requestStart,
|
|
189
223
|
});
|
|
190
224
|
}
|
|
191
225
|
};
|
|
@@ -106,16 +106,6 @@ import {
|
|
|
106
106
|
withSegmentResolution,
|
|
107
107
|
} from "./match-middleware/index.js";
|
|
108
108
|
|
|
109
|
-
/**
|
|
110
|
-
* Compose multiple async generator middleware into a single middleware
|
|
111
|
-
*
|
|
112
|
-
* Middleware are applied in reverse order (rightmost runs first, innermost).
|
|
113
|
-
* For the pipeline:
|
|
114
|
-
* compose(A, B, C)(source)
|
|
115
|
-
*
|
|
116
|
-
* The flow is: source -> C -> B -> A -> output
|
|
117
|
-
* Where C is the innermost (runs first on input) and A is outermost (runs last).
|
|
118
|
-
*/
|
|
119
109
|
export function compose<T>(
|
|
120
110
|
...middleware: GeneratorMiddleware<T>[]
|
|
121
111
|
): GeneratorMiddleware<T> {
|
|
@@ -126,54 +116,23 @@ export function compose<T>(
|
|
|
126
116
|
return middleware[0];
|
|
127
117
|
}
|
|
128
118
|
return (source) => {
|
|
129
|
-
// Apply middleware in reverse order (rightmost first)
|
|
130
119
|
return middleware.reduceRight((prev, fn) => fn(prev), source);
|
|
131
120
|
};
|
|
132
121
|
}
|
|
133
122
|
|
|
134
|
-
|
|
135
|
-
* Create an empty async generator (source for pipeline)
|
|
136
|
-
*/
|
|
137
|
-
export async function* empty<T>(): AsyncGenerator<T> {
|
|
138
|
-
// Yields nothing - used as the initial source for the pipeline
|
|
139
|
-
}
|
|
123
|
+
export async function* empty<T>(): AsyncGenerator<T> {}
|
|
140
124
|
|
|
141
|
-
/**
|
|
142
|
-
* Create the match partial pipeline
|
|
143
|
-
*
|
|
144
|
-
* Pipeline order (innermost to outermost):
|
|
145
|
-
* 1. cache-lookup - Check cache first, yield cached segments if hit
|
|
146
|
-
* 2. segment-resolution - Resolve segments if cache miss
|
|
147
|
-
* 3. intercept-resolution - Resolve intercept segments
|
|
148
|
-
* 4. cache-store - Store segments in cache
|
|
149
|
-
* 5. background-revalidation - Trigger SWR if cache was stale
|
|
150
|
-
*
|
|
151
|
-
* Data flow:
|
|
152
|
-
* - empty() produces no segments
|
|
153
|
-
* - cache-lookup either yields cached segments OR passes through to segment-resolution
|
|
154
|
-
* - segment-resolution resolves fresh segments on cache miss
|
|
155
|
-
* - intercept-resolution adds intercept segments
|
|
156
|
-
* - cache-store observes and caches segments
|
|
157
|
-
* - background-revalidation triggers SWR revalidation if needed
|
|
158
|
-
*/
|
|
159
125
|
export function createMatchPartialPipeline<TEnv>(
|
|
160
126
|
ctx: MatchContext<TEnv>,
|
|
161
127
|
state: MatchPipelineState,
|
|
162
128
|
): AsyncGenerator<ResolvedSegment> {
|
|
163
|
-
// Build the middleware chain
|
|
164
129
|
const pipeline = compose<ResolvedSegment>(
|
|
165
|
-
// Outermost - observes segments and triggers background revalidation
|
|
166
130
|
withBackgroundRevalidation(ctx, state),
|
|
167
|
-
// Observes and stores segments in cache
|
|
168
131
|
withCacheStore(ctx, state),
|
|
169
|
-
// Adds intercept segments after main segments
|
|
170
132
|
withInterceptResolution(ctx, state),
|
|
171
|
-
// Resolves segments on cache miss
|
|
172
133
|
withSegmentResolution(ctx, state),
|
|
173
|
-
// Innermost - checks cache first
|
|
174
134
|
withCacheLookup(ctx, state),
|
|
175
135
|
);
|
|
176
136
|
|
|
177
|
-
// Start with empty source - cache lookup or segment resolution will produce segments
|
|
178
137
|
return pipeline(empty());
|
|
179
138
|
}
|