@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
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { CookieOptions } from "../router/middleware-types.js";
|
|
11
|
-
import { getRequestContext } from "./request-context.js";
|
|
11
|
+
import { getRequestContext, _getRequestContext } from "./request-context.js";
|
|
12
|
+
import { isInsideCacheScope } from "./context.js";
|
|
12
13
|
import { INSIDE_CACHE_EXEC } from "../cache/taint.js";
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -84,10 +85,23 @@ export interface ReadonlyHeaders {
|
|
|
84
85
|
type HeadersIterator<T> = IterableIterator<T>;
|
|
85
86
|
|
|
86
87
|
/**
|
|
87
|
-
* Throw if called inside a "use cache" function
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
88
|
+
* Throw if called inside a cache boundary — either a "use cache" function
|
|
89
|
+
* (`INSIDE_CACHE_EXEC` stamped on ctx by the cache runtime) or a `cache()`
|
|
90
|
+
* DSL boundary (`isInsideCacheScope()` — the render-store flag set while
|
|
91
|
+
* resolving a `type: "cache"` route entry).
|
|
92
|
+
*
|
|
93
|
+
* Reading request-scoped data (cookies, headers) inside a cached scope
|
|
94
|
+
* produces per-request values that are NOT reflected in the cache key, so
|
|
95
|
+
* they would be frozen into the shared cache entry and served to the wrong
|
|
96
|
+
* users. This is the same hazard for both scopes: a `cache()` boundary caches
|
|
97
|
+
* everything except loaders (it is the document-level "PPR shell"), so a read
|
|
98
|
+
* here is baked into the shell exactly like a `"use cache"` return value is
|
|
99
|
+
* baked into its cache entry.
|
|
100
|
+
*
|
|
101
|
+
* `isInsideCacheScope()` returns false inside loaders (loaders always run
|
|
102
|
+
* fresh on every request, even on a cache hit), so reading cookies()/headers()
|
|
103
|
+
* from a loader is allowed — loaders are the dynamic "holes" of a cached
|
|
104
|
+
* document.
|
|
91
105
|
*/
|
|
92
106
|
function assertNotInsideCacheContext(ctx: unknown, fnName: string): void {
|
|
93
107
|
if (
|
|
@@ -106,6 +120,16 @@ function assertNotInsideCacheContext(ctx: unknown, fnName: string): void {
|
|
|
106
120
|
` const data = await getCachedData(locale); // locale is now in the cache key`,
|
|
107
121
|
);
|
|
108
122
|
}
|
|
123
|
+
if (isInsideCacheScope()) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`${fnName}() cannot be called inside a cache() boundary. ` +
|
|
126
|
+
`A cache() scope caches everything except loaders, so request-scoped ` +
|
|
127
|
+
`data (cookies, headers) read here would be frozen into the shared ` +
|
|
128
|
+
`cached shell and served to other users. Read it inside a loader ` +
|
|
129
|
+
`instead — loaders always run fresh on every request, even on a cache hit:\n\n` +
|
|
130
|
+
` loader("user", () => getUser(cookies().get("session")?.value));`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
109
133
|
}
|
|
110
134
|
|
|
111
135
|
const HEADERS_MUTATION_METHODS = new Set(["set", "append", "delete"]);
|
|
@@ -144,6 +168,57 @@ export function headers(): ReadonlyHeaders {
|
|
|
144
168
|
}) as unknown as ReadonlyHeaders;
|
|
145
169
|
}
|
|
146
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Force the calling client's caches to miss from now on, from the server seat:
|
|
173
|
+
* write a rotated `Set-Cookie` for the rango state. The responding client
|
|
174
|
+
* applies it on receipt, and its history cache is marked stale by the
|
|
175
|
+
* jar-divergence observer at its next read. Per-client and lazy — it rotates
|
|
176
|
+
* only the client that receives this response, not every client.
|
|
177
|
+
*
|
|
178
|
+
* Idempotent within a request (one `Set-Cookie`). Inert (a dev warning) when
|
|
179
|
+
* called outside a request context. Like `cookies()`, it throws inside a
|
|
180
|
+
* `"use cache"` / `cache()` boundary, but is allowed from a loader (loaders are
|
|
181
|
+
* the dynamic holes of a cached document).
|
|
182
|
+
*/
|
|
183
|
+
export function invalidateClientCache(): void {
|
|
184
|
+
const ctx = _getRequestContext();
|
|
185
|
+
if (!ctx) {
|
|
186
|
+
if (process.env.NODE_ENV !== "production") {
|
|
187
|
+
console.warn(
|
|
188
|
+
"[rango] invalidateClientCache() was called outside a request context; ignored.",
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
assertNotInsideCacheContext(ctx, "invalidateClientCache");
|
|
194
|
+
ctx._rotateStateCookie();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Suppress a server action's automatic client-cache invalidation: tell the
|
|
199
|
+
* action bridge this action changed nothing a route renders, so it should leave
|
|
200
|
+
* the client's state and caches alone (no rotation, no prefetch wipe, no
|
|
201
|
+
* broadcast, no revalidation refetch). Per-response, not per-action-definition —
|
|
202
|
+
* only the execution knows whether anything changed.
|
|
203
|
+
*
|
|
204
|
+
* Sets an internal response header the bridge reads. Idempotent within a
|
|
205
|
+
* request. Inert (a dev warning) outside a request context — there is no
|
|
206
|
+
* automatic invalidation to suppress.
|
|
207
|
+
*/
|
|
208
|
+
export function keepClientCache(): void {
|
|
209
|
+
const ctx = _getRequestContext();
|
|
210
|
+
if (!ctx) {
|
|
211
|
+
if (process.env.NODE_ENV !== "production") {
|
|
212
|
+
console.warn(
|
|
213
|
+
"[rango] keepClientCache() was called outside a request context; ignored.",
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
assertNotInsideCacheContext(ctx, "keepClientCache");
|
|
219
|
+
ctx._setKeepCacheDirective();
|
|
220
|
+
}
|
|
221
|
+
|
|
147
222
|
/**
|
|
148
223
|
* Create a CookieStore backed by a RequestContext.
|
|
149
224
|
* @internal Shared between cookies() shorthand and context methods.
|
|
@@ -13,6 +13,25 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export type HandleData = Record<string, Record<string, unknown[]>>;
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Build a HandleData snapshot from a HandleStore using segment ordering.
|
|
18
|
+
* Reads data directly from the store for each segment in order.
|
|
19
|
+
*/
|
|
20
|
+
export function buildHandleSnapshot(
|
|
21
|
+
handleStore: HandleStore,
|
|
22
|
+
segmentOrder: string[],
|
|
23
|
+
): HandleData {
|
|
24
|
+
const data: HandleData = {};
|
|
25
|
+
for (const segmentId of segmentOrder) {
|
|
26
|
+
const segData = handleStore.getDataForSegment(segmentId);
|
|
27
|
+
for (const handleName in segData) {
|
|
28
|
+
if (!data[handleName]) data[handleName] = {};
|
|
29
|
+
data[handleName][segmentId] = segData[handleName];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return data;
|
|
33
|
+
}
|
|
34
|
+
|
|
16
35
|
function createLateHandlePushError(
|
|
17
36
|
handleName: string,
|
|
18
37
|
segmentId: string,
|
|
@@ -26,10 +45,6 @@ function createLateHandlePushError(
|
|
|
26
45
|
return error;
|
|
27
46
|
}
|
|
28
47
|
|
|
29
|
-
/**
|
|
30
|
-
* Deep clone handle data to create a snapshot.
|
|
31
|
-
* @internal
|
|
32
|
-
*/
|
|
33
48
|
function cloneHandleData(data: HandleData): HandleData {
|
|
34
49
|
const clone: HandleData = {};
|
|
35
50
|
for (const handleName in data) {
|
|
@@ -159,8 +174,10 @@ export function createHandleStore(): HandleStore {
|
|
|
159
174
|
notifyDrain();
|
|
160
175
|
}
|
|
161
176
|
|
|
162
|
-
//
|
|
163
|
-
|
|
177
|
+
// Dirty flag for pending emissions and resolver for waiting consumer.
|
|
178
|
+
// stream() only ever yields the latest full state, so we track a single
|
|
179
|
+
// dirty bit and clone `data` once at yield time instead of per push.
|
|
180
|
+
let hasPendingEmission = false;
|
|
164
181
|
let emissionResolver: (() => void) | null = null;
|
|
165
182
|
let completed = false;
|
|
166
183
|
|
|
@@ -175,7 +192,7 @@ export function createHandleStore(): HandleStore {
|
|
|
175
192
|
|
|
176
193
|
// Wait for the next emission or completion
|
|
177
194
|
function waitForEmission(): Promise<void> {
|
|
178
|
-
if (
|
|
195
|
+
if (hasPendingEmission || completed) {
|
|
179
196
|
return Promise.resolve();
|
|
180
197
|
}
|
|
181
198
|
return new Promise((resolve) => {
|
|
@@ -186,11 +203,9 @@ export function createHandleStore(): HandleStore {
|
|
|
186
203
|
return {
|
|
187
204
|
track<T>(promise: Promise<T>): Promise<T> {
|
|
188
205
|
inflightCount++;
|
|
189
|
-
// Use .then(
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
// rejection on a new branch that nobody catches, which can crash
|
|
193
|
-
// the server process.
|
|
206
|
+
// Use .then() instead of .finally() to avoid creating an unhandled rejection
|
|
207
|
+
// branch when the promise rejects. .finally() re-throws on a new branch that
|
|
208
|
+
// can crash the process if not caught.
|
|
194
209
|
const onSettle = () => {
|
|
195
210
|
inflightCount--;
|
|
196
211
|
notifyDrain();
|
|
@@ -225,8 +240,8 @@ export function createHandleStore(): HandleStore {
|
|
|
225
240
|
}
|
|
226
241
|
data[handleName][segmentId].push(value);
|
|
227
242
|
|
|
228
|
-
//
|
|
229
|
-
|
|
243
|
+
// Mark dirty; the actual snapshot is cloned once at yield time.
|
|
244
|
+
hasPendingEmission = true;
|
|
230
245
|
signalEmission();
|
|
231
246
|
},
|
|
232
247
|
|
|
@@ -236,43 +251,31 @@ export function createHandleStore(): HandleStore {
|
|
|
236
251
|
},
|
|
237
252
|
|
|
238
253
|
async *stream(): AsyncGenerator<HandleData, void, unknown> {
|
|
239
|
-
// Auto-seal: stream() is called after all track() registrations.
|
|
240
254
|
sealInternal();
|
|
241
255
|
|
|
242
|
-
// Set up completion handler
|
|
243
256
|
this.settled.then(() => {
|
|
244
257
|
completed = true;
|
|
245
258
|
signalEmission();
|
|
246
259
|
});
|
|
247
260
|
|
|
248
|
-
//
|
|
249
|
-
// This allows multiple handles pushing in quick succession to be batched
|
|
261
|
+
// Batch rapid synchronous pushes with initial delay
|
|
250
262
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
251
263
|
|
|
252
|
-
// If we already have data, yield the accumulated state
|
|
253
264
|
if (Object.keys(data).length > 0) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const snapshot = cloneHandleData(data);
|
|
257
|
-
yield snapshot;
|
|
265
|
+
hasPendingEmission = false;
|
|
266
|
+
yield cloneHandleData(data);
|
|
258
267
|
}
|
|
259
268
|
|
|
260
|
-
// Continue streaming on each push
|
|
261
269
|
while (!completed) {
|
|
262
270
|
await waitForEmission();
|
|
263
271
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const latest = pendingEmissions[pendingEmissions.length - 1];
|
|
268
|
-
pendingEmissions = [];
|
|
269
|
-
yield latest;
|
|
272
|
+
if (hasPendingEmission) {
|
|
273
|
+
hasPendingEmission = false;
|
|
274
|
+
yield cloneHandleData(data);
|
|
270
275
|
}
|
|
271
276
|
}
|
|
272
277
|
|
|
273
|
-
|
|
274
|
-
// (handles that pushed after our last yield but before completion)
|
|
275
|
-
if (pendingEmissions.length > 0) {
|
|
278
|
+
if (hasPendingEmission) {
|
|
276
279
|
yield cloneHandleData(data);
|
|
277
280
|
}
|
|
278
281
|
},
|
|
@@ -295,13 +298,12 @@ export function createHandleStore(): HandleStore {
|
|
|
295
298
|
if (!data[handleName]) {
|
|
296
299
|
data[handleName] = {};
|
|
297
300
|
}
|
|
298
|
-
// Replace
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
+
// Replace (not append) to avoid handle bleeding between routes.
|
|
302
|
+
// Cached segment restoration should replace existing data for that
|
|
303
|
+
// segment, not accumulate on top of data from a different route.
|
|
301
304
|
data[handleName][segmentId] = [...segmentHandles[handleName]];
|
|
302
305
|
}
|
|
303
|
-
|
|
304
|
-
pendingEmissions.push(cloneHandleData(data));
|
|
306
|
+
hasPendingEmission = true;
|
|
305
307
|
signalEmission();
|
|
306
308
|
},
|
|
307
309
|
};
|
|
@@ -11,17 +11,10 @@ import {
|
|
|
11
11
|
type LoaderRegistryEntry,
|
|
12
12
|
} from "./fetchable-loader-store.js";
|
|
13
13
|
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// The source of truth is fetchableLoaderRegistry in loader.ts, which is populated
|
|
17
|
-
// when createLoader() runs. This cache exists to:
|
|
18
|
-
// 1. Avoid repeated lookups/imports for the same loader
|
|
19
|
-
// 2. Support lazy loading in production (loaders imported on-demand)
|
|
20
|
-
// 3. Provide a stable reference for the RSC handler
|
|
14
|
+
// Cache populated by getLoaderLazy() when loaders are first accessed.
|
|
15
|
+
// Source of truth is fetchableLoaderRegistry in loader.ts (populated on createLoader).
|
|
21
16
|
const loaderRegistry = new Map<string, LoaderRegistryEntry>();
|
|
22
17
|
|
|
23
|
-
// Lazy import map - set by the loader manifest
|
|
24
|
-
// Maps loader $$id to a function that imports the loader module
|
|
25
18
|
type LazyLoaderImport = () => Promise<{ $$id: string }>;
|
|
26
19
|
let lazyLoaderImports: Map<string, LazyLoaderImport> | null = null;
|
|
27
20
|
|
|
@@ -44,59 +37,49 @@ export function setLoaderImports(
|
|
|
44
37
|
export async function getLoaderLazy(
|
|
45
38
|
id: string,
|
|
46
39
|
): Promise<LoaderRegistryEntry | undefined> {
|
|
47
|
-
// Check
|
|
48
|
-
|
|
49
|
-
if (existing) {
|
|
50
|
-
return existing;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Check the fetchable loader registry (populated by createLoader)
|
|
40
|
+
// Check fetchableLoaderRegistry first — it's the source of truth.
|
|
41
|
+
// createLoader() updates it on HMR, ensuring fresh functions after file changes.
|
|
54
42
|
const fetchable = getFetchableLoader(id);
|
|
55
43
|
if (fetchable) {
|
|
56
|
-
// Cache in main registry for future requests
|
|
57
44
|
loaderRegistry.set(id, fetchable);
|
|
58
45
|
return fetchable;
|
|
59
46
|
}
|
|
60
47
|
|
|
61
|
-
|
|
48
|
+
const existing = loaderRegistry.get(id);
|
|
49
|
+
if (existing) {
|
|
50
|
+
return existing;
|
|
51
|
+
}
|
|
52
|
+
|
|
62
53
|
if (lazyLoaderImports && lazyLoaderImports.size > 0) {
|
|
63
54
|
const lazyImport = lazyLoaderImports.get(id);
|
|
64
55
|
if (lazyImport) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
// A failed import is a real server breakage (broken transitive import,
|
|
57
|
+
// syntax error, throw in module top-level code), not a "loader not
|
|
58
|
+
// registered" case. Rethrow so the caller can return a 500 and route
|
|
59
|
+
// the failure through onError, instead of collapsing it to a 404.
|
|
60
|
+
await lazyImport();
|
|
68
61
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return registered;
|
|
74
|
-
}
|
|
75
|
-
} catch (error) {
|
|
76
|
-
console.error(`[LoaderRegistry] Failed to load loader "${id}":`, error);
|
|
62
|
+
const registered = getFetchableLoader(id);
|
|
63
|
+
if (registered) {
|
|
64
|
+
loaderRegistry.set(id, registered);
|
|
65
|
+
return registered;
|
|
77
66
|
}
|
|
78
67
|
}
|
|
79
68
|
}
|
|
80
69
|
|
|
81
|
-
// Dev
|
|
82
|
-
// ID format in dev: "src/path/to/file.ts#ExportName"
|
|
70
|
+
// Dev fallback: parse ID (format: "src/path/to/file.ts#ExportName") and import
|
|
83
71
|
const hashIndex = id.indexOf("#");
|
|
84
72
|
if (hashIndex !== -1) {
|
|
85
73
|
const filePath = id.slice(0, hashIndex);
|
|
86
74
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
await import(/* @vite-ignore */ `/${filePath}`);
|
|
75
|
+
// Same as the lazy branch: a thrown import is a server error, not a
|
|
76
|
+
// not-found. Let it propagate to the caller for a 500 + onError.
|
|
77
|
+
await import(/* @vite-ignore */ `/${filePath}`);
|
|
91
78
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return registered;
|
|
97
|
-
}
|
|
98
|
-
} catch (error) {
|
|
99
|
-
console.error(`[LoaderRegistry] Failed to load loader "${id}":`, error);
|
|
79
|
+
const registered = getFetchableLoader(id);
|
|
80
|
+
if (registered) {
|
|
81
|
+
loaderRegistry.set(id, registered);
|
|
82
|
+
return registered;
|
|
100
83
|
}
|
|
101
84
|
}
|
|
102
85
|
|
|
@@ -114,15 +97,12 @@ export function registerLoaderById(loader: {
|
|
|
114
97
|
if (!loader.$$id) {
|
|
115
98
|
return;
|
|
116
99
|
}
|
|
117
|
-
// For fetchable loaders, fn is stored in the fetchable registry by $$id.
|
|
118
|
-
// Always re-check the fetchable registry so HMR picks up the new function.
|
|
119
100
|
const fetchable = getFetchableLoader(loader.$$id);
|
|
120
101
|
if (fetchable) {
|
|
121
102
|
loaderRegistry.set(loader.$$id, fetchable);
|
|
122
103
|
return;
|
|
123
104
|
}
|
|
124
105
|
|
|
125
|
-
// Fall back to using fn from the loader object (non-fetchable loaders)
|
|
126
106
|
if (loader.fn) {
|
|
127
107
|
loaderRegistry.set(loader.$$id, {
|
|
128
108
|
fn: loader.fn,
|