@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
package/dist/testing/vitest.js
CHANGED
|
@@ -1,6 +1,233 @@
|
|
|
1
1
|
// src/testing/vitest.ts
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
|
+
|
|
5
|
+
// src/vite/plugins/use-cache-transform.ts
|
|
6
|
+
import path2 from "node:path";
|
|
7
|
+
|
|
8
|
+
// src/vite/plugins/expose-id-utils.ts
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import crypto from "node:crypto";
|
|
11
|
+
function normalizePath(p) {
|
|
12
|
+
return p.split(path.sep).join("/");
|
|
13
|
+
}
|
|
14
|
+
function hashId(filePath, exportName) {
|
|
15
|
+
const input = `${filePath}#${exportName}`;
|
|
16
|
+
const hash = crypto.createHash("sha256").update(input).digest("hex");
|
|
17
|
+
return `${hash.slice(0, 8)}#${exportName}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/vite/plugins/use-cache-transform.ts
|
|
21
|
+
var CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
|
|
22
|
+
var LAYOUT_TEMPLATE_PATTERN = /\/(layout|template)\.(tsx?|jsx?)$/;
|
|
23
|
+
var USE_CACHE_DIRECTIVE_RE = /^use cache(:\s*[\w-]+)?$/;
|
|
24
|
+
function isUseCacheCandidate(code, id) {
|
|
25
|
+
return (
|
|
26
|
+
// Quick bail: no "use cache" in source
|
|
27
|
+
code.includes("use cache") && // Skip node_modules and virtual modules
|
|
28
|
+
!id.includes("/node_modules/") && !id.startsWith("\0") && // Only JS/TS files
|
|
29
|
+
/\.(tsx?|jsx?|mjs)$/.test(id)
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
var rscTransforms = null;
|
|
33
|
+
async function transformUseCache(code, id, options) {
|
|
34
|
+
if (!rscTransforms) {
|
|
35
|
+
try {
|
|
36
|
+
rscTransforms = await import("@vitejs/plugin-rsc/transforms");
|
|
37
|
+
} catch (error) {
|
|
38
|
+
options.onTransformsError?.(error);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const { hasDirective, transformWrapExport, transformHoistInlineDirective } = rscTransforms;
|
|
43
|
+
let ast;
|
|
44
|
+
try {
|
|
45
|
+
const { parseAst } = await import("vite");
|
|
46
|
+
ast = parseAst(code, { lang: "tsx" });
|
|
47
|
+
} catch {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
stripNullDirectiveFields(ast);
|
|
51
|
+
const filePath = normalizePath(path2.relative(options.root, id));
|
|
52
|
+
const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
|
|
53
|
+
if (hasDirective(ast.body, "use cache")) {
|
|
54
|
+
return transformFileLevelUseCache(
|
|
55
|
+
code,
|
|
56
|
+
ast,
|
|
57
|
+
filePath,
|
|
58
|
+
id,
|
|
59
|
+
options.isBuild,
|
|
60
|
+
isLayoutOrTemplate,
|
|
61
|
+
transformWrapExport,
|
|
62
|
+
hasDirective
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
const functionResult = transformFunctionLevelUseCache(
|
|
66
|
+
code,
|
|
67
|
+
ast,
|
|
68
|
+
filePath,
|
|
69
|
+
id,
|
|
70
|
+
options.isBuild,
|
|
71
|
+
transformHoistInlineDirective
|
|
72
|
+
);
|
|
73
|
+
warnOnNearMissDirectives(ast, id, options.warn);
|
|
74
|
+
if (functionResult) return functionResult;
|
|
75
|
+
}
|
|
76
|
+
async function transformFileLevelUseCache(code, ast, filePath, sourceId, isBuild, isLayoutOrTemplate, transformWrapExport, hasDirective) {
|
|
77
|
+
const unconfirmedExports = [];
|
|
78
|
+
const { exportNames, output } = transformWrapExport(code, ast, {
|
|
79
|
+
runtime: (value, name) => {
|
|
80
|
+
const funcId = isBuild ? hashId(filePath, name) : `${filePath}#${name}`;
|
|
81
|
+
return `__rango_registerCachedFunction(${value}, ${JSON.stringify(funcId)}, "default")`;
|
|
82
|
+
},
|
|
83
|
+
rejectNonAsyncFunction: false,
|
|
84
|
+
filter: (name, meta) => {
|
|
85
|
+
if (name === "default" && isLayoutOrTemplate) return false;
|
|
86
|
+
if (name.startsWith("$$hoist_")) return false;
|
|
87
|
+
if (isHoistedServerReferenceRebind(meta.valueNode)) return false;
|
|
88
|
+
if (functionHasUseServerDirective(meta.valueNode, hasDirective)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
if (meta.isFunction !== true) {
|
|
92
|
+
unconfirmedExports.push(name);
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
if (unconfirmedExports.length > 0) {
|
|
99
|
+
const plural = unconfirmedExports.length > 1;
|
|
100
|
+
throw new Error(
|
|
101
|
+
`[rango:use-cache] File-level "use cache" in ${sourceId} only wraps exports that are statically-confirmed functions. ${plural ? "These exports are" : "This export is"} not: ${unconfirmedExports.map((n) => `"${n}"`).join(", ")}. Declare them directly (export async function foo() {} or export const foo = async () => {}). A factory or otherwise statically-indeterminate initializer (export const foo = makeCached(fn)) is rejected even if it returns a function at runtime -- rewrite it as a direct async function, or move non-function exports to a separate module.`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (exportNames.length === 0) {
|
|
105
|
+
const { default: MagicString } = await import("magic-string");
|
|
106
|
+
const s = new MagicString(code);
|
|
107
|
+
const directive2 = findFileLevelDirective(ast);
|
|
108
|
+
if (directive2) {
|
|
109
|
+
s.overwrite(
|
|
110
|
+
directive2.start,
|
|
111
|
+
directive2.end,
|
|
112
|
+
`/* "use cache" -- wrapped by rango */`
|
|
113
|
+
);
|
|
114
|
+
return {
|
|
115
|
+
code: s.toString(),
|
|
116
|
+
map: s.generateMap({ source: sourceId, hires: "boundary" })
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
output.prepend(
|
|
122
|
+
`import { registerCachedFunction as __rango_registerCachedFunction } from ${JSON.stringify(CACHE_RUNTIME_IMPORT)};
|
|
123
|
+
`
|
|
124
|
+
);
|
|
125
|
+
const directive = findFileLevelDirective(ast);
|
|
126
|
+
if (directive) {
|
|
127
|
+
output.overwrite(
|
|
128
|
+
directive.start,
|
|
129
|
+
directive.end,
|
|
130
|
+
`/* "use cache" -- wrapped by rango */`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
code: output.toString(),
|
|
135
|
+
map: output.generateMap({ source: sourceId, hires: "boundary" })
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function transformFunctionLevelUseCache(code, ast, filePath, sourceId, isBuild, transformHoistInlineDirective) {
|
|
139
|
+
try {
|
|
140
|
+
const { output, names } = transformHoistInlineDirective(code, ast, {
|
|
141
|
+
directive: USE_CACHE_DIRECTIVE_RE,
|
|
142
|
+
runtime: (value, name, meta) => {
|
|
143
|
+
const funcId = isBuild ? hashId(filePath, name) : `${filePath}#${name}`;
|
|
144
|
+
const profileMatch = meta.directiveMatch[1];
|
|
145
|
+
const profileName = profileMatch ? profileMatch.replace(/^:\s*/, "").trim() : "default";
|
|
146
|
+
return `__rango_registerCachedFunction(${value}, ${JSON.stringify(funcId)}, ${JSON.stringify(profileName)})`;
|
|
147
|
+
},
|
|
148
|
+
rejectNonAsyncFunction: false
|
|
149
|
+
});
|
|
150
|
+
if (names.length === 0) return;
|
|
151
|
+
output.prepend(
|
|
152
|
+
`import { registerCachedFunction as __rango_registerCachedFunction } from ${JSON.stringify(CACHE_RUNTIME_IMPORT)};
|
|
153
|
+
`
|
|
154
|
+
);
|
|
155
|
+
return {
|
|
156
|
+
code: output.toString(),
|
|
157
|
+
map: output.generateMap({ source: sourceId, hires: "boundary" })
|
|
158
|
+
};
|
|
159
|
+
} catch {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function stripNullDirectiveFields(node) {
|
|
164
|
+
if (!node || typeof node !== "object") return;
|
|
165
|
+
const rec = node;
|
|
166
|
+
if (rec.type === "ExpressionStatement" && typeof rec.directive !== "string") {
|
|
167
|
+
delete rec.directive;
|
|
168
|
+
}
|
|
169
|
+
for (const value of Object.values(rec)) {
|
|
170
|
+
if (Array.isArray(value)) {
|
|
171
|
+
for (const item of value) stripNullDirectiveFields(item);
|
|
172
|
+
} else if (value && typeof value === "object" && "type" in value) {
|
|
173
|
+
stripNullDirectiveFields(value);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function isHoistedServerReferenceRebind(valueNode) {
|
|
178
|
+
if (!valueNode || valueNode.type !== "CallExpression") return false;
|
|
179
|
+
const first = valueNode.arguments[0];
|
|
180
|
+
return first !== void 0 && first.type === "Identifier" && first.name.startsWith("$$hoist_");
|
|
181
|
+
}
|
|
182
|
+
function functionHasUseServerDirective(valueNode, hasDirective) {
|
|
183
|
+
if (!valueNode || !("body" in valueNode)) return false;
|
|
184
|
+
const { body } = valueNode;
|
|
185
|
+
if (!body || Array.isArray(body) || body.type !== "BlockStatement") {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
return hasDirective(
|
|
189
|
+
body.body,
|
|
190
|
+
"use server"
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
function findFileLevelDirective(ast) {
|
|
194
|
+
for (const node of ast.body ?? []) {
|
|
195
|
+
if (node.type === "ExpressionStatement" && node.expression?.type === "Literal" && typeof node.expression.value === "string" && node.expression.value.startsWith("use cache")) {
|
|
196
|
+
return { start: node.start, end: node.end };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
var NEAR_MISS_RE = /^use cache:\s*.+$/;
|
|
202
|
+
function warnOnNearMissDirectives(ast, fileId, warn) {
|
|
203
|
+
const visit = (node) => {
|
|
204
|
+
if (!node || typeof node !== "object") return;
|
|
205
|
+
if (node.type === "ExpressionStatement" && node.expression?.type === "Literal" && typeof node.expression.value === "string") {
|
|
206
|
+
const value = node.expression.value;
|
|
207
|
+
if (value.startsWith("use cache") && NEAR_MISS_RE.test(value) && !USE_CACHE_DIRECTIVE_RE.test(value)) {
|
|
208
|
+
const profilePart = value.slice("use cache:".length).trim();
|
|
209
|
+
warn(
|
|
210
|
+
`[rango:use-cache] "${value}" in ${fileId} has an invalid profile name "${profilePart}". Profile names must match [a-zA-Z0-9_-]+. This directive will be ignored.`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
for (const key of Object.keys(node)) {
|
|
215
|
+
const child = node[key];
|
|
216
|
+
if (Array.isArray(child)) {
|
|
217
|
+
for (const item of child) {
|
|
218
|
+
visit(item);
|
|
219
|
+
}
|
|
220
|
+
} else if (child && typeof child === "object" && child.type) {
|
|
221
|
+
visit(child);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
for (const node of ast.body ?? []) {
|
|
226
|
+
visit(node);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/testing/vitest.ts
|
|
4
231
|
function here(relativeFromRoot) {
|
|
5
232
|
return fileURLToPath(new URL(`../../${relativeFromRoot}`, import.meta.url));
|
|
6
233
|
}
|
|
@@ -94,9 +321,34 @@ function rangoUseClientTransform() {
|
|
|
94
321
|
}
|
|
95
322
|
};
|
|
96
323
|
}
|
|
324
|
+
function rangoUseCacheTransform() {
|
|
325
|
+
let root = process.cwd();
|
|
326
|
+
return {
|
|
327
|
+
name: "rango:testing-use-cache",
|
|
328
|
+
enforce: "post",
|
|
329
|
+
configResolved(config) {
|
|
330
|
+
root = config.root;
|
|
331
|
+
},
|
|
332
|
+
async transform(code, id) {
|
|
333
|
+
if (!isUseCacheCandidate(code, id)) return void 0;
|
|
334
|
+
return transformUseCache(code, id, {
|
|
335
|
+
root,
|
|
336
|
+
isBuild: false,
|
|
337
|
+
warn: (message) => this.warn(message),
|
|
338
|
+
onTransformsError: (error) => {
|
|
339
|
+
throw new Error(
|
|
340
|
+
`rangoUseCacheTransform: @vitejs/plugin-rsc/transforms failed to load, so "use cache" functions in ${id} would run unwrapped.`,
|
|
341
|
+
{ cause: error }
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
97
348
|
export {
|
|
98
349
|
rangoInlineDeps,
|
|
99
350
|
rangoTestAliases,
|
|
100
351
|
rangoTestConfig,
|
|
352
|
+
rangoUseCacheTransform,
|
|
101
353
|
rangoUseClientTransform
|
|
102
354
|
};
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
*/
|
|
13
13
|
/** Default prefix when `stateCookiePrefix` is unset or empty after sanitization. */
|
|
14
14
|
export declare const DEFAULT_STATE_COOKIE_PREFIX = "rango-state";
|
|
15
|
+
/**
|
|
16
|
+
* The fresh-reads cookie name for a router's resolved state cookie name
|
|
17
|
+
* (`{prefix}_{routerId}`; the prefix never contains `_`, see
|
|
18
|
+
* router/state-cookie-name.ts).
|
|
19
|
+
*/
|
|
20
|
+
export declare function freshReadsCookieName(stateCookieName: string): string;
|
|
15
21
|
/** Internal response header carrying the keepClientCache() directive. */
|
|
16
22
|
export declare const KEEP_CACHE_HEADER = "x-rango-keep-cache";
|
|
17
23
|
/** True for a per-client signal header. */
|
|
@@ -18,6 +18,22 @@ export declare function buildHistoryState(userState: unknown, routerState?: {
|
|
|
18
18
|
* relying on the Navigation API.
|
|
19
19
|
*/
|
|
20
20
|
export declare function pushHistoryWithIdx(state: Record<string, unknown> | null, url: string, replace: boolean): void;
|
|
21
|
+
/**
|
|
22
|
+
* The forced-MISS reload marker (`SHELL_MISS_PARAM` in rsc/shell-serve.ts,
|
|
23
|
+
* which the browser bundle must not import): a degraded PPR shell HIT
|
|
24
|
+
* reloads the page with it, and the server renders a marked request like a
|
|
25
|
+
* cache miss.
|
|
26
|
+
*/
|
|
27
|
+
export declare const SHELL_MISS_MARKER: string;
|
|
28
|
+
/**
|
|
29
|
+
* Drop the forced-MISS marker from the address bar at boot, keeping the
|
|
30
|
+
* history entry's state (initBrowserApp calls it before any location read).
|
|
31
|
+
* Left in place, a refresh or a shared link would stay off the shell and the
|
|
32
|
+
* app's search params would carry it. The server strips the same marker from
|
|
33
|
+
* the request before rendering (shell-serve.ts withoutShellMissMarker), so
|
|
34
|
+
* the page was rendered for the clean URL and the hydrating client agrees.
|
|
35
|
+
*/
|
|
36
|
+
export declare function stripShellMissMarker(): void;
|
|
21
37
|
/**
|
|
22
38
|
* Merge server-set location state into the current history entry.
|
|
23
39
|
* Replaces the current history state and dispatches notification event
|
|
@@ -22,6 +22,12 @@ interface CommitOptions {
|
|
|
22
22
|
cacheOnly?: boolean;
|
|
23
23
|
/** Server-set location state to merge into history.pushState */
|
|
24
24
|
serverState?: Record<string, unknown>;
|
|
25
|
+
/**
|
|
26
|
+
* Back/forward: history is already at this entry. Its state (scroll key,
|
|
27
|
+
* location state, idx) stays as the browser restored it; serverState is
|
|
28
|
+
* merged in. The page left was saved by handleTraversalStart.
|
|
29
|
+
*/
|
|
30
|
+
traversal?: boolean;
|
|
25
31
|
}
|
|
26
32
|
/**
|
|
27
33
|
* Options that can override the pre-configured commit settings
|
|
@@ -2,6 +2,17 @@ import type { NavigationStore, NavigationClient, UpdateSubscriber, ResolvedSegme
|
|
|
2
2
|
import type { ReactNode } from "react";
|
|
3
3
|
import type { RenderSegmentsOptions } from "../segment-system.js";
|
|
4
4
|
import type { BoundTransaction } from "./navigation-transaction.js";
|
|
5
|
+
import type { NavigationUpdate } from "./types.js";
|
|
6
|
+
export declare function shouldStartViewTransition(segments: ResolvedSegment[]): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Transition commit. Every held commit announces the loader streams the
|
|
9
|
+
* committed tree is still receiving (loader-store.ts announcePendingStreams)
|
|
10
|
+
* so held useLoader readers pin isLoading:true until this transition lands;
|
|
11
|
+
* settled/cached segments make it a no-op. Urgent commits call onUpdate
|
|
12
|
+
* directly: nothing is held there, a reader of a pending stream suspends to
|
|
13
|
+
* its fallback. Also used by renderRoute's navigate() (testing/render-route.tsx).
|
|
14
|
+
*/
|
|
15
|
+
export declare function commitInTransition(onUpdate: UpdateSubscriber, segments: readonly ResolvedSegment[], update: NavigationUpdate, transitionTypes: readonly string[]): void;
|
|
5
16
|
/**
|
|
6
17
|
* Configuration for creating a partial updater
|
|
7
18
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Props for ScrollRestoration
|
|
2
|
+
* Props for `<Html.ScrollRestoration />`.
|
|
3
3
|
*/
|
|
4
4
|
export interface ScrollRestorationProps {
|
|
5
5
|
/**
|
|
@@ -20,10 +20,10 @@ export interface ScrollRestorationProps {
|
|
|
20
20
|
* const byPathname = (location) => location.pathname;
|
|
21
21
|
*
|
|
22
22
|
* // Restore based on pathname (same URL = same scroll)
|
|
23
|
-
* <ScrollRestoration getKey={byPathname} />
|
|
23
|
+
* <Html.ScrollRestoration getKey={byPathname} />
|
|
24
24
|
*
|
|
25
25
|
* // Restore based on unique history entry (default)
|
|
26
|
-
* // <ScrollRestoration /> — omit getKey to use location.key
|
|
26
|
+
* // <Html.ScrollRestoration /> — omit getKey to use location.key
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
29
|
getKey?: (location: {
|
|
@@ -34,7 +34,7 @@ export interface ScrollRestorationProps {
|
|
|
34
34
|
}) => string;
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
|
-
* ScrollRestoration
|
|
37
|
+
* Public as `Html.ScrollRestoration` (`@rangojs/router/client`).
|
|
38
38
|
*
|
|
39
39
|
* Enables scroll position restoration across navigations:
|
|
40
40
|
* - Saves scroll positions to sessionStorage
|
|
@@ -51,7 +51,7 @@ export interface ScrollRestorationProps {
|
|
|
51
51
|
* return (
|
|
52
52
|
* <html>
|
|
53
53
|
* <body>
|
|
54
|
-
* <ScrollRestoration />
|
|
54
|
+
* <Html.ScrollRestoration />
|
|
55
55
|
* {children}
|
|
56
56
|
* </body>
|
|
57
57
|
* </html>
|
|
@@ -63,7 +63,7 @@ export declare function ScrollRestoration({ getKey }: ScrollRestorationProps): n
|
|
|
63
63
|
/**
|
|
64
64
|
* Hook to initialize scroll restoration
|
|
65
65
|
*
|
|
66
|
-
* Alternative to
|
|
66
|
+
* Alternative to `<Html.ScrollRestoration />` for more control.
|
|
67
67
|
*
|
|
68
68
|
* @example
|
|
69
69
|
* ```tsx
|
|
@@ -126,7 +126,7 @@ export interface RangoProps {
|
|
|
126
126
|
*
|
|
127
127
|
* @example
|
|
128
128
|
* ```tsx
|
|
129
|
-
* import { initBrowserApp, Rango } from "
|
|
129
|
+
* import { initBrowserApp, Rango } from "@rangojs/router/browser";
|
|
130
130
|
* import { rscStream } from "rsc-html-stream/client";
|
|
131
131
|
* import * as rscBrowser from "@vitejs/plugin-rsc/browser";
|
|
132
132
|
*
|
|
@@ -84,6 +84,13 @@ export declare function scrollToTop(): void;
|
|
|
84
84
|
* - Ensures new history entry has a key
|
|
85
85
|
*/
|
|
86
86
|
export declare function handleNavigationStart(): void;
|
|
87
|
+
/**
|
|
88
|
+
* Handle scroll for a back/forward traversal, at popstate before anything
|
|
89
|
+
* renders: save the page being left under ITS key (history has already moved,
|
|
90
|
+
* so saveCurrentScrollPosition would use the destination's key) and track the
|
|
91
|
+
* destination as the displayed entry.
|
|
92
|
+
*/
|
|
93
|
+
export declare function handleTraversalStart(): void;
|
|
87
94
|
/**
|
|
88
95
|
* Handle scroll after navigation completes.
|
|
89
96
|
* @param options.restore - If true, restore saved position (for popstate)
|
|
@@ -22,10 +22,27 @@
|
|
|
22
22
|
* registration below wires assertNotInsideCacheExec to this scope without
|
|
23
23
|
* taint.ts importing it.
|
|
24
24
|
*/
|
|
25
|
+
/**
|
|
26
|
+
* One "use cache" execution. Identity matters: the execution's handle capture
|
|
27
|
+
* (cache-runtime.ts) records only pushes from its own chain, including cached
|
|
28
|
+
* functions it calls (`parent` links them), not concurrent pushes into the
|
|
29
|
+
* same request store.
|
|
30
|
+
*/
|
|
31
|
+
export interface CacheExecScope {
|
|
32
|
+
readonly parent?: CacheExecScope;
|
|
33
|
+
}
|
|
25
34
|
/**
|
|
26
35
|
* Run fn with the "use cache" execution scope active. Continuations spawned
|
|
27
36
|
* from fn's synchronous kickoff inherit the scope; parallel chains do not.
|
|
37
|
+
* Pass `scope` (createCacheExecScope) to test membership with
|
|
38
|
+
* isInCacheExecChain.
|
|
28
39
|
*/
|
|
29
|
-
export declare function runWithCacheExecScope<T>(fn: () => T): T;
|
|
40
|
+
export declare function runWithCacheExecScope<T>(fn: () => T, scope?: CacheExecScope): T;
|
|
41
|
+
/** A scope nested in the calling chain's current one, if any. */
|
|
42
|
+
export declare function createCacheExecScope(): CacheExecScope;
|
|
30
43
|
/** True when the calling async chain is inside a "use cache" body. */
|
|
31
44
|
export declare function isInsideCacheExecScope(): boolean;
|
|
45
|
+
/** The calling chain's innermost "use cache" scope, if any. */
|
|
46
|
+
export declare function getCacheExecScope(): CacheExecScope | undefined;
|
|
47
|
+
/** True when the calling async chain runs inside `scope`, at any depth. */
|
|
48
|
+
export declare function isInCacheExecChain(scope: CacheExecScope): boolean;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Shared Cache Key Utilities
|
|
3
3
|
*
|
|
4
4
|
* Deterministic normalization of search params and route params
|
|
5
|
-
* for cache key generation
|
|
6
|
-
* document-cache, and loader-cache.
|
|
5
|
+
* for cache key generation, and the composition of nested cache() keys.
|
|
6
|
+
* Used by cache-runtime, cache-scope, document-cache, and loader-cache.
|
|
7
7
|
*/
|
|
8
8
|
import type { SearchParamsFilter } from "./search-params-filter.js";
|
|
9
9
|
/**
|
|
@@ -41,3 +41,18 @@ export declare function sortedRouteParams(params: Record<string, string> | undef
|
|
|
41
41
|
* tier-specific suffixes (`:rsc`/`:html`, segment hash) after this base.
|
|
42
42
|
*/
|
|
43
43
|
export declare function cacheKeyBase(host: string, pathname: string, searchParams?: URLSearchParams, params?: Record<string, string>, filter?: SearchParamsFilter): string;
|
|
44
|
+
/**
|
|
45
|
+
* The record key, or shell partition, of nested keyed cache() scopes (#970)
|
|
46
|
+
* from its parts, outermost first: the chain's `key()` results and, for an
|
|
47
|
+
* inner scope without its own `key()`, that scope's default key
|
|
48
|
+
* (CacheScope.resolveKeyFrom). One part is returned raw, so a single keyed
|
|
49
|
+
* scope keeps its key byte for byte. Two or more are joined by `|`, each
|
|
50
|
+
* URI-encoded (like partitionShellKey, rsc/shell-capture-constants.ts): an
|
|
51
|
+
* encoded part holds no `|`, so every tuple maps to its own key, of any
|
|
52
|
+
* depth. Raw, `("a|b", "c")` and `("a", "b|c")` named one record. A composed
|
|
53
|
+
* key holds no `:` either, so a single raw result with one (a prefixed
|
|
54
|
+
* `tier:...`, a `doc:` default key) never equals it; an unprefixed raw result
|
|
55
|
+
* can. Shared with the testing helper `shellCacheKey`, which composes a
|
|
56
|
+
* nested partition the same way.
|
|
57
|
+
*/
|
|
58
|
+
export declare function composeCacheKeys(parts: readonly string[]): string;
|
|
@@ -42,6 +42,23 @@ export declare function resolveTtl(explicit: number | undefined, defaults: Cache
|
|
|
42
42
|
* non-finite/negative (degrade rather than feed bad math into expiry).
|
|
43
43
|
*/
|
|
44
44
|
export declare function resolveSwrWindow(explicit: number | undefined, defaults: CacheDefaults | undefined): number;
|
|
45
|
+
/**
|
|
46
|
+
* Minimum age (ms) of a stale PPR shell before a HIT schedules its recapture.
|
|
47
|
+
* A shell capped to a route cache() record inside its swr window is stale
|
|
48
|
+
* from the start (rsc/shell-capture.ts capShellWindow), so without a floor
|
|
49
|
+
* every HIT of it recaptured, and a capture's store write is one KV write per
|
|
50
|
+
* key: Cloudflare KV accepts one per second per key. Inside the floor the
|
|
51
|
+
* stale shell is still served. Per isolate: each isolate's stale HIT past the
|
|
52
|
+
* floor can schedule one (stampede-guarded per isolate).
|
|
53
|
+
*/
|
|
54
|
+
export declare const SHELL_MIN_RECAPTURE_INTERVAL_MS = 1000;
|
|
55
|
+
/**
|
|
56
|
+
* Whether a stale shell HIT schedules its recapture (see the floor above).
|
|
57
|
+
* Fails open: an entry without a numeric `createdAt` recaptures.
|
|
58
|
+
*/
|
|
59
|
+
export declare function staleShellRecaptureDue(entry: {
|
|
60
|
+
readonly createdAt?: number;
|
|
61
|
+
}, now: number): boolean;
|
|
45
62
|
/**
|
|
46
63
|
* Compute staleAt and expiresAt timestamps from TTL and SWR window.
|
|
47
64
|
*
|