@rangojs/router 0.16.0 → 0.18.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/README.md +255 -89
- package/dist/testing/vitest.js +252 -0
- package/dist/types/browser/cookie-name.d.ts +6 -0
- package/dist/types/browser/history-state.d.ts +16 -0
- package/dist/types/browser/navigation-transaction.d.ts +6 -0
- package/dist/types/browser/partial-update.d.ts +11 -0
- package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
- package/dist/types/browser/rsc-router.d.ts +1 -1
- package/dist/types/browser/scroll-restoration.d.ts +7 -0
- package/dist/types/cache/cache-exec-scope.d.ts +18 -1
- package/dist/types/cache/cache-key-utils.d.ts +17 -2
- package/dist/types/cache/cache-policy.d.ts +17 -0
- package/dist/types/cache/cache-scope.d.ts +132 -19
- package/dist/types/cache/cache-tag.d.ts +92 -7
- package/dist/types/cache/cf/cf-base64.d.ts +6 -0
- package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
- package/dist/types/cache/cf/cf-cache-store.d.ts +185 -44
- package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
- package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +34 -0
- package/dist/types/cache/handle-capture.d.ts +41 -3
- package/dist/types/cache/handle-snapshot.d.ts +54 -13
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/segment-codec.d.ts +10 -3
- package/dist/types/cache/shell-memo.d.ts +178 -0
- package/dist/types/cache/shell-snapshot.d.ts +110 -21
- package/dist/types/cache/tag-invalidation.d.ts +30 -3
- package/dist/types/cache/taint.d.ts +1 -1
- package/dist/types/cache/types.d.ts +220 -44
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
- package/dist/types/client.d.ts +4 -4
- package/dist/types/client.rsc.d.ts +4 -5
- package/dist/types/components/DefaultDocument.d.ts +5 -2
- package/dist/types/context-var.d.ts +6 -5
- package/dist/types/escape-script.d.ts +1 -1
- package/dist/types/handles/MetaTags.d.ts +5 -4
- package/dist/types/handles/Scripts.d.ts +8 -7
- package/dist/types/handles/meta.d.ts +2 -2
- package/dist/types/handles/script.d.ts +17 -16
- package/dist/types/href-client.d.ts +1 -1
- package/dist/types/html.d.ts +12 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +3 -2
- package/dist/types/loader-store.d.ts +7 -4
- package/dist/types/prerender/build-shell-capture.d.ts +11 -4
- package/dist/types/reverse.d.ts +36 -4
- package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
- package/dist/types/route-definition/helpers-types.d.ts +33 -9
- package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
- package/dist/types/route-types.d.ts +1 -1
- package/dist/types/router/debug-manifest.d.ts +3 -2
- package/dist/types/router/error-handling.d.ts +20 -0
- package/dist/types/router/handler-context.d.ts +6 -0
- package/dist/types/router/instrument.d.ts +8 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
- package/dist/types/router/middleware-types.d.ts +9 -3
- package/dist/types/router/router-context.d.ts +3 -1
- package/dist/types/router/router-interfaces.d.ts +24 -2
- package/dist/types/router/router-options.d.ts +8 -5
- package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +66 -1
- package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +23 -12
- package/dist/types/router/segment-resolution/mask-nested.d.ts +67 -26
- package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
- package/dist/types/router/segment-wrappers.d.ts +1 -1
- package/dist/types/rsc/full-payload.d.ts +14 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/response-route-handler.d.ts +1 -1
- package/dist/types/rsc/rsc-rendering.d.ts +1 -2
- package/dist/types/rsc/shell-build-manifest.d.ts +8 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +61 -6
- package/dist/types/rsc/shell-capture.d.ts +151 -19
- package/dist/types/rsc/shell-serve.d.ts +140 -17
- package/dist/types/rsc/ssr-setup.d.ts +6 -6
- package/dist/types/rsc/types.d.ts +17 -0
- package/dist/types/server/context.d.ts +139 -15
- package/dist/types/server/cookie-store.d.ts +21 -0
- package/dist/types/server/handle-store.d.ts +26 -2
- package/dist/types/server/request-context.d.ts +182 -53
- package/dist/types/ssr/index.d.ts +24 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/static-handler.d.ts +4 -1
- package/dist/types/testing/flight.entry.d.ts +7 -0
- package/dist/types/testing/generated-routes.d.ts +2 -2
- package/dist/types/testing/internal/context.d.ts +1 -1
- package/dist/types/testing/render-route.d.ts +32 -6
- package/dist/types/testing/run-loader.d.ts +18 -3
- package/dist/types/testing/serve-shell-request.d.ts +132 -0
- package/dist/types/testing/shell-status.d.ts +23 -10
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
- package/dist/types/testing/vitest.d.ts +35 -1
- package/dist/types/theme/ThemeScript.d.ts +3 -3
- package/dist/types/types/cache-types.d.ts +14 -10
- package/dist/types/types/handler-context.d.ts +28 -1
- package/dist/types/types/loader-types.d.ts +8 -6
- package/dist/types/urls/pattern-types.d.ts +22 -18
- package/dist/types/urls/response-types.d.ts +3 -12
- package/dist/types/use-loader.d.ts +12 -3
- package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
- package/dist/types/vite/rango.d.ts +1 -1
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +1420 -1348
- package/package.json +2 -2
- package/skills/api-client/SKILL.md +22 -10
- package/skills/breadcrumbs/SKILL.md +75 -22
- package/skills/bundle-analysis/SKILL.md +22 -22
- package/skills/cache-guide/SKILL.md +306 -124
- package/skills/caching/SKILL.md +530 -98
- package/skills/catalog.json +16 -16
- package/skills/client-urls/SKILL.md +65 -38
- package/skills/cloudflare/SKILL.md +87 -20
- package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
- package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
- package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
- package/skills/comparison/SKILL.md +5 -0
- package/skills/comparison/references/framework-comparison.md +75 -57
- package/skills/composability/SKILL.md +38 -29
- package/skills/css/SKILL.md +10 -13
- package/skills/debug-manifest/SKILL.md +74 -31
- package/skills/defer-hydration/SKILL.md +52 -31
- package/skills/deployment-caching/SKILL.md +11 -3
- package/skills/document-cache/SKILL.md +98 -28
- package/skills/fonts/SKILL.md +16 -8
- package/skills/handler-use/SKILL.md +55 -17
- package/skills/hooks/SKILL.md +28 -20
- package/skills/hooks/data.md +23 -3
- package/skills/hooks/handle-and-actions.md +34 -17
- package/skills/hooks/navigation.md +39 -10
- package/skills/hooks/outlets.md +13 -6
- package/skills/hooks/state.md +39 -15
- package/skills/hooks/urls.md +7 -5
- package/skills/host-router/SKILL.md +64 -12
- package/skills/i18n/SKILL.md +60 -34
- package/skills/intercept/SKILL.md +161 -26
- package/skills/layout/SKILL.md +106 -50
- package/skills/links/SKILL.md +51 -19
- package/skills/loader/SKILL.md +386 -128
- package/skills/middleware/SKILL.md +129 -26
- package/skills/migrate-nextjs/SKILL.md +156 -70
- package/skills/migrate-nextjs/backend-host-swap.md +5 -2
- package/skills/migrate-react-router/SKILL.md +51 -39
- package/skills/migrate-react-router/component-migration.md +28 -7
- package/skills/migrate-react-router/data-and-actions.md +21 -6
- package/skills/migrate-react-router/route-mapping.md +19 -10
- package/skills/mime-routes/SKILL.md +12 -7
- package/skills/observability/SKILL.md +92 -14
- package/skills/parallel/SKILL.md +88 -47
- package/skills/ppr/SKILL.md +866 -379
- package/skills/prerender/SKILL.md +210 -108
- package/skills/rango/SKILL.md +115 -65
- package/skills/react-compiler/SKILL.md +6 -5
- package/skills/response-routes/SKILL.md +133 -25
- package/skills/route/SKILL.md +170 -60
- package/skills/router-setup/SKILL.md +420 -240
- package/skills/scripts/SKILL.md +39 -21
- package/skills/server-actions/SKILL.md +231 -59
- package/skills/shell-manifest/SKILL.md +28 -14
- package/skills/streams-and-websockets/SKILL.md +32 -14
- package/skills/tailwind/SKILL.md +37 -33
- package/skills/testing/SKILL.md +73 -33
- package/skills/testing/cache-prerender.md +60 -33
- package/skills/testing/client-components.md +59 -6
- package/skills/testing/e2e-parity.md +12 -2
- package/skills/testing/flight.md +19 -11
- package/skills/testing/handles.md +8 -5
- package/skills/testing/loader.md +26 -6
- package/skills/testing/middleware.md +8 -3
- package/skills/testing/predicates.md +120 -0
- package/skills/testing/render-handler.md +70 -22
- package/skills/testing/response-routes.md +11 -4
- package/skills/testing/reverse-and-types.md +20 -14
- package/skills/testing/serve-shell-request.md +188 -0
- package/skills/testing/server-actions.md +12 -3
- package/skills/testing/server-tree.md +9 -0
- package/skills/testing/setup.md +9 -5
- package/skills/theme/SKILL.md +155 -30
- package/skills/typesafety/SKILL.md +20 -9
- package/skills/typesafety/env-and-bindings.md +137 -50
- package/skills/typesafety/generated-files-and-cli.md +26 -14
- package/skills/typesafety/params-and-search.md +44 -19
- package/skills/typesafety/route-types.md +50 -14
- package/skills/use-cache/SKILL.md +224 -83
- package/skills/vercel/SKILL.md +35 -13
- package/skills/view-transitions/SKILL.md +42 -21
- package/src/__augment-tests__/augmented.check.ts +26 -1
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/history-state.ts +23 -0
- package/src/browser/index.ts +2 -2
- package/src/browser/navigation-bridge.ts +3 -4
- package/src/browser/navigation-transaction.ts +22 -2
- package/src/browser/partial-update.ts +35 -25
- package/src/browser/react/ScrollRestoration.tsx +6 -6
- package/src/browser/rsc-router.tsx +6 -1
- package/src/browser/scroll-restoration.ts +53 -6
- package/src/cache/cache-exec-scope.ts +37 -4
- package/src/cache/cache-key-utils.ts +23 -8
- package/src/cache/cache-policy.ts +22 -0
- package/src/cache/cache-runtime.ts +264 -68
- package/src/cache/cache-scope.ts +419 -43
- package/src/cache/cache-tag.ts +375 -11
- package/src/cache/cf/cf-base64.ts +36 -4
- package/src/cache/cf/cf-cache-constants.ts +12 -0
- package/src/cache/cf/cf-cache-store.ts +990 -348
- package/src/cache/cf/cf-cache-types.ts +32 -4
- package/src/cache/cf/cf-shell-frame.ts +258 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +143 -39
- package/src/cache/document-cache.ts +15 -0
- package/src/cache/handle-capture.ts +90 -10
- package/src/cache/handle-snapshot.ts +133 -28
- package/src/cache/index.ts +2 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/segment-codec.ts +26 -7
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +217 -27
- package/src/cache/tag-invalidation.ts +108 -22
- package/src/cache/taint.ts +1 -1
- package/src/cache/types.ts +225 -44
- package/src/cache/vercel/vercel-cache-store.ts +545 -61
- package/src/client.rsc.tsx +10 -9
- package/src/client.tsx +5 -4
- package/src/components/DefaultDocument.tsx +7 -2
- package/src/context-var.ts +6 -5
- package/src/escape-script.ts +1 -1
- package/src/handles/MetaTags.tsx +13 -11
- package/src/handles/Scripts.tsx +11 -10
- package/src/handles/meta.ts +2 -2
- package/src/handles/script.ts +17 -16
- package/src/href-client.ts +1 -1
- package/src/html.ts +12 -0
- package/src/index.rsc.ts +5 -0
- package/src/index.ts +1 -0
- package/src/loader-store.ts +7 -4
- package/src/prerender/build-shell-capture.ts +73 -22
- package/src/reverse.ts +44 -4
- package/src/route-definition/dsl-helpers.ts +121 -33
- package/src/route-definition/helpers-types.ts +33 -9
- package/src/route-definition/resolve-handler-use.ts +121 -6
- package/src/route-types.ts +0 -3
- package/src/router/debug-manifest.ts +10 -2
- package/src/router/error-handling.ts +51 -15
- package/src/router/handler-context.ts +71 -28
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +141 -43
- package/src/router/loader-resolution.ts +115 -39
- package/src/router/match-api.ts +40 -26
- package/src/router/match-handlers.ts +5 -0
- package/src/router/match-middleware/background-revalidation.ts +161 -96
- package/src/router/match-middleware/cache-lookup.ts +110 -10
- package/src/router/match-middleware/cache-store.ts +48 -101
- package/src/router/middleware-types.ts +9 -6
- package/src/router/middleware.ts +75 -13
- package/src/router/prerender-match.ts +59 -19
- package/src/router/route-snapshot.ts +11 -1
- package/src/router/router-context.ts +2 -0
- package/src/router/router-interfaces.ts +25 -2
- package/src/router/router-options.ts +8 -5
- package/src/router/segment-resolution/fresh.ts +62 -21
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +407 -76
- package/src/router/segment-resolution/loader-mask.ts +12 -22
- package/src/router/segment-resolution/loader-snapshot.ts +208 -56
- package/src/router/segment-resolution/mask-nested.ts +138 -44
- package/src/router/segment-resolution/revalidation.ts +61 -16
- package/src/router/segment-wrappers.ts +4 -0
- package/src/rsc/full-payload.ts +25 -1
- package/src/rsc/handler.ts +14 -2
- package/src/rsc/progressive-enhancement.ts +3 -2
- package/src/rsc/render-pipeline.ts +3 -0
- package/src/rsc/response-cache-serve.ts +9 -6
- package/src/rsc/response-route-handler.ts +11 -3
- package/src/rsc/rsc-rendering.ts +608 -225
- package/src/rsc/shell-build-manifest.ts +27 -9
- package/src/rsc/shell-capture-constants.ts +73 -6
- package/src/rsc/shell-capture.ts +1068 -296
- package/src/rsc/shell-serve.ts +316 -34
- package/src/rsc/ssr-setup.ts +14 -8
- package/src/rsc/types.ts +18 -0
- package/src/server/context.ts +300 -32
- package/src/server/cookie-store.ts +87 -61
- package/src/server/handle-store.ts +232 -7
- package/src/server/request-context.ts +297 -67
- package/src/ssr/index.tsx +136 -44
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/static-handler.ts +4 -1
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/flight.entry.ts +14 -0
- package/src/testing/generated-routes.ts +2 -2
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/internal/flight-client-globals.ts +7 -5
- package/src/testing/render-route.tsx +85 -24
- package/src/testing/run-loader.ts +68 -6
- package/src/testing/serve-shell-request.ts +378 -0
- package/src/testing/shell-status.ts +34 -12
- package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
- package/src/testing/vitest.ts +62 -1
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/theme/ThemeScript.tsx +3 -3
- package/src/types/cache-types.ts +14 -10
- package/src/types/handler-context.ts +28 -1
- package/src/types/loader-types.ts +8 -6
- package/src/urls/pattern-types.ts +22 -18
- package/src/urls/response-types.ts +3 -23
- package/src/use-loader.tsx +12 -3
- package/src/vite/discovery/shell-prerender-phase.ts +9 -0
- package/src/vite/plugins/use-cache-transform.ts +118 -74
- package/src/vite/rango.ts +7 -3
- package/src/vite/router-discovery.ts +14 -4
|
@@ -6,14 +6,21 @@ argument-hint: [profile-name]
|
|
|
6
6
|
|
|
7
7
|
# "use cache" Directive
|
|
8
8
|
|
|
9
|
-
Function-level caching for async server functions and RSC components.
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
Function-level caching for async server functions and RSC components. Put the
|
|
10
|
+
directive in a function (or at the top of a module) and its return value is
|
|
11
|
+
cached with a TTL and a stale-while-revalidate window, keyed by its arguments.
|
|
12
|
+
Use it when one data fetch or one component is expensive and is reused across
|
|
13
|
+
routes or requests; the code around the call keeps running.
|
|
14
|
+
|
|
15
|
+
It complements the route-level `cache()` DSL (a whole segment subtree) and
|
|
16
|
+
build-time `Static()`/`Prerender()`. `"use cache"` needs an app-level store
|
|
17
|
+
(`createRouter({ cache })`); without one the function runs uncached.
|
|
12
18
|
|
|
13
19
|
## Not this skill if…
|
|
14
20
|
|
|
15
21
|
- You want to cache a whole route or a rendered subtree of segments — that is
|
|
16
22
|
the segment-level `cache()` DSL: see `/caching`.
|
|
23
|
+
- You want a value fresh on every request — use a loader (see `/loader`).
|
|
17
24
|
- You are unsure which cache layer you need — start at `/cache-guide`.
|
|
18
25
|
|
|
19
26
|
## Basic Usage
|
|
@@ -56,6 +63,16 @@ export async function ProductCard({ id }: { id: string }) {
|
|
|
56
63
|
}
|
|
57
64
|
```
|
|
58
65
|
|
|
66
|
+
### Results that are not cached
|
|
67
|
+
|
|
68
|
+
A result Flight cannot encode cleanly is not cached: an async child component
|
|
69
|
+
that throws, a promise in the result or in a handle push that rejects, a
|
|
70
|
+
function or a class instance. Storing it would bake the failure into every hit
|
|
71
|
+
until expiry. Instead the write is skipped and reported to `onError` (phase
|
|
72
|
+
`"cache"`, category `cache-write`, or `stale-revalidation` for a background
|
|
73
|
+
refresh): a miss still returns the live result, the next call runs the
|
|
74
|
+
function again, and a stale entry keeps serving.
|
|
75
|
+
|
|
59
76
|
## Named Cache Profiles
|
|
60
77
|
|
|
61
78
|
Define profiles in createRouter. Profile names map to `"use cache: <name>"` in
|
|
@@ -85,11 +102,17 @@ createRouter({
|
|
|
85
102
|
mutation-related cached data; incidental TTL staleness on an ordinary action
|
|
86
103
|
stays SWR so the action is not turned into a synchronous cache-refresh barrier.
|
|
87
104
|
For strong read-your-own-writes after a mutation, prefer `updateTag()` (a hard
|
|
88
|
-
purge, so the action's own re-render is a fresh foreground miss).
|
|
105
|
+
purge, so the action's own re-render is a fresh foreground miss). Only
|
|
106
|
+
`"use cache"` honors this flag; `cache()` boundaries and cached loaders ignore
|
|
107
|
+
it.
|
|
108
|
+
- `default` is built in as `{ ttl: 900, swr: 1800 }`; defining `default`
|
|
109
|
+
overrides it.
|
|
110
|
+
- Profile names must match `[a-zA-Z0-9_-]+`, and `ttl`/`swr` must be finite and
|
|
111
|
+
non-negative; `createRouter()` throws otherwise.
|
|
89
112
|
- Unknown profile names throw at runtime, on the first invocation of the cached
|
|
90
113
|
function (the Vite transform does not validate names at build/boot). The error
|
|
91
|
-
|
|
92
|
-
|
|
114
|
+
names the missing profile and shows the `createRouter({ cacheProfiles })` entry
|
|
115
|
+
to add.
|
|
93
116
|
|
|
94
117
|
## Cache Key
|
|
95
118
|
|
|
@@ -97,8 +120,20 @@ cacheProfiles: { ... } })` entry to add.
|
|
|
97
120
|
use-cache:{functionId}:{serializedArgs}
|
|
98
121
|
```
|
|
99
122
|
|
|
100
|
-
- `functionId` -- stable ID from Vite transform (module path + export name
|
|
101
|
-
|
|
123
|
+
- `functionId` -- stable ID from Vite transform (module path + export name in
|
|
124
|
+
dev, a hash of both in production builds).
|
|
125
|
+
- `serializedArgs` -- key-generating arguments: a stable JSON encoding when every
|
|
126
|
+
argument is JSON-safe, otherwise serialized via RSC `encodeReply()`.
|
|
127
|
+
|
|
128
|
+
React elements (`header`, `children` slots) and client or server references
|
|
129
|
+
are left out of the key: the first call's rendered slot is part of the cached
|
|
130
|
+
output, and a hit returns it for any slot passed.
|
|
131
|
+
|
|
132
|
+
If any other argument cannot be serialized, top-level or nested (a function, a
|
|
133
|
+
class instance such as a database client, a symbol), the call runs uncached and
|
|
134
|
+
warns once per function in dev. Flight's encoder writes the same placeholder
|
|
135
|
+
for every such value, so keying on it would serve one caller's result to every
|
|
136
|
+
other caller. Pass the serializable values the function needs.
|
|
102
137
|
|
|
103
138
|
When there are no key-generating arguments, the key has no trailing colon -- it is
|
|
104
139
|
just `use-cache:{functionId}`.
|
|
@@ -107,6 +142,11 @@ Different functions always produce different cache keys, even for the same route
|
|
|
107
142
|
This is important for intercepted routes -- the path handler and intercept handler
|
|
108
143
|
each have their own `functionId` and therefore their own cache entries.
|
|
109
144
|
|
|
145
|
+
The key is only what the function declares: its id and arguments. A call made
|
|
146
|
+
inside a route `cache()` with a `key()` does not inherit that key, so a value
|
|
147
|
+
the route partitions by (a tier header, a session) must be passed in as an
|
|
148
|
+
argument. The same holds for a loader's own `cache()` (`/loader` → "Cache Key").
|
|
149
|
+
|
|
110
150
|
### Route context is folded into the key
|
|
111
151
|
|
|
112
152
|
The tainted `ctx` object is excluded from arg serialization (see below), but
|
|
@@ -117,16 +157,48 @@ params excluded). The same cached function called with `ctx` on different routes
|
|
|
117
157
|
param combinations, hosts, response types, or query variants therefore produces
|
|
118
158
|
distinct cache entries -- not one shared entry.
|
|
119
159
|
|
|
120
|
-
##
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
160
|
+
## Request-Scoped Arguments (ctx, Request, env)
|
|
161
|
+
|
|
162
|
+
Three request-scoped arguments are kept out of the serialized arguments:
|
|
163
|
+
|
|
164
|
+
- **`ctx`** is branded with `Symbol.for('rango:nocache')` at creation. Its route
|
|
165
|
+
fields are folded into the key, and its handle pushes are captured and
|
|
166
|
+
replayed (below). Every ctx is branded: handler, loader, middleware,
|
|
167
|
+
response-route and the request context. A loader ctx keys exactly like the
|
|
168
|
+
handler ctx of the same request. A middleware ctx keys by host, pathname,
|
|
169
|
+
its params and search, plus the route name once the route is matched (a
|
|
170
|
+
global middleware before `next()` has none yet). A ctx carrying a request
|
|
171
|
+
body (a fetchable loader called with `method: "POST"` and a `body` or form
|
|
172
|
+
data) runs the call uncached and warns in dev: the body is not in the key.
|
|
173
|
+
The method is not in the key either.
|
|
174
|
+
- **A `Request`** (such as `ctx.request`) keys by its URL: host, pathname and
|
|
175
|
+
the user-facing sorted search params, with the same internal-param exclusion
|
|
176
|
+
and `cache.searchParams` filter as the URL-keyed tiers. Headers, cookies and
|
|
177
|
+
the method are not in the key; read what you need outside and pass the values.
|
|
178
|
+
- **The request's `env`** (the object `ctx.env` returns) is left out of the key:
|
|
179
|
+
it is fixed per deployment. A binding passed on its own (`env.DB`) is a class
|
|
180
|
+
instance and runs uncached; pass `env` instead.
|
|
181
|
+
|
|
182
|
+
When a `ctx` is detected:
|
|
124
183
|
|
|
125
184
|
1. **Excluded from cache key** -- request-scoped, not meaningful for keying.
|
|
126
185
|
(The route-identifying fields read off `ctx` are still folded in -- see
|
|
127
186
|
"Route context is folded into the key" above.)
|
|
128
|
-
2. **Handle data captured on miss** -- side effects via `ctx.use(Handle)` are recorded
|
|
129
|
-
|
|
187
|
+
2. **Handle data captured on miss** -- side effects via `ctx.use(Handle)` are recorded:
|
|
188
|
+
the function's own pushes (and those of cached functions it calls), not the
|
|
189
|
+
handler's or loaders' pushes into the same request.
|
|
190
|
+
3. **Handle data replayed on hit** -- appended to the calling segment (for a
|
|
191
|
+
loader ctx, the segment that declares the loader), after what the handler
|
|
192
|
+
and loaders already pushed, as if the body had run (a layout and its page
|
|
193
|
+
calling the same function each get their copy). A
|
|
194
|
+
stale hit's background refresh records its pushes into the refreshed entry only.
|
|
195
|
+
4. **A loader the function reads shows its pushes once** -- pushes from a loader
|
|
196
|
+
read with `ctx.use(Loader)` inside the function are recorded under that
|
|
197
|
+
loader. Loaders stay live, so if the handler or a DSL loader also reads it,
|
|
198
|
+
that live run's pushes are the ones on the page: a live run before the hit
|
|
199
|
+
makes the replay skip them, and one after it replaces the replayed values in
|
|
200
|
+
place. A loader read only inside the function is replayed once, also when
|
|
201
|
+
a layout and its page both call the function.
|
|
130
202
|
|
|
131
203
|
```typescript
|
|
132
204
|
export async function getProductData(ctx) {
|
|
@@ -136,6 +208,17 @@ export async function getProductData(ctx) {
|
|
|
136
208
|
return await db.query("SELECT * FROM products");
|
|
137
209
|
}
|
|
138
210
|
// On hit: return value restored, breadcrumb replayed.
|
|
211
|
+
|
|
212
|
+
export async function getProduct(ctx) {
|
|
213
|
+
"use cache";
|
|
214
|
+
const category = await ctx.use(CategoryLoader); // pushes a category crumb
|
|
215
|
+
return db.product(ctx.params.id, category.id);
|
|
216
|
+
}
|
|
217
|
+
// Handler: `await getProduct(ctx); await ctx.use(CategoryLoader);`
|
|
218
|
+
// On hit: one category crumb, from the handler's live CategoryLoader run.
|
|
219
|
+
|
|
220
|
+
export const ProductLoader = createLoader(async (ctx) => getProduct(ctx));
|
|
221
|
+
// A loader ctx keys like the handler ctx: one entry per route, params and query.
|
|
139
222
|
```
|
|
140
223
|
|
|
141
224
|
## Request-Scoped Guards
|
|
@@ -153,27 +236,57 @@ const locale = cookies().get("locale")?.value ?? "en";
|
|
|
153
236
|
const data = await getCachedData(locale); // locale is now in the cache key
|
|
154
237
|
```
|
|
155
238
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
239
|
+
`ctx.get()` of a **non-cacheable variable** (`createVar({ cache: false })`, or
|
|
240
|
+
a value written with `ctx.set(key, value, { cache: false })`) throws the same
|
|
241
|
+
way, whether it goes through `getRequestContext().get()` or a `ctx` passed in
|
|
242
|
+
(handler, loader, middleware or response-route).
|
|
243
|
+
The key does not include the value, so the first caller's value would be
|
|
244
|
+
served to later callers. Read it before the call and pass it in:
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
const Tenant = createVar<string>({ cache: false });
|
|
248
|
+
|
|
249
|
+
async function getNav(tenant: string) {
|
|
250
|
+
"use cache";
|
|
251
|
+
return loadNav(tenant); // tenant is part of the key
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// in a handler or loader, outside the cached function
|
|
255
|
+
const nav = await getNav(ctx.get(Tenant)!);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Ordinary (cacheable) variables stay readable. Calling the cached function from
|
|
259
|
+
a loader does not exempt it: the loader re-runs on every request, but the
|
|
260
|
+
cached body does not.
|
|
261
|
+
|
|
262
|
+
The non-cacheable variable guard does not reach into LOADER bodies consumed
|
|
263
|
+
inside the cached function (`await ctx.use(loader)`): loaders always run
|
|
264
|
+
fresh, so their variable reads are exempt — but the CONSUMED VALUE is captured
|
|
265
|
+
into the shared cache entry like any other computed data. (`cookies()` and
|
|
266
|
+
`headers()` still throw there: that guard follows the cached body's whole
|
|
267
|
+
async chain, loaders included.) Same rule across `cache()` and the PPR shell:
|
|
160
268
|
handler/cached-scope consumption = baked copy, client-side `useLoader` = live
|
|
161
|
-
(the consumption-lane rule, `/rango` → Invariants).
|
|
269
|
+
(the consumption-lane rule, `/rango` → Invariants). The PPR shell is stricter
|
|
270
|
+
about the loader's own reads: during a capture, `cookies()`, `headers()`, and a
|
|
271
|
+
`{ cache: false }` variable read inside a handler-consumed loader refuse the
|
|
272
|
+
capture.
|
|
162
273
|
|
|
163
274
|
### Side-Effect Guards
|
|
164
275
|
|
|
165
276
|
These ctx methods **throw** inside a `"use cache"` function because their effects
|
|
166
277
|
are lost on cache hit (the function body is skipped):
|
|
167
278
|
|
|
168
|
-
- `ctx.set()` for passing values to children
|
|
169
|
-
- `ctx.
|
|
279
|
+
- `ctx.set()` for passing values to children (handler and middleware ctx)
|
|
280
|
+
- `ctx.headers.set()` and the other mutating `Headers` methods (handler and
|
|
281
|
+
middleware ctx), and a middleware's `ctx.header()`
|
|
282
|
+
- cookie writes and the request-context writers `header()`, `setCookie()`,
|
|
283
|
+
`deleteCookie()`, `setStatus()`, `onResponse()`
|
|
170
284
|
- `ctx.setTheme()`
|
|
171
285
|
- `ctx.setLocationState()`
|
|
172
|
-
- `ctx.onResponse()`
|
|
173
286
|
|
|
174
|
-
`ctx.get()` is
|
|
175
|
-
|
|
176
|
-
route-level `cache()`
|
|
287
|
+
`ctx.get()` is a read, not a side effect: it stays allowed for ordinary
|
|
288
|
+
variables and throws only for a non-cacheable one (see "Read Guards" above).
|
|
289
|
+
The same non-cacheable read throws inside a route-level `cache()` boundary.
|
|
177
290
|
|
|
178
291
|
The error message recommends two alternatives:
|
|
179
292
|
|
|
@@ -257,9 +370,8 @@ On cache miss, the function executes and the result is serialized inline (blocki
|
|
|
257
370
|
The cache **store write** (`setItem`) is deferred to `waitUntil` and does NOT block
|
|
258
371
|
the response.
|
|
259
372
|
|
|
260
|
-
On stale hit, stale data is returned immediately. Background revalidation
|
|
261
|
-
|
|
262
|
-
- store) runs entirely inside `waitUntil`.
|
|
373
|
+
On stale hit, stale data is returned immediately. Background revalidation
|
|
374
|
+
(re-execute + store write) runs entirely inside `waitUntil`.
|
|
263
375
|
|
|
264
376
|
| Phase | Blocks response? |
|
|
265
377
|
| ------------------------------------ | ---------------- |
|
|
@@ -356,75 +468,104 @@ decrypt; without it they fail until the entry expires or revalidates.
|
|
|
356
468
|
|
|
357
469
|
## Vite Transform
|
|
358
470
|
|
|
359
|
-
The
|
|
360
|
-
`registerCachedFunction()
|
|
471
|
+
The `@rangojs/router:use-cache` Vite plugin (part of `rango()`) detects the directive and wraps each cached
|
|
472
|
+
function with `registerCachedFunction(fn, functionId, profileName)` from
|
|
473
|
+
`@rangojs/router/cache-runtime`. You never call it yourself. Simplified output:
|
|
361
474
|
|
|
362
475
|
```typescript
|
|
363
|
-
// Input
|
|
364
|
-
"use cache"
|
|
476
|
+
// Input (file-level directive)
|
|
477
|
+
"use cache";
|
|
365
478
|
export async function getProducts() { ... }
|
|
366
479
|
|
|
367
|
-
// Output
|
|
368
|
-
import { registerCachedFunction } from
|
|
480
|
+
// Output (simplified)
|
|
481
|
+
import { registerCachedFunction } from "@rangojs/router/cache-runtime";
|
|
369
482
|
export const getProducts = registerCachedFunction(
|
|
370
483
|
async function getProducts() { ... },
|
|
371
|
-
"src/data/products.ts#getProducts",
|
|
372
|
-
"default"
|
|
484
|
+
"src/data/products.ts#getProducts", // a hash of this in production builds
|
|
485
|
+
"default",
|
|
373
486
|
);
|
|
374
487
|
```
|
|
375
488
|
|
|
376
|
-
|
|
489
|
+
A function-level directive (`"use cache: short"` inside the body) is hoisted out
|
|
490
|
+
of the function and wrapped the same way, with the profile name from the
|
|
491
|
+
directive.
|
|
377
492
|
|
|
378
|
-
|
|
379
|
-
// Input
|
|
380
|
-
export async function getProducts() {
|
|
381
|
-
"use cache: short";
|
|
382
|
-
return await db.query("...");
|
|
383
|
-
}
|
|
493
|
+
File-level rules:
|
|
384
494
|
|
|
385
|
-
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
return __rango_cached_getProducts();
|
|
395
|
-
}
|
|
396
|
-
```
|
|
495
|
+
- Only exports that are statically confirmed functions are wrapped
|
|
496
|
+
(`export async function foo() {}`, `export const foo = async () => {}`). Any
|
|
497
|
+
other export (a constant, or a factory call like `makeCached(fn)`) fails the
|
|
498
|
+
build — move it to another module.
|
|
499
|
+
- The default export of a file named `layout.tsx` or `template.tsx` (any
|
|
500
|
+
`.ts/.tsx/.js/.jsx` extension) is not wrapped: it receives `children`, which
|
|
501
|
+
cannot be part of a cache key.
|
|
502
|
+
- Functions carrying their own `"use server"` directive are server actions, not
|
|
503
|
+
cached functions, and are left alone.
|
|
397
504
|
|
|
398
505
|
## Backing Store
|
|
399
506
|
|
|
400
|
-
|
|
401
|
-
One store, one
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
`
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
`
|
|
413
|
-
|
|
414
|
-
(
|
|
507
|
+
`"use cache"` reads and writes the app-level store from `createRouter({ cache })`
|
|
508
|
+
— the same store `cache()` boundaries and cached loaders use. One store, one
|
|
509
|
+
configuration. (`Static()`/`Prerender()` output is not in this store; it is
|
|
510
|
+
built into the server bundle, see `/prerender`.)
|
|
511
|
+
|
|
512
|
+
Entries are tagged by the profile's `tags` plus any runtime `cacheTag(...tags)`
|
|
513
|
+
calls in the function body. `cacheTag` has two forms: inside a `"use cache"`
|
|
514
|
+
function it tags that entry; called during a request render outside
|
|
515
|
+
`"use cache"` it tags what the render stores: the enclosing route `cache()`
|
|
516
|
+
entry, a cached loader's own entry when called in its body, and the `ppr`
|
|
517
|
+
shell or document-cache entry built from them (on a `ppr` route, only when
|
|
518
|
+
shell material calls it). See `/caching` → "Tag-Based Invalidation". All
|
|
519
|
+
built-in stores (`MemorySegmentCacheStore`, `CFCacheStore`,
|
|
520
|
+
`VercelCacheStore`) index by tag. Invalidate on demand with
|
|
521
|
+
`updateTag(...tags)` (awaitable; for server actions) or
|
|
522
|
+
`revalidateTag(...tags)` (background, non-blocking; for route
|
|
523
|
+
handlers/webhooks). Both evict rather than mark stale, and the request that
|
|
524
|
+
calls either reads its own writes; `updateTag()` also waits for the durable
|
|
525
|
+
write. PPR shell reads on `CFCacheStore`/`VercelCacheStore` go through
|
|
526
|
+
per-isolate memos, which the invalidating user skips via the fresh-reads
|
|
527
|
+
cookie. For `CFCacheStore`, cross-colo invalidation needs a `kv` namespace
|
|
528
|
+
(markers live in that same namespace) or `tagPurge`. The separate
|
|
529
|
+
`revalidate()` export is the client-update control (which segments re-render
|
|
530
|
+
on a navigation or action), not a cache bust.
|
|
531
|
+
|
|
532
|
+
A route `cache()` entry stores the tags of the `"use cache"` reads inside it,
|
|
533
|
+
so `updateTag()` of one evicts the enclosing entry and any `ppr` shell or
|
|
534
|
+
document built from it. A loader's own `cache()` entry does the same for the
|
|
535
|
+
reads in its body. The exception is a `"use cache"` function called inside
|
|
536
|
+
another `"use cache"` function: its tags do not reach the enclosing entry, so
|
|
537
|
+
invalidating one of them neither evicts the outer entry nor keeps this request
|
|
538
|
+
from joining or storing an outer call that started before the invalidation
|
|
539
|
+
([#980](https://github.com/rangojs/rango/issues/980)).
|
|
415
540
|
|
|
416
541
|
## Interaction with Other Caching
|
|
417
542
|
|
|
418
|
-
| Mechanism
|
|
419
|
-
|
|
|
420
|
-
| `"use cache"`
|
|
421
|
-
| `cache()`
|
|
422
|
-
| `cache(
|
|
423
|
-
| `
|
|
424
|
-
| `
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
a
|
|
430
|
-
|
|
543
|
+
| Mechanism | Granularity | When | Use case |
|
|
544
|
+
| ---------------- | ------------------ | ---------- | ----------------------------------------------------- |
|
|
545
|
+
| `"use cache"` | Function/component | Runtime | Cache individual data fetches or components |
|
|
546
|
+
| loader `cache()` | One loader's data | Runtime | Cache a loader result while the page stays live |
|
|
547
|
+
| `cache()` DSL | Route segment tree | Runtime | Cache entire route subtrees with children |
|
|
548
|
+
| `ppr` | HTML shell | Runtime | Serve a cached HTML shell, resume live holes (`/ppr`) |
|
|
549
|
+
| `Static()` | One segment | Build-time | Render once at build, no params |
|
|
550
|
+
| `Prerender()` | Route segment tree | Build-time | Pre-render known params, optional live fallback |
|
|
551
|
+
|
|
552
|
+
Inside a `ppr` shell, a `"use cache"` value that renders as shell material is
|
|
553
|
+
pinned at capture time for the life of that shell (see `/ppr` → Pitfalls). A
|
|
554
|
+
hole's own read (in a loader without `ssr: false` under `loading()` or an
|
|
555
|
+
inline `<Suspense>`) returns the current entry, not the shell's copy, unless a
|
|
556
|
+
loader the capture ran (an `ssr: false` loader, or one a handler awaits) read
|
|
557
|
+
the same entry.
|
|
558
|
+
|
|
559
|
+
## Dev Mode and tests
|
|
560
|
+
|
|
561
|
+
The transform runs in dev exactly as in production, but caching only happens
|
|
562
|
+
when the router has a store: there is no implicit dev store. Configure one (a
|
|
563
|
+
`MemorySegmentCacheStore` is the usual dev/test choice) to see cache hits
|
|
564
|
+
locally.
|
|
565
|
+
|
|
566
|
+
With no item-capable store configured, a `"use cache"` function simply runs on
|
|
567
|
+
every call. The request-scoped guards (`cookies()`, `ctx.set()`, …) are not
|
|
568
|
+
active on that uncached path, so a guard violation only shows up once a store is
|
|
569
|
+
configured — test with a store. Under Vitest, `runLoader`, `renderHandler`, and
|
|
570
|
+
the other `@rangojs/router/testing` helpers take `{ cacheStore, cacheProfiles }`
|
|
571
|
+
and warn when a cached function ran without one (see `/testing`).
|
package/skills/vercel/SKILL.md
CHANGED
|
@@ -6,7 +6,11 @@ argument-hint:
|
|
|
6
6
|
|
|
7
7
|
# Vercel deployment
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
This skill covers deploying a Rango app to Vercel Functions: the `vercel`
|
|
10
|
+
preset, function settings, the Runtime Cache store, host routers, tracing, and
|
|
11
|
+
local validation. For router options in general see `/router-setup`.
|
|
12
|
+
|
|
13
|
+
The `vercel` preset builds like the `node` preset (Vercel runs Node Functions, not Workers): rango owns the RSC entry (it imports the named `router` export of your `createRouter()` module), folds `process.env.NODE_ENV` for the SSR/RSC build, and after `vite build` assembles a `.vercel/output` directory (Build Output API v3) from `dist/`: a single streaming Node Function plus the static client assets.
|
|
10
14
|
|
|
11
15
|
## Deployment boundary
|
|
12
16
|
|
|
@@ -30,27 +34,27 @@ checklist before adding shared-cache headers.
|
|
|
30
34
|
## Setup
|
|
31
35
|
|
|
32
36
|
```bash
|
|
33
|
-
|
|
37
|
+
pnpm add @vercel/functions
|
|
34
38
|
```
|
|
35
39
|
|
|
36
40
|
```typescript
|
|
37
41
|
// vite.config.ts
|
|
38
42
|
import { defineConfig } from "vite";
|
|
39
|
-
import react from "@vitejs/plugin-react";
|
|
40
43
|
import { rango } from "@rangojs/router/vite";
|
|
41
44
|
|
|
42
45
|
export default defineConfig({
|
|
43
|
-
plugins: [
|
|
46
|
+
plugins: [rango({ preset: "vercel" })], // add react() first if you use @vitejs/plugin-react
|
|
44
47
|
});
|
|
45
48
|
```
|
|
46
49
|
|
|
47
50
|
`@vercel/functions` is required: it backs the generated function launcher (`waitUntil`) and `VercelCacheStore`. The build fails with a clear error if it is missing.
|
|
48
51
|
|
|
49
|
-
`vite build` produces `.vercel/output`; deploy with the Vercel CLI (`vercel deploy --prebuilt`) or via Git integration.
|
|
52
|
+
`vite build` produces `.vercel/output`; deploy with the Vercel CLI (`vercel deploy --prebuilt`) or via Git integration. The generated routing serves files first (hashed assets under the assets directory get `Cache-Control: public, max-age=31536000, immutable`) and sends everything else to the function.
|
|
50
53
|
|
|
51
54
|
## Function configuration
|
|
52
55
|
|
|
53
|
-
Per-function knobs go under `vercel` and are written into `.vc-config.json
|
|
56
|
+
Per-function knobs go under `vercel` and are written into `.vc-config.json`
|
|
57
|
+
(`functionName` also sets the `config.json` route):
|
|
54
58
|
|
|
55
59
|
```typescript
|
|
56
60
|
rango({
|
|
@@ -60,11 +64,15 @@ rango({
|
|
|
60
64
|
maxDuration: 30, // seconds, default
|
|
61
65
|
memory: 1024, // MB (platform default when omitted)
|
|
62
66
|
regions: ["fra1"], // pin regions (platform default when omitted)
|
|
63
|
-
functionName: "index", // the <name>.func dir + config.json route
|
|
67
|
+
functionName: "index", // the <name>.func dir + config.json route (default)
|
|
64
68
|
},
|
|
65
69
|
});
|
|
66
70
|
```
|
|
67
71
|
|
|
72
|
+
`runtime` must be a `nodejs*` runtime. The preset emits a Node serverless
|
|
73
|
+
function, so the build fails on any other value; the Edge runtime is not
|
|
74
|
+
supported.
|
|
75
|
+
|
|
68
76
|
## Runtime Cache
|
|
69
77
|
|
|
70
78
|
`VercelCacheStore` wraps the Vercel Runtime Cache for segment, item, response,
|
|
@@ -72,6 +80,7 @@ and PPR shell families. Locally (no `process.env.VERCEL`) fall back to an
|
|
|
72
80
|
in-memory store so dev/preview work without the platform:
|
|
73
81
|
|
|
74
82
|
```typescript
|
|
83
|
+
import { createRouter } from "@rangojs/router";
|
|
75
84
|
import {
|
|
76
85
|
MemorySegmentCacheStore,
|
|
77
86
|
VercelCacheStore,
|
|
@@ -80,12 +89,24 @@ import { getCache, waitUntil } from "@vercel/functions";
|
|
|
80
89
|
|
|
81
90
|
const defaults = { ttl: 60, swr: 300 };
|
|
82
91
|
const memoryStore = new MemorySegmentCacheStore({ defaults });
|
|
92
|
+
// One handle per process: getCache() resolves the platform cache on every
|
|
93
|
+
// call, and VercelCacheStore keeps its PPR shell and tag-marker memos per
|
|
94
|
+
// handle. After updateTag(), the same process misses at once, the rest of the
|
|
95
|
+
// region once its marker memo refreshes (memo.markerMaxStaleMs, default 2000),
|
|
96
|
+
// and the mutating user at once (the fresh-reads cookie skips the memos).
|
|
97
|
+
// Another region serves a memoized shell until memo.shellMs (default 2000)
|
|
98
|
+
// passes, since the tag markers are regional and only expireTag is global. A
|
|
99
|
+
// platform expireTag issued outside rango writes no marker, so every memo
|
|
100
|
+
// serves the shell until its window passes.
|
|
101
|
+
const runtimeCache = process.env.VERCEL
|
|
102
|
+
? getCache({ namespace: process.env.VERCEL_DEPLOYMENT_ID })
|
|
103
|
+
: undefined;
|
|
83
104
|
|
|
84
105
|
function resolveCache() {
|
|
85
|
-
if (
|
|
106
|
+
if (runtimeCache) {
|
|
86
107
|
return {
|
|
87
108
|
store: new VercelCacheStore({
|
|
88
|
-
cache:
|
|
109
|
+
cache: runtimeCache,
|
|
89
110
|
waitUntil,
|
|
90
111
|
defaults,
|
|
91
112
|
}),
|
|
@@ -97,7 +118,7 @@ function resolveCache() {
|
|
|
97
118
|
export const router = createRouter({ cache: resolveCache }).routes(/* ... */);
|
|
98
119
|
```
|
|
99
120
|
|
|
100
|
-
The cache factory receives `(env, ctx)
|
|
121
|
+
The cache factory receives `(env, ctx)`: on Vercel `env` is `process.env` and `ctx` is `{ waitUntil }`; off-platform `ctx` is `undefined`. Cache semantics and tag invalidation: `/caching`, `/cache-guide`.
|
|
101
122
|
|
|
102
123
|
## Host routers (multi-app)
|
|
103
124
|
|
|
@@ -121,7 +142,7 @@ hostRouter.host(["."]).lazy(() => import("./apps/site/handler.js"));
|
|
|
121
142
|
export default hostRouter; // the instance
|
|
122
143
|
```
|
|
123
144
|
|
|
124
|
-
`{ env, ctx }` is threaded unchanged from the function to each matched sub-app's handler and its `cache(env, ctx)` factory. See
|
|
145
|
+
`{ env, ctx }` is threaded unchanged from the function to each matched sub-app's handler and its `cache(env, ctx)` factory. See `/host-router` for sub-app structure and routing patterns.
|
|
125
146
|
|
|
126
147
|
## Tracing (custom spans)
|
|
127
148
|
|
|
@@ -138,12 +159,13 @@ registerOTel({ serviceName: "my-app" });
|
|
|
138
159
|
export const tracing = createVercelTracing();
|
|
139
160
|
|
|
140
161
|
// router.tsx — importing `tracing` runs instrumentation.ts (and registerOTel)
|
|
162
|
+
import { createRouter } from "@rangojs/router";
|
|
141
163
|
import { tracing } from "./instrumentation.js";
|
|
142
164
|
export const router = createRouter({ tracing }).routes(/* ... */);
|
|
143
165
|
```
|
|
144
166
|
|
|
145
|
-
`createVercelTracing(opts?)` takes `{ enabled, spans, tracerName, tracer }`
|
|
167
|
+
`createVercelTracing(opts?)` takes `{ enabled, spans, tracerName, tracer }` (`tracerName` defaults to `"rango"`; `tracer` overrides the global tracer) and emits the same phases as `createCloudflareTracing`: `rango.request`, `rango.middleware`, `rango.action`, `rango.loader`, `rango.handler`, `rango.render`, `rango.ssr`, `rango.response`, and `rango.background`. Caveats: Node-runtime only (Vercel custom spans are unsupported on Edge); `registerOTel()` must run before the first request; `@vercel/otel` is what unlocks Vercel Session Tracing + Trace Drains. The deploy bundles `@vercel/otel` and its `@opentelemetry/*` peers into the function (no `node_modules` at runtime), so they must be installed. The Rango repository's `examples/vercel-basic` (not shipped in this package) has a worked setup; `/observability` covers the cross-platform tracing model.
|
|
146
168
|
|
|
147
169
|
## Local validation without deploying
|
|
148
170
|
|
|
149
|
-
`vite preview`
|
|
171
|
+
`vite preview` does not run the assembled function. To exercise it locally, serve `.vercel/output` with filesystem-then-function routing (static files first, everything else to `functions/<name>.func/index.mjs`). The Rango repository's `examples/vercel-basic` (not shipped in this package) has two scripts for this: `scripts/preview.mjs` (`pnpm preview:vercel`) serves it in place, and `scripts/smoke.mjs` copies it to a temp dir outside the repo for a faithful deploy test (isolated filesystem, ESM, self-contained bundle). Both share `scripts/serve-vercel-output.mjs`.
|