vinext 1.0.1 → 1.1.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 +72 -1
- package/dist/build/app-page-static-eligibility.d.ts +15 -0
- package/dist/build/app-page-static-eligibility.js +82 -0
- package/dist/build/prerender-paths.js +3 -78
- package/dist/build/prerender.js +33 -8
- package/dist/build/server-manifest.d.ts +7 -0
- package/dist/build/server-manifest.js +11 -1
- package/dist/cache/cache-adapters-virtual.d.ts +7 -1
- package/dist/cache/cache-adapters-virtual.js +3 -3
- package/dist/client/navigation-runtime.d.ts +11 -0
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/config/config-matchers.js +3 -1
- package/dist/config/next-config.d.ts +7 -0
- package/dist/config/next-config.js +18 -3
- package/dist/entries/app-rsc-entry.d.ts +2 -0
- package/dist/entries/app-rsc-entry.js +64 -43
- package/dist/index.d.ts +4 -1
- package/dist/index.js +59 -30
- package/dist/plugins/cache-flight-codec.d.ts +19 -0
- package/dist/plugins/cache-flight-codec.js +68 -0
- package/dist/plugins/commonjs-esm-facade.d.ts +35 -0
- package/dist/plugins/commonjs-esm-facade.js +80 -0
- package/dist/plugins/fonts.d.ts +8 -11
- package/dist/plugins/fonts.js +71 -34
- package/dist/routing/file-matcher.d.ts +1 -0
- package/dist/routing/file-matcher.js +1 -1
- package/dist/server/api-handler.js +17 -7
- package/dist/server/app-browser-entry.js +11 -7
- package/dist/server/app-browser-history-controller.d.ts +2 -0
- package/dist/server/app-browser-history-controller.js +8 -0
- package/dist/server/app-browser-interception-context.js +26 -2
- package/dist/server/app-browser-navigation-controller.d.ts +7 -1
- package/dist/server/app-browser-navigation-controller.js +93 -10
- package/dist/server/app-fallback-renderer.d.ts +2 -0
- package/dist/server/app-fallback-renderer.js +1 -0
- package/dist/server/app-history-state.d.ts +3 -0
- package/dist/server/app-history-state.js +7 -0
- package/dist/server/app-middleware.js +2 -1
- package/dist/server/app-optimistic-routing.js +1 -1
- package/dist/server/app-page-boundary-render.d.ts +5 -0
- package/dist/server/app-page-boundary-render.js +6 -4
- package/dist/server/app-page-cache-finalizer.d.ts +11 -0
- package/dist/server/app-page-cache-finalizer.js +41 -7
- package/dist/server/app-page-cache-render.d.ts +18 -0
- package/dist/server/app-page-cache-render.js +63 -43
- package/dist/server/app-page-cache.d.ts +5 -0
- package/dist/server/app-page-cache.js +27 -7
- package/dist/server/app-page-dispatch.d.ts +9 -1
- package/dist/server/app-page-dispatch.js +47 -30
- package/dist/server/app-page-element-builder.d.ts +7 -0
- package/dist/server/app-page-element-builder.js +27 -42
- package/dist/server/app-page-execution.d.ts +15 -12
- package/dist/server/app-page-execution.js +30 -40
- package/dist/server/app-page-head.d.ts +5 -0
- package/dist/server/app-page-head.js +9 -3
- package/dist/server/app-page-probe.d.ts +0 -109
- package/dist/server/app-page-probe.js +3 -136
- package/dist/server/app-page-render-identity.d.ts +0 -1
- package/dist/server/app-page-render-identity.js +1 -1
- package/dist/server/app-page-render-observation.js +2 -2
- package/dist/server/app-page-render.d.ts +13 -5
- package/dist/server/app-page-render.js +219 -119
- package/dist/server/app-page-request.d.ts +0 -1
- package/dist/server/app-page-request.js +1 -2
- package/dist/server/app-page-response.d.ts +4 -3
- package/dist/server/app-page-response.js +4 -3
- package/dist/server/app-page-route-wiring.d.ts +17 -5
- package/dist/server/app-page-route-wiring.js +55 -33
- package/dist/server/app-page-rsc-render-status.d.ts +48 -0
- package/dist/server/app-page-rsc-render-status.js +54 -0
- package/dist/server/app-page-stream.d.ts +15 -8
- package/dist/server/app-page-stream.js +6 -0
- package/dist/server/app-request-stage-dispatch.js +1 -1
- package/dist/server/app-request-stage-independent-entry.js +4 -4
- package/dist/server/app-response-header-provenance.d.ts +2 -0
- package/dist/server/app-response-header-provenance.js +10 -4
- package/dist/server/app-route-handler-dispatch.js +1 -1
- package/dist/server/app-router-entry.js +3 -3
- package/dist/server/app-rsc-handler.d.ts +4 -0
- package/dist/server/app-rsc-handler.js +212 -96
- package/dist/server/app-rsc-request-normalization.d.ts +3 -1
- package/dist/server/app-rsc-request-normalization.js +4 -2
- package/dist/server/app-rsc-response-stage.d.ts +1 -1
- package/dist/server/app-rsc-response-stage.js +6 -4
- package/dist/server/app-rsc-route-matching.d.ts +9 -1
- package/dist/server/app-rsc-route-matching.js +36 -19
- package/dist/server/app-server-action-execution.js +1 -1
- package/dist/server/app-ssr-entry.d.ts +3 -2
- package/dist/server/app-ssr-entry.js +6 -4
- package/dist/server/app-ssr-stream.d.ts +6 -1
- package/dist/server/app-ssr-stream.js +34 -15
- package/dist/server/app-worker-stages.d.ts +2 -2
- package/dist/server/app-worker-stages.js +3 -3
- package/dist/server/cacheability-request.js +9 -5
- package/dist/server/config-headers.js +2 -0
- package/dist/server/dev-server.js +12 -2
- package/dist/server/headers.d.ts +14 -0
- package/dist/server/headers.js +15 -1
- package/dist/server/image-optimization.d.ts +16 -0
- package/dist/server/image-optimization.js +26 -6
- package/dist/server/isr-cache.js +2 -2
- package/dist/server/middleware-matcher-pattern.js +8 -4
- package/dist/server/middleware-response-headers.js +2 -0
- package/dist/server/multi-stage.d.ts +8 -0
- package/dist/server/navigation-planner.js +1 -1
- package/dist/server/node-response-signal.d.ts +16 -0
- package/dist/server/node-response-signal.js +26 -0
- package/dist/server/open-redirect.d.ts +47 -0
- package/dist/server/open-redirect.js +74 -1
- package/dist/server/pages-page-handler.js +21 -4
- package/dist/server/pages-request-stage-entry.js +8 -4
- package/dist/server/prod-server.d.ts +8 -7
- package/dist/server/prod-server.js +153 -69
- package/dist/server/request-pipeline.d.ts +20 -8
- package/dist/server/request-pipeline.js +21 -7
- package/dist/server/response-compression.d.ts +43 -0
- package/dist/server/response-compression.js +111 -0
- package/dist/server/seed-cache.js +10 -7
- package/dist/server/socket-error-backstop.d.ts +14 -0
- package/dist/server/socket-error-backstop.js +27 -4
- package/dist/shims/cache-adapter-instantiate.d.ts +50 -0
- package/dist/shims/cache-adapter-instantiate.js +118 -0
- package/dist/shims/cache-callable-runtime.d.ts +2 -40
- package/dist/shims/cache-callable-runtime.js +50 -168
- package/dist/shims/cache-flight-arguments.d.ts +22 -0
- package/dist/shims/cache-flight-arguments.js +56 -0
- package/dist/shims/cache-request-state.d.ts +15 -0
- package/dist/shims/cache-request-state.js +31 -8
- package/dist/shims/cache-runtime.d.ts +6 -19
- package/dist/shims/cache-runtime.js +165 -225
- package/dist/shims/cache.d.ts +2 -2
- package/dist/shims/cache.js +5 -4
- package/dist/shims/cacheability-classification.d.ts +7 -0
- package/dist/shims/error-boundary-navigation.d.ts +7 -0
- package/dist/shims/error-boundary-navigation.js +19 -1
- package/dist/shims/error-boundary.d.ts +10 -10
- package/dist/shims/error-boundary.js +26 -7
- package/dist/shims/fetch-cache.d.ts +6 -0
- package/dist/shims/fetch-cache.js +19 -1
- package/dist/shims/form.js +1 -1
- package/dist/shims/headers.d.ts +13 -0
- package/dist/shims/headers.js +47 -4
- package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -2
- package/dist/shims/internal/app-page-props-cache-key.js +24 -15
- package/dist/shims/internal/headers-state.d.ts +0 -1
- package/dist/shims/metadata.js +3 -3
- package/dist/shims/navigation-errors.d.ts +6 -0
- package/dist/shims/navigation-errors.js +11 -1
- package/dist/shims/navigation.d.ts +7 -0
- package/dist/shims/navigation.js +14 -4
- package/dist/shims/request-context.d.ts +9 -0
- package/dist/shims/thenable-params.d.ts +4 -0
- package/dist/shims/thenable-params.js +25 -3
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/utils/mdx-scan.d.ts +5 -0
- package/dist/utils/mdx-scan.js +8 -1
- package/dist/utils/regex-safety.d.ts +10 -0
- package/dist/utils/regex-safety.js +70 -18
- package/package.json +3 -3
|
@@ -1,190 +1,72 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { restoreFlightReply, snapshotFlightReply } from "./cache-flight-arguments.js";
|
|
2
|
+
import { isUseCacheFunction, memoizeInCacheScope, registerCachedFunction as registerCachedFunction$1, replayCachedFunction } from "./cache-runtime.js";
|
|
2
3
|
import { decryptActionBoundArgs, encryptActionBoundArgs } from "@vitejs/plugin-rsc/utils/encryption-runtime";
|
|
4
|
+
import { createFromReadableStream, renderToReadableStream } from "@vitejs/plugin-rsc/react/rsc";
|
|
3
5
|
//#region src/shims/cache-callable-runtime.ts
|
|
4
6
|
const CACHE_CAPTURE_TYPE = "use-cache-captures";
|
|
5
|
-
function
|
|
6
|
-
return
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
async function encryptArguments(value) {
|
|
8
|
+
return encryptActionBoundArgs(JSON.stringify(value));
|
|
9
|
+
}
|
|
10
|
+
async function decryptArguments(encrypted) {
|
|
11
|
+
const serialized = await decryptActionBoundArgs(Promise.resolve(encrypted));
|
|
12
|
+
if (typeof serialized !== "string") throw new Error("Invalid cache function arguments");
|
|
13
|
+
const value = JSON.parse(serialized);
|
|
14
|
+
if (value?.version !== 1) throw new Error("Invalid cache function arguments");
|
|
15
|
+
return value;
|
|
10
16
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return decryptCacheArguments(value.encrypted);
|
|
17
|
+
function encryptCacheCaptures(captures) {
|
|
18
|
+
return memoizedCaptureEnvelope(...captures);
|
|
14
19
|
}
|
|
20
|
+
const memoizedCaptureEnvelope = memoizeInCacheScope((...captures) => ({
|
|
21
|
+
type: CACHE_CAPTURE_TYPE,
|
|
22
|
+
encrypted: encryptCaptures(captures)
|
|
23
|
+
}));
|
|
15
24
|
async function encryptCaptures(captures) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return "then" in value && typeof value.then === "function";
|
|
26
|
-
}
|
|
27
|
-
function isPlainRecord(value) {
|
|
28
|
-
const prototype = Object.getPrototypeOf(value);
|
|
29
|
-
return (prototype === Object.prototype || prototype === null) && !("$$typeof" in value);
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* The arguments hold a getter. Flight reads it again while serializing, and a
|
|
33
|
-
* getter can return a different value, such as a new promise, on every read,
|
|
34
|
-
* so the recorded promise fields may not match what Flight serializes.
|
|
35
|
-
*/
|
|
36
|
-
var UnreplayableCacheArgumentsError = class extends Error {
|
|
37
|
-
constructor() {
|
|
38
|
-
super("Cache function arguments with getters cannot be replayed");
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* The own entries of `value` that Flight serializes: every index below an
|
|
43
|
-
* array's `length`, including holes and non-enumerable indices, as
|
|
44
|
-
* `JSON.stringify` reads them, and the enumerable own keys of anything else.
|
|
45
|
-
* A getter, own or inherited through an array hole, is never run.
|
|
46
|
-
*/
|
|
47
|
-
function ownEntries(value, getters) {
|
|
48
|
-
const keys = Array.isArray(value) ? Array.from({ length: value.length }, (_, index) => String(index)) : Object.keys(value);
|
|
49
|
-
const entries = [];
|
|
50
|
-
for (const key of keys) {
|
|
51
|
-
if (readsGetter(value, key)) {
|
|
52
|
-
if (getters === "reject") throw new UnreplayableCacheArgumentsError();
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
entries.push([key, Reflect.get(value, key)]);
|
|
56
|
-
}
|
|
57
|
-
return entries;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Whether reading `key` from `value` runs a getter. An array hole reads
|
|
61
|
-
* through the prototype chain, which can hold an indexed data property or
|
|
62
|
-
* accessor. A setter-only accessor reads as `undefined` without running code.
|
|
63
|
-
*/
|
|
64
|
-
function readsGetter(value, key) {
|
|
65
|
-
for (let target = value; target !== null; target = Reflect.getPrototypeOf(target)) {
|
|
66
|
-
const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
|
|
67
|
-
if (descriptor) return typeof descriptor.get === "function";
|
|
68
|
-
}
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
/** Members of the values Flight serializes recursively, which can hold params. */
|
|
72
|
-
function flightMembers(value, getters) {
|
|
73
|
-
if (value instanceof Map) return [...value].flat();
|
|
74
|
-
if (value instanceof Set) return [...value];
|
|
75
|
-
if (isThenableObject(value) || Array.isArray(value) || isPlainRecord(value)) return ownEntries(value, getters).map(([, member]) => member);
|
|
76
|
-
return [];
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Collect the own fields of every promise-augmented object in the arguments,
|
|
80
|
-
* including ones inside resolved promise values. The arguments are passed to
|
|
81
|
-
* Flight as they are, so Flight keeps their shared references and cycles.
|
|
82
|
-
* With `getters: "reject"`, throws `UnreplayableCacheArgumentsError` when the
|
|
83
|
-
* arguments hold a getter; with `"skip"`, promise fields behind a getter are
|
|
84
|
-
* not recorded.
|
|
85
|
-
*/
|
|
86
|
-
async function encodeCacheArguments(args, getters = "reject") {
|
|
87
|
-
const settled = /* @__PURE__ */ new Map();
|
|
88
|
-
for (;;) {
|
|
89
|
-
const thenableObjects = [];
|
|
90
|
-
const unsettled = [];
|
|
91
|
-
const visited = /* @__PURE__ */ new Set();
|
|
92
|
-
const pending = [args];
|
|
93
|
-
while (pending.length > 0) {
|
|
94
|
-
const value = pending.pop();
|
|
95
|
-
if (typeof value !== "object" || value === null || visited.has(value)) continue;
|
|
96
|
-
visited.add(value);
|
|
97
|
-
const children = flightMembers(value, getters);
|
|
98
|
-
if (isThenableObject(value)) {
|
|
99
|
-
const fields = Object.fromEntries(ownEntries(value, getters));
|
|
100
|
-
thenableObjects.push({
|
|
101
|
-
fields,
|
|
102
|
-
promise: value
|
|
103
|
-
});
|
|
25
|
+
const files = new FormData();
|
|
26
|
+
let nextFileId = 0;
|
|
27
|
+
const temporaryReferences = new class extends WeakMap {
|
|
28
|
+
get(value) {
|
|
29
|
+
let id = super.get(value);
|
|
30
|
+
if (id === void 0 && value instanceof File) {
|
|
31
|
+
id = String(nextFileId++);
|
|
32
|
+
super.set(value, id);
|
|
33
|
+
files.append(id, value);
|
|
104
34
|
}
|
|
105
|
-
|
|
106
|
-
const result = settled.get(value);
|
|
107
|
-
if (!result) unsettled.push(value);
|
|
108
|
-
else if (result.status === "fulfilled") children.push(result.value);
|
|
109
|
-
}
|
|
110
|
-
for (const child of children) pending.push(child);
|
|
35
|
+
return id;
|
|
111
36
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
/** Restore the promise fields that `encodeCacheArguments` captured before Flight encoding. */
|
|
121
|
-
function decodeCacheArguments(value) {
|
|
122
|
-
if (typeof value !== "object" || value === null || !("args" in value) || !Array.isArray(value.args) || !("thenableObjects" in value) || !Array.isArray(value.thenableObjects)) throw new Error("Invalid cache function arguments");
|
|
123
|
-
adoptFlightThenables(value);
|
|
124
|
-
for (const thenableObject of value.thenableObjects) {
|
|
125
|
-
if (!isEncodedThenableObject(thenableObject)) throw new Error("Invalid cache function arguments");
|
|
126
|
-
Object.assign(thenableObject.promise, thenableObject.fields);
|
|
127
|
-
}
|
|
128
|
-
return value.args;
|
|
37
|
+
}();
|
|
38
|
+
const stream = renderToReadableStream(captures, { temporaryReferences });
|
|
39
|
+
const bytes = await new Response(stream).arrayBuffer();
|
|
40
|
+
return encryptActionBoundArgs(JSON.stringify({
|
|
41
|
+
version: 1,
|
|
42
|
+
result: Buffer.from(bytes).toString("base64"),
|
|
43
|
+
files: await snapshotFlightReply(files)
|
|
44
|
+
}));
|
|
129
45
|
}
|
|
130
|
-
function
|
|
131
|
-
if (typeof value !== "object" || value === null || !
|
|
132
|
-
const { fields, promise } = value;
|
|
133
|
-
return typeof fields === "object" && fields !== null && isPlainRecord(fields) && promise instanceof Promise;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Flight decodes promises as React chunks whose own fields are React's
|
|
137
|
-
* internal state. Adopt them into native promises, which have no own fields,
|
|
138
|
-
* so key serialization sees a promise exactly as the original call did.
|
|
139
|
-
* Flight shares one chunk between references to the same promise, so each
|
|
140
|
-
* chunk is adopted once, including references inside resolved values.
|
|
141
|
-
*/
|
|
142
|
-
function adoptFlightThenables(value, adopted = /* @__PURE__ */ new WeakMap()) {
|
|
143
|
-
if (typeof value !== "object" || value === null) return value;
|
|
144
|
-
if (adopted.has(value)) return adopted.get(value);
|
|
145
|
-
if (isThenable(value)) {
|
|
146
|
-
const promise = Promise.resolve(value).then((resolved) => adoptFlightThenables(resolved, adopted));
|
|
147
|
-
promise.catch(() => {});
|
|
148
|
-
adopted.set(value, promise);
|
|
149
|
-
return promise;
|
|
150
|
-
}
|
|
151
|
-
if (value instanceof Map) {
|
|
152
|
-
adopted.set(value, value);
|
|
153
|
-
const entries = [...value];
|
|
154
|
-
value.clear();
|
|
155
|
-
for (const [key, item] of entries) value.set(adoptFlightThenables(key, adopted), adoptFlightThenables(item, adopted));
|
|
156
|
-
return value;
|
|
157
|
-
}
|
|
158
|
-
if (value instanceof Set) {
|
|
159
|
-
adopted.set(value, value);
|
|
160
|
-
const items = [...value];
|
|
161
|
-
value.clear();
|
|
162
|
-
for (const item of items) value.add(adoptFlightThenables(item, adopted));
|
|
163
|
-
return value;
|
|
164
|
-
}
|
|
165
|
-
if (!Array.isArray(value) && !isPlainRecord(value)) return value;
|
|
166
|
-
adopted.set(value, value);
|
|
167
|
-
for (const key of Object.keys(value)) Reflect.set(value, key, adoptFlightThenables(Reflect.get(value, key), adopted));
|
|
168
|
-
return value;
|
|
169
|
-
}
|
|
170
|
-
function isCacheCaptureEnvelope(value) {
|
|
171
|
-
if (typeof value !== "object" || value === null) return false;
|
|
172
|
-
if (!("type" in value) || value.type !== CACHE_CAPTURE_TYPE || !("encrypted" in value)) return false;
|
|
46
|
+
async function decryptCacheCaptures(value) {
|
|
47
|
+
if (typeof value !== "object" || value === null || !("type" in value) || value.type !== CACHE_CAPTURE_TYPE || !("encrypted" in value)) return;
|
|
173
48
|
const encrypted = value.encrypted;
|
|
174
|
-
|
|
49
|
+
if (typeof encrypted !== "string" && !(typeof encrypted === "object" && encrypted !== null && "then" in encrypted && typeof encrypted.then === "function")) return;
|
|
50
|
+
const serialized = await decryptActionBoundArgs(Promise.resolve(encrypted));
|
|
51
|
+
if (typeof serialized !== "string") throw new Error("Invalid cache capture arguments");
|
|
52
|
+
const payload = JSON.parse(serialized);
|
|
53
|
+
if (payload.version !== 1) throw new Error("Invalid cache capture arguments");
|
|
54
|
+
const files = restoreFlightReply(payload.files);
|
|
55
|
+
if (typeof files === "string") throw new Error("Invalid cache capture files");
|
|
56
|
+
return await createFromReadableStream(new Response(Buffer.from(payload.result, "base64")).body, { temporaryReferences: new Map([...files].map(([id, file]) => [`$${id}`, file])) }, { preserveServerReferences: true });
|
|
175
57
|
}
|
|
176
58
|
function registerCachedFunction(fn, id, variant, options) {
|
|
177
59
|
return registerCachedFunction$1(fn, id, variant, {
|
|
178
60
|
...options,
|
|
179
61
|
decryptCaptures: decryptCacheCaptures,
|
|
180
|
-
|
|
62
|
+
encodeInvocation: encryptArguments
|
|
181
63
|
});
|
|
182
64
|
}
|
|
183
|
-
/**
|
|
65
|
+
/** Replay the persisted payload without re-reading or re-encoding caller objects. */
|
|
184
66
|
async function invokeCacheFunction(invocation, loadServerAction) {
|
|
185
67
|
const fn = await loadServerAction(invocation.referenceId);
|
|
186
68
|
if (!isUseCacheFunction(fn)) throw new Error(`Server reference ${invocation.referenceId} is not a cache function`);
|
|
187
|
-
await fn
|
|
69
|
+
await replayCachedFunction(fn, await decryptArguments(invocation.encryptedArgs));
|
|
188
70
|
}
|
|
189
71
|
//#endregion
|
|
190
|
-
export {
|
|
72
|
+
export { encryptCacheCaptures, invokeCacheFunction, registerCachedFunction };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/shims/cache-flight-arguments.d.ts
|
|
2
|
+
/** The persisted argument transport. React owns the object graph and wire protocol. */
|
|
3
|
+
export type CacheFlightArguments = {
|
|
4
|
+
version: 1;
|
|
5
|
+
reply: string | ([string, string] | [string, string, string, number, string])[];
|
|
6
|
+
pagePropsIndex?: number;
|
|
7
|
+
layoutPropsIndex?: number;
|
|
8
|
+
};
|
|
9
|
+
/** Snapshot only the multipart transport, never traverse user arguments. */
|
|
10
|
+
export declare function snapshotFlightReply(reply: string | FormData, pagePropsIndex?: number): Promise<CacheFlightArguments>;
|
|
11
|
+
export declare function restoreFlightReply(args: CacheFlightArguments): string | FormData;
|
|
12
|
+
/**
|
|
13
|
+
* Like Next.js (use-cache-wrapper.ts, `encodeFormData`), a binary entry is keyed
|
|
14
|
+
* by its bytes alone. Its name, type, and timestamp are replayed but not keyed:
|
|
15
|
+
* native FormData gives each Blob wrapper a wall-clock timestamp.
|
|
16
|
+
*
|
|
17
|
+
* Hashed synchronously, as Next.js derives its key without leaving the task: a
|
|
18
|
+
* cache hit must resolve before React flushes the shell, and an async digest
|
|
19
|
+
* waits for a later event-loop turn.
|
|
20
|
+
*/
|
|
21
|
+
export declare function flightArgumentsKey(args: CacheFlightArguments): string;
|
|
22
|
+
//#endregion
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
//#region src/shims/cache-flight-arguments.ts
|
|
3
|
+
const blobType = Object.getOwnPropertyDescriptor(Blob.prototype, "type").get;
|
|
4
|
+
const fileName = Object.getOwnPropertyDescriptor(File.prototype, "name").get;
|
|
5
|
+
const fileTime = Object.getOwnPropertyDescriptor(File.prototype, "lastModified").get;
|
|
6
|
+
/** Snapshot only the multipart transport, never traverse user arguments. */
|
|
7
|
+
async function snapshotFlightReply(reply, pagePropsIndex) {
|
|
8
|
+
if (typeof reply === "string") return {
|
|
9
|
+
version: 1,
|
|
10
|
+
reply,
|
|
11
|
+
pagePropsIndex
|
|
12
|
+
};
|
|
13
|
+
const entries = [];
|
|
14
|
+
for (const [name, value] of reply) entries.push(typeof value === "string" ? [name, value] : [
|
|
15
|
+
name,
|
|
16
|
+
Buffer.from(await Blob.prototype.arrayBuffer.call(value)).toString("base64"),
|
|
17
|
+
fileName.call(value),
|
|
18
|
+
fileTime.call(value),
|
|
19
|
+
blobType.call(value)
|
|
20
|
+
]);
|
|
21
|
+
return {
|
|
22
|
+
version: 1,
|
|
23
|
+
reply: entries,
|
|
24
|
+
pagePropsIndex
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function restoreFlightReply(args) {
|
|
28
|
+
if (typeof args.reply === "string") return args.reply;
|
|
29
|
+
const reply = new FormData();
|
|
30
|
+
for (const entry of args.reply) {
|
|
31
|
+
const [name, data] = entry;
|
|
32
|
+
reply.append(name, entry.length === 2 ? data : new File([Buffer.from(data, "base64")], entry[2], {
|
|
33
|
+
lastModified: entry[3],
|
|
34
|
+
type: entry[4]
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
return reply;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Like Next.js (use-cache-wrapper.ts, `encodeFormData`), a binary entry is keyed
|
|
41
|
+
* by its bytes alone. Its name, type, and timestamp are replayed but not keyed:
|
|
42
|
+
* native FormData gives each Blob wrapper a wall-clock timestamp.
|
|
43
|
+
*
|
|
44
|
+
* Hashed synchronously, as Next.js derives its key without leaving the task: a
|
|
45
|
+
* cache hit must resolve before React flushes the shell, and an async digest
|
|
46
|
+
* waits for a later event-loop turn.
|
|
47
|
+
*/
|
|
48
|
+
function flightArgumentsKey(args) {
|
|
49
|
+
const reply = typeof args.reply === "string" ? args.reply : args.reply.map((entry) => entry.length === 2 ? entry : [entry[0], { bytes: entry[1] }]);
|
|
50
|
+
return createHash("sha256").update(JSON.stringify({
|
|
51
|
+
...args,
|
|
52
|
+
reply
|
|
53
|
+
})).digest("base64url");
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { flightArgumentsKey, restoreFlightReply, snapshotFlightReply };
|
|
@@ -15,8 +15,17 @@ export type UnstableCacheObservation = Readonly<{
|
|
|
15
15
|
tagCount: number;
|
|
16
16
|
tagHash: string | null;
|
|
17
17
|
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Collects the cacheLife claims made anywhere below the scope that installed
|
|
20
|
+
* it, including nested scopes that reset `requestScopedCacheLife`. Shallow
|
|
21
|
+
* scope clones share it by reference.
|
|
22
|
+
*/
|
|
23
|
+
export type CacheLifeSink = {
|
|
24
|
+
cacheLife: CacheLifeConfig | null;
|
|
25
|
+
};
|
|
18
26
|
export type CacheState = {
|
|
19
27
|
actionRevalidationKind: ActionRevalidationKind;
|
|
28
|
+
cacheLifeSink: CacheLifeSink | null;
|
|
20
29
|
pendingRevalidatedTags: Set<string>;
|
|
21
30
|
pendingRevalidations: Set<Promise<void>>;
|
|
22
31
|
requestScopedCacheLife: CacheLifeConfig | null;
|
|
@@ -70,6 +79,12 @@ export declare function _captureRequestScopedCacheLifeAccessors(): {
|
|
|
70
79
|
};
|
|
71
80
|
export declare function recordUnstableCacheObservation(observation: UnstableCacheObservation): void;
|
|
72
81
|
export declare function _peekUnstableCacheObservations(): UnstableCacheObservation[];
|
|
82
|
+
/**
|
|
83
|
+
* Run `fn` with the request's cache settings, including its revalidation mode,
|
|
84
|
+
* but its own record of cache lives and unstable_cache observations, so work
|
|
85
|
+
* done on the request's behalf after the fact leaves the request untouched.
|
|
86
|
+
*/
|
|
87
|
+
export declare function runWithDetachedCacheObservations<T>(fn: () => Promise<T>): Promise<T>;
|
|
73
88
|
export declare function shouldServeStaleUnstableCacheEntry(): boolean;
|
|
74
89
|
//#endregion
|
|
75
90
|
export { _recordUseCacheRootParamRead, _registerCacheContextAccessor, getRegisteredCacheContext };
|
|
@@ -47,6 +47,7 @@ const ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC = 1;
|
|
|
47
47
|
const ACTION_DID_REVALIDATE_DYNAMIC_ONLY = 2;
|
|
48
48
|
const fallbackState = globalState[FALLBACK_KEY] ??= {
|
|
49
49
|
actionRevalidationKind: ACTION_DID_NOT_REVALIDATE,
|
|
50
|
+
cacheLifeSink: null,
|
|
50
51
|
pendingRevalidatedTags: /* @__PURE__ */ new Set(),
|
|
51
52
|
pendingRevalidations: /* @__PURE__ */ new Set(),
|
|
52
53
|
requestScopedCacheLife: null,
|
|
@@ -60,12 +61,14 @@ function getCacheState() {
|
|
|
60
61
|
function _runWithCacheState(fn) {
|
|
61
62
|
if (isInsideUnifiedScope()) return runWithUnifiedStateMutation((context) => {
|
|
62
63
|
context.actionRevalidationKind = ACTION_DID_NOT_REVALIDATE;
|
|
64
|
+
context.cacheLifeSink = null;
|
|
63
65
|
context.requestScopedCacheLife = null;
|
|
64
66
|
context.unstableCacheObservations = /* @__PURE__ */ new Map();
|
|
65
67
|
context.unstableCacheRevalidation = "foreground";
|
|
66
68
|
}, fn);
|
|
67
69
|
const state = {
|
|
68
70
|
actionRevalidationKind: ACTION_DID_NOT_REVALIDATE,
|
|
71
|
+
cacheLifeSink: null,
|
|
69
72
|
pendingRevalidatedTags: /* @__PURE__ */ new Set(),
|
|
70
73
|
pendingRevalidations: /* @__PURE__ */ new Set(),
|
|
71
74
|
requestScopedCacheLife: null,
|
|
@@ -146,15 +149,17 @@ async function _drainPendingRevalidations() {
|
|
|
146
149
|
}
|
|
147
150
|
if (didReject) throw firstRejection;
|
|
148
151
|
}
|
|
152
|
+
function mergeCacheLife(current, config) {
|
|
153
|
+
if (current === null) return { ...config };
|
|
154
|
+
if (config.stale !== void 0) current.stale = current.stale !== void 0 ? Math.min(current.stale, config.stale) : config.stale;
|
|
155
|
+
if (config.revalidate !== void 0) current.revalidate = current.revalidate !== void 0 ? Math.min(current.revalidate, config.revalidate) : config.revalidate;
|
|
156
|
+
if (config.expire !== void 0) current.expire = current.expire !== void 0 ? Math.min(current.expire, config.expire) : config.expire;
|
|
157
|
+
return current;
|
|
158
|
+
}
|
|
149
159
|
function _setRequestScopedCacheLife(config) {
|
|
150
160
|
const state = getCacheState();
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
if (config.stale !== void 0) state.requestScopedCacheLife.stale = state.requestScopedCacheLife.stale !== void 0 ? Math.min(state.requestScopedCacheLife.stale, config.stale) : config.stale;
|
|
156
|
-
if (config.revalidate !== void 0) state.requestScopedCacheLife.revalidate = state.requestScopedCacheLife.revalidate !== void 0 ? Math.min(state.requestScopedCacheLife.revalidate, config.revalidate) : config.revalidate;
|
|
157
|
-
if (config.expire !== void 0) state.requestScopedCacheLife.expire = state.requestScopedCacheLife.expire !== void 0 ? Math.min(state.requestScopedCacheLife.expire, config.expire) : config.expire;
|
|
161
|
+
state.requestScopedCacheLife = mergeCacheLife(state.requestScopedCacheLife, config);
|
|
162
|
+
if (state.cacheLifeSink) state.cacheLifeSink.cacheLife = mergeCacheLife(state.cacheLifeSink.cacheLife, config);
|
|
158
163
|
}
|
|
159
164
|
function _peekRequestScopedCacheLife() {
|
|
160
165
|
const config = getCacheState().requestScopedCacheLife;
|
|
@@ -193,8 +198,26 @@ function recordUnstableCacheObservation(observation) {
|
|
|
193
198
|
function _peekUnstableCacheObservations() {
|
|
194
199
|
return [...getCacheState().unstableCacheObservations.values()].sort((a, b) => a.keyHash.localeCompare(b.keyHash));
|
|
195
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Run `fn` with the request's cache settings, including its revalidation mode,
|
|
203
|
+
* but its own record of cache lives and unstable_cache observations, so work
|
|
204
|
+
* done on the request's behalf after the fact leaves the request untouched.
|
|
205
|
+
*/
|
|
206
|
+
function runWithDetachedCacheObservations(fn) {
|
|
207
|
+
if (isInsideUnifiedScope()) return runWithUnifiedStateMutation((context) => {
|
|
208
|
+
context.cacheLifeSink = null;
|
|
209
|
+
context.requestScopedCacheLife = null;
|
|
210
|
+
context.unstableCacheObservations = /* @__PURE__ */ new Map();
|
|
211
|
+
}, fn);
|
|
212
|
+
return cacheAls.run({
|
|
213
|
+
...getCacheState(),
|
|
214
|
+
cacheLifeSink: null,
|
|
215
|
+
requestScopedCacheLife: null,
|
|
216
|
+
unstableCacheObservations: /* @__PURE__ */ new Map()
|
|
217
|
+
}, fn);
|
|
218
|
+
}
|
|
196
219
|
function shouldServeStaleUnstableCacheEntry() {
|
|
197
220
|
return getCacheState().unstableCacheRevalidation === "background";
|
|
198
221
|
}
|
|
199
222
|
//#endregion
|
|
200
|
-
export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _queuePendingRevalidation, _recordUseCacheRootParamRead, _registerCacheContextAccessor, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, getRegisteredCacheContext, markActionRevalidation, recordUnstableCacheObservation, shouldServeStaleUnstableCacheEntry };
|
|
223
|
+
export { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, _captureRequestScopedCacheLifeAccessors, _consumeRequestScopedCacheLife, _drainPendingRevalidations, _hasPendingRevalidatedTag, _initRequestScopedCacheState, _markPendingRevalidatedTag, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _queuePendingRevalidation, _recordUseCacheRootParamRead, _registerCacheContextAccessor, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, getRegisteredCacheContext, markActionRevalidation, recordUnstableCacheObservation, runWithDetachedCacheObservations, shouldServeStaleUnstableCacheEntry };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CacheLifeConfig } from "./cache-request-state.js";
|
|
2
|
-
import {
|
|
2
|
+
import { CacheFlightArguments } from "./cache-flight-arguments.js";
|
|
3
3
|
//#region src/shims/cache-runtime.d.ts
|
|
4
|
+
export declare function replayCachedFunction(fn: object, args: CacheFlightArguments): Promise<unknown>;
|
|
4
5
|
/**
|
|
5
6
|
* Used purely as `cause` for the nested-dynamic cache error: its captured stack
|
|
6
7
|
* points at the inner "use cache" invocation that propagated a dynamic cache
|
|
@@ -52,15 +53,6 @@ export declare function getCacheContext(): CacheContext | null;
|
|
|
52
53
|
* Exported for testing.
|
|
53
54
|
*/
|
|
54
55
|
export declare function buildUseCacheKey(id: string, keySeed: string | undefined, argsKey?: string): string;
|
|
55
|
-
/**
|
|
56
|
-
* Convert an encodeReply result (string | FormData) to a cache key string.
|
|
57
|
-
* For FormData (binary args), produces a deterministic SHA-256 hash over
|
|
58
|
-
* the sorted entries. We can't hash `new Response(formData).arrayBuffer()`
|
|
59
|
-
* because multipart boundaries are non-deterministic across serializations.
|
|
60
|
-
*
|
|
61
|
-
* Exported for testing.
|
|
62
|
-
*/
|
|
63
|
-
export declare function replyToCacheKey(reply: string | FormData): Promise<string>;
|
|
64
56
|
export type PrivateCacheState = {
|
|
65
57
|
_privateCache: Map<string, unknown> | null;
|
|
66
58
|
};
|
|
@@ -86,7 +78,7 @@ export type RegisterCachedFunctionOptions = {
|
|
|
86
78
|
/** Number of declared arguments supplied by the directive transform. */
|
|
87
79
|
argumentCount?: number;
|
|
88
80
|
decryptCaptures?: (value: unknown) => Promise<unknown[] | undefined>;
|
|
89
|
-
|
|
81
|
+
encodeInvocation?: (args: CacheFlightArguments) => Promise<string>;
|
|
90
82
|
serverReferenceId?: string;
|
|
91
83
|
};
|
|
92
84
|
/**
|
|
@@ -99,13 +91,8 @@ export type RegisterCachedFunctionOptions = {
|
|
|
99
91
|
* @returns A wrapper function that checks cache before calling the original
|
|
100
92
|
*/
|
|
101
93
|
export declare function registerCachedFunction<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>, id: string, variant?: string, options?: RegisterCachedFunctionOptions): (...args: TArgs) => Promise<TResult>;
|
|
94
|
+
/** Memoize in one React render without skipping cache guards or scoped serialization. */
|
|
95
|
+
export declare function memoizeInCacheScope<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => TResult): (...args: TArgs) => TResult;
|
|
102
96
|
/** Whether a loaded server reference is a transformed `"use cache"` function. */
|
|
103
97
|
export declare function isUseCacheFunction(value: unknown): value is (...args: unknown[]) => Promise<unknown>;
|
|
104
|
-
|
|
105
|
-
* A thenable (not an array) with own enumerable properties — the
|
|
106
|
-
* `Object.assign(Promise.resolve(obj), obj)` pattern Next.js params use. The
|
|
107
|
-
* cache key is built from its fields instead of the promise.
|
|
108
|
-
*/
|
|
109
|
-
export declare function isThenableObject(value: object): value is PromiseLike<unknown>;
|
|
110
|
-
//#endregion
|
|
111
|
-
export { markAppPagePropsForUseCache };
|
|
98
|
+
//#endregion
|