vinext 1.0.0-beta.12 → 1.0.0-beta.13
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 +39 -12
- package/dist/build/action-owner-manifest.js +23 -5
- package/dist/build/lifecycle.d.ts +42 -0
- package/dist/build/lifecycle.js +246 -0
- package/dist/build/nitro-route-rules.js +1 -1
- package/dist/build/pages-client-assets-module.d.ts +0 -3
- package/dist/build/pages-client-assets-module.js +1 -13
- package/dist/build/precompress.js +1 -1
- package/dist/build/prerender-paths.d.ts +1 -1
- package/dist/build/prerender-paths.js +6 -6
- package/dist/build/prerender-server-pool.js +1 -1
- package/dist/build/prerender.d.ts +1 -1
- package/dist/build/prerender.js +5 -5
- package/dist/build/preview-credentials.d.ts +1 -1
- package/dist/build/preview-credentials.js +2 -2
- package/dist/build/report.js +1 -1
- package/dist/build/run-prerender.d.ts +2 -2
- package/dist/build/run-prerender.js +5 -5
- package/dist/build/standalone.js +2 -2
- package/dist/cli-args.d.ts +5 -13
- package/dist/cli-args.js +2 -45
- package/dist/cli-dev-config.d.ts +6 -2
- package/dist/cli-dev-config.js +160 -3
- package/dist/cli.js +104 -439
- package/dist/config/config-matchers.js +1 -1
- package/dist/config/next-config.d.ts +2 -2
- package/dist/config/next-config.js +5 -5
- package/dist/config/prerender.d.ts +3 -0
- package/dist/config/prerender.js +8 -2
- package/dist/entries/app-browser-entry.js +1 -1
- package/dist/entries/app-rsc-entry.js +1 -1
- package/dist/entries/pages-client-entry.d.ts +1 -0
- package/dist/entries/pages-client-entry.js +17 -2
- package/dist/index.d.ts +6 -8
- package/dist/index.js +262 -101
- package/dist/init-cloudflare.d.ts +3 -2
- package/dist/init-cloudflare.js +117 -5
- package/dist/init-platform.d.ts +2 -0
- package/dist/init-platform.js +9 -3
- package/dist/init.d.ts +7 -5
- package/dist/init.js +41 -22
- package/dist/plugins/dynamic-preload-metadata.js +1 -1
- package/dist/plugins/extensionless-dynamic-import.js +2 -2
- package/dist/plugins/fonts.js +1 -1
- package/dist/plugins/ignore-dynamic-requests.js +1 -1
- package/dist/plugins/import-meta-url.js +1 -1
- package/dist/plugins/instrumentation-client.js +1 -1
- package/dist/plugins/og-assets.js +32 -13
- package/dist/plugins/og-harfbuzz.d.ts +24 -0
- package/dist/plugins/og-harfbuzz.js +260 -0
- package/dist/plugins/optimize-imports.js +1 -1
- package/dist/plugins/require-condition-resolution.js +2 -2
- package/dist/plugins/require-context.js +2 -2
- package/dist/plugins/typeof-window.js +1 -1
- package/dist/plugins/use-cache-callable.d.ts +0 -2
- package/dist/plugins/use-cache-callable.js +8 -16
- package/dist/plugins/worker-image-imports.js +1 -1
- package/dist/routing/app-route-graph.js +1 -1
- package/dist/routing/app-router.js +1 -1
- package/dist/routing/pages-router.js +1 -1
- package/dist/server/app-action-forwarding.js +1 -1
- package/dist/server/app-browser-error.js +1 -1
- package/dist/server/app-elements-wire.js +2 -2
- package/dist/server/app-middleware.js +1 -1
- package/dist/server/app-optimistic-routing.js +1 -1
- package/dist/server/app-page-dispatch.js +1 -1
- package/dist/server/app-page-element-builder.js +3 -2
- package/dist/server/app-page-probe.js +3 -3
- package/dist/server/app-request-stage-independent-entry.js +2 -2
- package/dist/server/app-route-handler-dispatch.js +1 -1
- package/dist/server/app-route-handler-runtime.js +1 -1
- package/dist/server/app-router-entry.js +2 -2
- package/dist/server/app-rsc-handler.d.ts +1 -1
- package/dist/server/app-rsc-handler.js +4 -4
- package/dist/server/app-rsc-response-stage.js +1 -1
- package/dist/server/app-rsc-route-matching.js +1 -1
- package/dist/server/app-server-action-execution.js +4 -4
- package/dist/server/artifact-compatibility.js +1 -1
- package/dist/server/client-reuse-manifest.js +1 -1
- package/dist/server/dev-lockfile.d.ts +1 -1
- package/dist/server/dev-lockfile.js +4 -1
- package/dist/server/dev-server.d.ts +1 -1
- package/dist/server/dev-server.js +28 -6
- package/dist/server/dev-stack-sourcemap.js +1 -1
- package/dist/server/isr-cache.js +1 -1
- package/dist/server/metadata-route-response.js +8 -0
- package/dist/server/middleware-runtime.js +3 -3
- package/dist/server/pages-data-route.js +1 -1
- package/dist/server/pages-dev-hydration.d.ts +6 -0
- package/dist/server/pages-dev-hydration.js +19 -7
- package/dist/server/pages-page-data.js +2 -2
- package/dist/server/pages-page-handler.js +1 -1
- package/dist/server/pages-page-response.js +1 -1
- package/dist/server/pages-request-pipeline.js +3 -3
- package/dist/server/pages-request-stage-entry.js +3 -3
- package/dist/server/pages-revalidate.js +1 -1
- package/dist/server/prerender-route-params.js +1 -1
- package/dist/server/prod-server.js +7 -7
- package/dist/server/request-pipeline.js +2 -2
- package/dist/server/socket-error-backstop.js +1 -1
- package/dist/shims/cache-callable-runtime.d.ts +38 -0
- package/dist/shims/cache-callable-runtime.js +162 -11
- package/dist/shims/cache-runtime.d.ts +6 -8
- package/dist/shims/cache-runtime.js +82 -22
- package/dist/shims/font-local.js +1 -1
- package/dist/shims/form.js +1 -1
- package/dist/shims/headers.js +2 -2
- package/dist/shims/internal/app-page-props-cache-key.d.ts +29 -0
- package/dist/shims/internal/app-page-props-cache-key.js +53 -1
- package/dist/shims/internal/format-url-object.d.ts +7 -0
- package/dist/shims/internal/format-url-object.js +53 -0
- package/dist/shims/internal/normalize-router-href.d.ts +15 -0
- package/dist/shims/internal/normalize-router-href.js +41 -0
- package/dist/shims/internal/pages-data-target.d.ts +4 -1
- package/dist/shims/internal/pages-data-target.js +28 -35
- package/dist/shims/internal/route-pattern-for-warning.d.ts +2 -2
- package/dist/shims/internal/route-pattern-for-warning.js +3 -3
- package/dist/shims/link.js +31 -92
- package/dist/shims/metadata.js +5 -4
- package/dist/shims/navigation.js +2 -2
- package/dist/shims/router-state.js +1 -1
- package/dist/shims/router.d.ts +8 -7
- package/dist/shims/router.js +312 -148
- package/dist/shims/server.js +3 -3
- package/dist/utils/built-asset-url.js +1 -1
- package/dist/utils/project.d.ts +1 -11
- package/dist/utils/project.js +19 -28
- package/dist/utils/react-version.d.ts +3 -1
- package/dist/utils/react-version.js +19 -1
- package/dist/utils/vite-cli-invocation.d.ts +21 -0
- package/dist/utils/vite-cli-invocation.js +144 -0
- package/package.json +10 -2
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
|
+
import { isUnknownRecord } from "../utils/record.js";
|
|
3
|
+
import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
|
|
4
|
+
import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
|
|
5
|
+
import { StaticFileCache, contentTypeForPath, etagFromFilenameHash } from "./static-file-cache.js";
|
|
2
6
|
import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
|
|
3
7
|
import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
4
|
-
import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
|
|
5
8
|
import { VINEXT_PRERENDER_RENDER_ERROR_HEADER } from "./headers.js";
|
|
6
9
|
import { normalizePath } from "./normalize-path.js";
|
|
7
10
|
import { notFoundResponse } from "./http-error-responses.js";
|
|
8
11
|
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
9
12
|
import { readStaticFileSignal } from "./static-file-signal.js";
|
|
10
13
|
import { canonicalizeRequestPathname, filterInternalHeaders } from "./request-pipeline.js";
|
|
11
|
-
import { isUnknownRecord } from "../utils/record.js";
|
|
12
14
|
import { parseHttpDate } from "./http-date.js";
|
|
13
15
|
import { evaluateStaticPreconditions } from "./http-conditional.js";
|
|
14
16
|
import { buildNextDataNotFoundResponse, encodeUrlParserIgnoredCharacters, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname, shouldAddTrailingSlashToPagesDataPath, urlParserCreatesPagesDataPath } from "./pages-data-route.js";
|
|
15
|
-
import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
|
|
16
17
|
import { setPagesClientAssets } from "./pages-client-assets.js";
|
|
17
18
|
import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
|
|
18
19
|
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath, isSafeImageContentType, parseImageParams } from "./image-optimization.js";
|
|
19
20
|
import { installSocketErrorBackstop } from "./socket-error-backstop.js";
|
|
20
|
-
import { StaticFileCache, contentTypeForPath, etagFromFilenameHash } from "./static-file-cache.js";
|
|
21
21
|
import { collectInlineCssManifest } from "../build/inline-css.js";
|
|
22
22
|
import { readTrustedRevalidationHostname } from "./revalidation-host.js";
|
|
23
23
|
import { traceFrameworkRequest } from "./request-tracing.js";
|
|
@@ -32,8 +32,8 @@ import { ifRangeAllowsRange, parseByteRange } from "./http-range.js";
|
|
|
32
32
|
import { createRequire } from "node:module";
|
|
33
33
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
34
34
|
import fs from "node:fs";
|
|
35
|
-
import fsp from "node:fs/promises";
|
|
36
35
|
import { pathToFileURL } from "node:url";
|
|
36
|
+
import fsp from "node:fs/promises";
|
|
37
37
|
import { Readable, pipeline } from "node:stream";
|
|
38
38
|
import zlib from "node:zlib";
|
|
39
39
|
import { createServer } from "node:http";
|
|
@@ -41,7 +41,7 @@ import { createServer } from "node:http";
|
|
|
41
41
|
/**
|
|
42
42
|
* Production server for vinext.
|
|
43
43
|
*
|
|
44
|
-
* Serves the built output from `
|
|
44
|
+
* Serves the built output from `vite build`. Handles:
|
|
45
45
|
* - Static asset serving from client build output
|
|
46
46
|
* - Pages Router: SSR rendering + API route handling
|
|
47
47
|
* - App Router: RSC/SSR rendering, route handlers, server actions
|
|
@@ -842,7 +842,7 @@ async function startProdServer(options = {}) {
|
|
|
842
842
|
const isAppRouter = fs.existsSync(rscEntryPath);
|
|
843
843
|
if (!isAppRouter && !fs.existsSync(serverEntryPath)) {
|
|
844
844
|
console.error(`[vinext] No build output found in ${outDir}`);
|
|
845
|
-
console.error("Run `
|
|
845
|
+
console.error("Run `vite build` first.");
|
|
846
846
|
process.exit(1);
|
|
847
847
|
}
|
|
848
848
|
if (isAppRouter) return startAppRouterServer({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
|
|
2
1
|
import "../utils/protocol-headers.js";
|
|
3
|
-
import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS } from "./headers.js";
|
|
4
2
|
import { getUnconsumedMiddlewareRequestHeaders } from "../utils/middleware-request-headers.js";
|
|
3
|
+
import { hasBasePath, removeTrailingSlash, stripBasePath } from "../utils/base-path.js";
|
|
4
|
+
import { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS } from "./headers.js";
|
|
5
5
|
import { forbiddenResponse, methodNotAllowedResponse, notFoundResponse } from "./http-error-responses.js";
|
|
6
6
|
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
7
7
|
import { createStaticFileSignal } from "./static-file-signal.js";
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
*
|
|
34
34
|
* **Prerender check is dynamic, not install-time.** Prerender (in
|
|
35
35
|
* `build/prerender.ts` and `build/run-prerender.ts`) calls
|
|
36
|
-
* `startProdServer()` from inside `
|
|
36
|
+
* `startProdServer()` from inside `vite build` to render pages
|
|
37
37
|
* against a real HTTP server. User `fetch()` calls during prerender
|
|
38
38
|
* hit external APIs that can drop connections; absorbing those would
|
|
39
39
|
* silently produce corrupt prerendered output instead of crashing
|
|
@@ -6,7 +6,45 @@ type CacheCaptureEnvelope = {
|
|
|
6
6
|
type: typeof CACHE_CAPTURE_TYPE;
|
|
7
7
|
encrypted: string | PromiseLike<string>;
|
|
8
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* Flight serializes a promise as its resolved value and drops its own fields.
|
|
11
|
+
* Next.js params are promises that also expose their resolved fields, and the
|
|
12
|
+
* cache key is built from those fields, so the payload carries each such
|
|
13
|
+
* promise's fields next to the arguments and decoding assigns them back.
|
|
14
|
+
*/
|
|
15
|
+
type EncodedCacheArguments = {
|
|
16
|
+
args: unknown[];
|
|
17
|
+
thenableObjects: EncodedThenableObject[];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Flight writes each object once, so `promise` decodes to the same promise as
|
|
21
|
+
* every other reference to it, including ones inside resolved values, and the
|
|
22
|
+
* fields share references with the arguments. The fields build the cache key;
|
|
23
|
+
* the resolved value can differ: the params proxy hides params named like
|
|
24
|
+
* promise or React fields (`value`, `status`) from its own keys but still
|
|
25
|
+
* resolves to them.
|
|
26
|
+
*/
|
|
27
|
+
type EncodedThenableObject = {
|
|
28
|
+
fields: Record<string, unknown>;
|
|
29
|
+
promise: PromiseLike<unknown>;
|
|
30
|
+
};
|
|
9
31
|
export declare function encryptCacheCaptures(captures: unknown[]): CacheCaptureEnvelope;
|
|
32
|
+
/**
|
|
33
|
+
* What to do when a read would run a getter: `reject` throws
|
|
34
|
+
* `UnreplayableCacheArgumentsError`, `skip` leaves the getter unread.
|
|
35
|
+
*/
|
|
36
|
+
type GetterHandling = "reject" | "skip";
|
|
37
|
+
/**
|
|
38
|
+
* Collect the own fields of every promise-augmented object in the arguments,
|
|
39
|
+
* including ones inside resolved promise values. The arguments are passed to
|
|
40
|
+
* Flight as they are, so Flight keeps their shared references and cycles.
|
|
41
|
+
* With `getters: "reject"`, throws `UnreplayableCacheArgumentsError` when the
|
|
42
|
+
* arguments hold a getter; with `"skip"`, promise fields behind a getter are
|
|
43
|
+
* not recorded.
|
|
44
|
+
*/
|
|
45
|
+
export declare function encodeCacheArguments(args: unknown[], getters?: GetterHandling): Promise<EncodedCacheArguments>;
|
|
46
|
+
/** Restore the promise fields that `encodeCacheArguments` captured before Flight encoding. */
|
|
47
|
+
export declare function decodeCacheArguments(value: unknown): unknown[];
|
|
10
48
|
export declare function registerCachedFunction<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>, id: string, variant: string, options: RegisterCachedFunctionOptions): (...args: TArgs) => Promise<TResult>;
|
|
11
49
|
/** Load and invoke one transformed cache function through its server-reference identity. */
|
|
12
50
|
export declare function invokeCacheFunction(invocation: VinextCacheFunctionInvocation, loadServerAction: (id: string) => Promise<unknown>): Promise<void>;
|
|
@@ -1,39 +1,190 @@
|
|
|
1
|
-
import { isUseCacheFunction, registerCachedFunction as registerCachedFunction$1 } from "./cache-runtime.js";
|
|
1
|
+
import { isThenableObject, isUseCacheFunction, registerCachedFunction as registerCachedFunction$1 } from "./cache-runtime.js";
|
|
2
2
|
import { decryptActionBoundArgs, encryptActionBoundArgs } from "@vitejs/plugin-rsc/utils/encryption-runtime";
|
|
3
3
|
//#region src/shims/cache-callable-runtime.ts
|
|
4
4
|
const CACHE_CAPTURE_TYPE = "use-cache-captures";
|
|
5
5
|
function encryptCacheCaptures(captures) {
|
|
6
6
|
return {
|
|
7
7
|
type: CACHE_CAPTURE_TYPE,
|
|
8
|
-
encrypted:
|
|
8
|
+
encrypted: encryptCaptures(captures)
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
11
|
async function decryptCacheCaptures(value) {
|
|
12
12
|
if (!isCacheCaptureEnvelope(value)) return;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
return decryptCacheArguments(value.encrypted);
|
|
14
|
+
}
|
|
15
|
+
async function encryptCaptures(captures) {
|
|
16
|
+
return encryptActionBoundArgs(await encodeCacheArguments(captures, "skip"));
|
|
17
|
+
}
|
|
18
|
+
async function encryptCacheArguments(args) {
|
|
19
|
+
return encryptActionBoundArgs(await encodeCacheArguments(args));
|
|
20
|
+
}
|
|
21
|
+
async function decryptCacheArguments(encrypted) {
|
|
22
|
+
return decodeCacheArguments(await decryptActionBoundArgs(Promise.resolve(encrypted)));
|
|
23
|
+
}
|
|
24
|
+
function isThenable(value) {
|
|
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
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (isThenable(value)) {
|
|
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);
|
|
111
|
+
}
|
|
112
|
+
if (unsettled.length === 0) return {
|
|
113
|
+
args,
|
|
114
|
+
thenableObjects
|
|
115
|
+
};
|
|
116
|
+
const results = await Promise.allSettled(unsettled);
|
|
117
|
+
unsettled.forEach((thenable, index) => settled.set(thenable, results[index]));
|
|
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;
|
|
129
|
+
}
|
|
130
|
+
function isEncodedThenableObject(value) {
|
|
131
|
+
if (typeof value !== "object" || value === null || !isPlainRecord(value)) return false;
|
|
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;
|
|
16
169
|
}
|
|
17
170
|
function isCacheCaptureEnvelope(value) {
|
|
18
171
|
if (typeof value !== "object" || value === null) return false;
|
|
19
172
|
if (!("type" in value) || value.type !== CACHE_CAPTURE_TYPE || !("encrypted" in value)) return false;
|
|
20
173
|
const encrypted = value.encrypted;
|
|
21
|
-
return typeof encrypted === "string" || typeof encrypted === "object" && encrypted !== null &&
|
|
174
|
+
return typeof encrypted === "string" || typeof encrypted === "object" && encrypted !== null && isThenable(encrypted);
|
|
22
175
|
}
|
|
23
176
|
function registerCachedFunction(fn, id, variant, options) {
|
|
24
177
|
return registerCachedFunction$1(fn, id, variant, {
|
|
25
178
|
...options,
|
|
26
179
|
decryptCaptures: decryptCacheCaptures,
|
|
27
|
-
encodeInvocationArgs:
|
|
180
|
+
encodeInvocationArgs: encryptCacheArguments
|
|
28
181
|
});
|
|
29
182
|
}
|
|
30
183
|
/** Load and invoke one transformed cache function through its server-reference identity. */
|
|
31
184
|
async function invokeCacheFunction(invocation, loadServerAction) {
|
|
32
185
|
const fn = await loadServerAction(invocation.referenceId);
|
|
33
186
|
if (!isUseCacheFunction(fn)) throw new Error(`Server reference ${invocation.referenceId} is not a cache function`);
|
|
34
|
-
|
|
35
|
-
if (!Array.isArray(args)) throw new Error("Invalid cache function arguments");
|
|
36
|
-
await fn(...args);
|
|
187
|
+
await fn(...await decryptCacheArguments(invocation.encryptedArgs));
|
|
37
188
|
}
|
|
38
189
|
//#endregion
|
|
39
|
-
export { encryptCacheCaptures, invokeCacheFunction, registerCachedFunction };
|
|
190
|
+
export { decodeCacheArguments, encodeCacheArguments, encryptCacheCaptures, invokeCacheFunction, registerCachedFunction };
|
|
@@ -83,14 +83,6 @@ export type RegisterCachedFunctionOptions = {
|
|
|
83
83
|
* transform records this separately for metadata parent resolution.
|
|
84
84
|
*/
|
|
85
85
|
acceptsSecondArgument?: boolean;
|
|
86
|
-
/**
|
|
87
|
-
* Internal transform metadata for file-level `"use cache"` default exports
|
|
88
|
-
* in App Router `page.*` files. Page components receive framework-owned
|
|
89
|
-
* `{ params, searchParams }` props. React may copy that props object before
|
|
90
|
-
* invocation, so this invariant must live at the cached function boundary
|
|
91
|
-
* rather than on the intermediate createElement config object.
|
|
92
|
-
*/
|
|
93
|
-
appPageDefaultExport?: boolean;
|
|
94
86
|
/** Number of declared arguments supplied by the directive transform. */
|
|
95
87
|
argumentCount?: number;
|
|
96
88
|
decryptCaptures?: (value: unknown) => Promise<unknown[] | undefined>;
|
|
@@ -109,5 +101,11 @@ export type RegisterCachedFunctionOptions = {
|
|
|
109
101
|
export declare function registerCachedFunction<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>, id: string, variant?: string, options?: RegisterCachedFunctionOptions): (...args: TArgs) => Promise<TResult>;
|
|
110
102
|
/** Whether a loaded server reference is a transformed `"use cache"` function. */
|
|
111
103
|
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>;
|
|
112
110
|
//#endregion
|
|
113
111
|
export { markAppPagePropsForUseCache };
|
|
@@ -6,10 +6,11 @@ import { isRouteCacheabilityProbe, recordRouteCacheabilityProbeBailout } from ".
|
|
|
6
6
|
import { getDataCacheHandler } from "./cache-handler.js";
|
|
7
7
|
import { suppressHangingPromiseAbortRejections } from "./internal/make-hanging-promise.js";
|
|
8
8
|
import { createPprFallbackShellSuspensePromise, trackPprFallbackShellCacheTask } from "./ppr-fallback-shell.js";
|
|
9
|
-
import { isDraftModeEnabled, markDynamicUsage } from "./headers.js";
|
|
9
|
+
import { isDraftModeEnabled, markDynamicUsage, throwIfInsideCacheScope } from "./headers.js";
|
|
10
10
|
import { _hasPendingRevalidatedTag, _registerCacheContextAccessor, _setRequestScopedCacheLife, cacheLifeProfiles } from "./cache-request-state.js";
|
|
11
11
|
import { addCollectedRequestTags, getCurrentFetchSoftTags } from "./fetch-cache.js";
|
|
12
|
-
import {
|
|
12
|
+
import { makeThenableParams } from "./thenable-params.js";
|
|
13
|
+
import { APP_PAGE_USE_CACHE_MARKER, hasUseCachePageMarker, isMarkedAppPagePropsObject, isUseCacheFunctionReference, markAppPagePropsForUseCache } from "./internal/app-page-props-cache-key.js";
|
|
13
14
|
import { getCurrentRootParams } from "./root-params.js";
|
|
14
15
|
//#region src/shims/cache-runtime.ts
|
|
15
16
|
/**
|
|
@@ -309,7 +310,7 @@ function propagateRootParamNamesToParent(names) {
|
|
|
309
310
|
*/
|
|
310
311
|
function registerCachedFunction(fn, id, variant, options = {}) {
|
|
311
312
|
const cacheVariant = variant ?? "";
|
|
312
|
-
const
|
|
313
|
+
const omitAppPageSearchParams = cacheVariant !== "private";
|
|
313
314
|
const cacheFunctionId = options.serverReferenceId ? JSON.stringify([id, options.serverReferenceId]) : id;
|
|
314
315
|
const isDev = typeof process !== "undefined" && process.env.NODE_ENV === "development";
|
|
315
316
|
const cachedFn = (...args) => {
|
|
@@ -347,12 +348,19 @@ function registerCachedFunction(fn, id, variant, options = {}) {
|
|
|
347
348
|
const keySeed = getUseCacheKeySeed();
|
|
348
349
|
const captures = options.decryptCaptures ? await options.decryptCaptures(args[0]) : void 0;
|
|
349
350
|
const hasCaptureEnvelope = captures !== void 0;
|
|
350
|
-
const
|
|
351
|
+
const pagePropsArgIndex = args.findIndex(hasUseCachePageMarker);
|
|
352
|
+
const isPageInvocation = pagePropsArgIndex !== -1;
|
|
353
|
+
const invocationArgs = isPageInvocation ? replaceArgument(args, pagePropsArgIndex, withoutUseCachePageMarker(args[pagePropsArgIndex])) : args;
|
|
354
|
+
const admittedArgs = options.argumentCount === void 0 ? invocationArgs : hasCaptureEnvelope ? [invocationArgs[0], ...invocationArgs.slice(1, 1 + options.argumentCount)] : invocationArgs.slice(0, options.argumentCount);
|
|
351
355
|
const executionArgs = hasCaptureEnvelope ? [captures, ...admittedArgs.slice(1)] : admittedArgs;
|
|
352
|
-
const
|
|
356
|
+
const pagePropsIndex = omitAppPageSearchParams && isPageInvocation ? pagePropsArgIndex : void 0;
|
|
357
|
+
const callArgs = pagePropsIndex === void 0 ? executionArgs : withErroringPageSearchParams(executionArgs, pagePropsIndex);
|
|
353
358
|
let cacheKey;
|
|
354
359
|
try {
|
|
355
|
-
const processedArgs = executionArgs.length > 0 ? unwrapThenableObjectArray(executionArgs, {
|
|
360
|
+
const processedArgs = executionArgs.length > 0 ? unwrapThenableObjectArray(executionArgs, {
|
|
361
|
+
pagePropsIndex,
|
|
362
|
+
omitMarkedAppPageSearchParams: omitAppPageSearchParams
|
|
363
|
+
}) : [];
|
|
356
364
|
if (rsc && executionArgs.length > 0) {
|
|
357
365
|
const tempRefs = rsc.createClientTemporaryReferenceSet();
|
|
358
366
|
const encoded = await rsc.encodeReply(processedArgs, { temporaryReferences: tempRefs });
|
|
@@ -423,7 +431,7 @@ function registerCachedFunction(fn, id, variant, options = {}) {
|
|
|
423
431
|
let cacheFunctionInvocation;
|
|
424
432
|
if (options.serverReferenceId && options.encodeInvocationArgs) try {
|
|
425
433
|
cacheFunctionInvocation = {
|
|
426
|
-
encryptedArgs: await options.encodeInvocationArgs(admittedArgs),
|
|
434
|
+
encryptedArgs: await options.encodeInvocationArgs(pagePropsIndex === void 0 ? admittedArgs : toReplayablePageArgs(admittedArgs, pagePropsIndex)),
|
|
427
435
|
referenceId: options.serverReferenceId,
|
|
428
436
|
rootParams: Object.fromEntries(Object.entries(rootParams ?? {}).filter((entry) => entry[1] !== void 0)),
|
|
429
437
|
softTags
|
|
@@ -487,7 +495,7 @@ const USE_CACHE_FUNCTION_SYMBOL = Symbol.for("vinext.useCacheFunction");
|
|
|
487
495
|
const USE_CACHE_ACCEPTS_SECOND_ARGUMENT_SYMBOL = Symbol.for("vinext.useCacheAcceptsSecondArgument");
|
|
488
496
|
/** Whether a loaded server reference is a transformed `"use cache"` function. */
|
|
489
497
|
function isUseCacheFunction(value) {
|
|
490
|
-
return
|
|
498
|
+
return isUseCacheFunctionReference(value);
|
|
491
499
|
}
|
|
492
500
|
function throwPrivateUseCacheInsidePublicUseCacheError() {
|
|
493
501
|
const error = /* @__PURE__ */ new Error("\"use cache: private\" must not be used within \"use cache\". It can only be nested inside of another \"use cache: private\".");
|
|
@@ -595,27 +603,79 @@ async function runCachedFunctionWithContext(fn, args, variant, collectResult) {
|
|
|
595
603
|
collectedResult
|
|
596
604
|
};
|
|
597
605
|
}
|
|
598
|
-
function unwrapThenableObjects(value, options
|
|
606
|
+
function unwrapThenableObjects(value, options) {
|
|
599
607
|
if (value === null || value === void 0 || typeof value !== "object") return value;
|
|
600
|
-
|
|
601
|
-
if (
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
return plain;
|
|
607
|
-
}
|
|
608
|
-
return value;
|
|
608
|
+
const childOptions = { omitMarkedAppPageSearchParams: options.omitMarkedAppPageSearchParams };
|
|
609
|
+
if (Array.isArray(value)) return value.map((item) => unwrapThenableObjects(item, childOptions));
|
|
610
|
+
if (isThenableObject(value)) {
|
|
611
|
+
const plain = {};
|
|
612
|
+
for (const key of Object.keys(value)) plain[key] = unwrapThenableObjects(value[key], childOptions);
|
|
613
|
+
return plain;
|
|
609
614
|
}
|
|
615
|
+
if (typeof value.then === "function") return value;
|
|
610
616
|
const result = {};
|
|
611
617
|
for (const key of Object.keys(value)) {
|
|
612
|
-
if (key === "searchParams" && (options.omitAppPageSearchParamsAtRoot || isMarkedAppPagePropsObject(value))) continue;
|
|
613
|
-
result[key] = unwrapThenableObjects(value[key]);
|
|
618
|
+
if (key === "searchParams" && (options.omitAppPageSearchParamsAtRoot || options.omitMarkedAppPageSearchParams && isMarkedAppPagePropsObject(value))) continue;
|
|
619
|
+
result[key] = unwrapThenableObjects(value[key], childOptions);
|
|
614
620
|
}
|
|
615
621
|
return result;
|
|
616
622
|
}
|
|
623
|
+
/**
|
|
624
|
+
* A thenable (not an array) with own enumerable properties — the
|
|
625
|
+
* `Object.assign(Promise.resolve(obj), obj)` pattern Next.js params use. The
|
|
626
|
+
* cache key is built from its fields instead of the promise.
|
|
627
|
+
*/
|
|
628
|
+
function isThenableObject(value) {
|
|
629
|
+
return !Array.isArray(value) && "then" in value && typeof value.then === "function" && Object.keys(value).length > 0;
|
|
630
|
+
}
|
|
631
|
+
function isPagePropsObject(value) {
|
|
632
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
633
|
+
}
|
|
634
|
+
function replaceArgument(args, index, value) {
|
|
635
|
+
const result = [...args];
|
|
636
|
+
result[index] = value;
|
|
637
|
+
return result;
|
|
638
|
+
}
|
|
639
|
+
/** Remove the `$$isPage` invocation marker before props reach the key or user code. */
|
|
640
|
+
function withoutUseCachePageMarker(props) {
|
|
641
|
+
const { [APP_PAGE_USE_CACHE_MARKER]: _marker, ...pageProps } = props;
|
|
642
|
+
return isMarkedAppPagePropsObject(props) ? markAppPagePropsForUseCache(pageProps) : pageProps;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Response Store replay args for a public page cache: drop `searchParams` from
|
|
646
|
+
* the page props at `index` (after a capture envelope, if any), as Next.js does
|
|
647
|
+
* for the serialized arguments (use-cache-wrapper.ts, `isPageSegmentFunction`),
|
|
648
|
+
* and keep the `$$isPage` marker so a replay regains page semantics, including
|
|
649
|
+
* the erroring searchParams fallback.
|
|
650
|
+
*/
|
|
651
|
+
function toReplayablePageArgs(args, index) {
|
|
652
|
+
const props = args[index];
|
|
653
|
+
if (!isPagePropsObject(props)) return [...args];
|
|
654
|
+
const { searchParams: _searchParams, ...pageProps } = props;
|
|
655
|
+
return replaceArgument(args, index, {
|
|
656
|
+
...pageProps,
|
|
657
|
+
[APP_PAGE_USE_CACHE_MARKER]: true
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Give page props at `index` without `searchParams` (a Response Store replay of
|
|
662
|
+
* args encoded by `toReplayablePageArgs`) a value that throws on
|
|
663
|
+
* access inside the cache scope, like Next.js's
|
|
664
|
+
* `makeErroringSearchParamsForUseCache`, instead of `undefined`.
|
|
665
|
+
*/
|
|
666
|
+
function withErroringPageSearchParams(args, index) {
|
|
667
|
+
const props = args[index];
|
|
668
|
+
if (!isPagePropsObject(props) || "searchParams" in props) return args;
|
|
669
|
+
return replaceArgument(args, index, {
|
|
670
|
+
...props,
|
|
671
|
+
searchParams: makeThenableParams({}, { observeParamAccess: () => throwIfInsideCacheScope("searchParams") })
|
|
672
|
+
});
|
|
673
|
+
}
|
|
617
674
|
function unwrapThenableObjectArray(values, options) {
|
|
618
|
-
return values.map((value, index) => unwrapThenableObjects(value, {
|
|
675
|
+
return values.map((value, index) => unwrapThenableObjects(value, {
|
|
676
|
+
omitAppPageSearchParamsAtRoot: index === options.pagePropsIndex,
|
|
677
|
+
omitMarkedAppPageSearchParams: options.omitMarkedAppPageSearchParams
|
|
678
|
+
}));
|
|
619
679
|
}
|
|
620
680
|
function stableStringify(value, seen) {
|
|
621
681
|
if (value === void 0) return "undefined";
|
|
@@ -642,4 +702,4 @@ function stableStringify(value, seen) {
|
|
|
642
702
|
return JSON.stringify(value);
|
|
643
703
|
}
|
|
644
704
|
//#endregion
|
|
645
|
-
export { NestedDynamicUseCacheError, buildUseCacheKey, cacheContextStorage, clearPrivateCache, getCacheContext, isUseCacheFunction, markAppPagePropsForUseCache, registerCachedFunction, replyToCacheKey, runWithPrivateCache };
|
|
705
|
+
export { NestedDynamicUseCacheError, buildUseCacheKey, cacheContextStorage, clearPrivateCache, getCacheContext, isThenableObject, isUseCacheFunction, markAppPagePropsForUseCache, registerCachedFunction, replyToCacheKey, runWithPrivateCache };
|
package/dist/shims/font-local.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { fnv1a64 } from "../utils/hash.js";
|
|
2
1
|
import { escapeCSSString, formatFontClassRule, getFontMimeType, resolveSingleFaceStyle, sanitizeCSSVarName, sanitizeFallback, sanitizeFontDescriptorValue } from "./font-utils.js";
|
|
2
|
+
import { fnv1a64 } from "../utils/hash.js";
|
|
3
3
|
//#region src/shims/font-local.ts
|
|
4
4
|
/**
|
|
5
5
|
* next/font/local shim
|
package/dist/shims/form.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { isBotUserAgent } from "../utils/html-limited-bots.js";
|
|
2
3
|
import { VINEXT_MOUNTED_SLOTS_HEADER } from "../server/app-rsc-vary.js";
|
|
3
4
|
import "../server/headers.js";
|
|
4
5
|
import { assertSafeNavigationUrl } from "./url-safety.js";
|
|
@@ -6,7 +7,6 @@ import { APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL } from "../server/app-rsc-re
|
|
|
6
7
|
import { AppElementsWire } from "../server/app-elements-wire.js";
|
|
7
8
|
import "../server/app-elements.js";
|
|
8
9
|
import { withBasePath } from "./url-utils.js";
|
|
9
|
-
import { isBotUserAgent } from "../utils/html-limited-bots.js";
|
|
10
10
|
import { createRscRequestHeaders, createRscRequestUrl } from "../server/app-rsc-cache-busting.js";
|
|
11
11
|
import { hasAppNavigationRuntime } from "../client/navigation-runtime.js";
|
|
12
12
|
import { getMountedSlotsHeader, getPrefetchInterceptionContext, getPrefetchedUrls, hasPrefetchCacheEntryForNavigation, navigateClientSide, prefetchRscResponse } from "./navigation.js";
|
package/dist/shims/headers.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { getOrCreateAls } from "./internal/als-registry.js";
|
|
2
2
|
import { getRequestContext, isInsideUnifiedScope, runWithUnifiedStateMutation } from "./unified-request-context.js";
|
|
3
|
-
import { parseEdgeRequestCookieHeader } from "../utils/parse-cookie.js";
|
|
4
3
|
import { MIDDLEWARE_SET_COOKIE_HEADER } from "../utils/protocol-headers.js";
|
|
5
|
-
import { FLIGHT_HEADERS, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER } from "../server/headers.js";
|
|
6
4
|
import { buildRequestHeadersFromMiddlewareResponse } from "../utils/middleware-request-headers.js";
|
|
5
|
+
import { parseEdgeRequestCookieHeader } from "../utils/parse-cookie.js";
|
|
6
|
+
import { FLIGHT_HEADERS, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER } from "../server/headers.js";
|
|
7
7
|
import { serializeSetCookie, validateCookieName } from "./internal/cookie-serialize.js";
|
|
8
8
|
import { createPprFallbackShellSuspensePromise } from "./ppr-fallback-shell.js";
|
|
9
9
|
//#region src/shims/headers.ts
|
|
@@ -1,4 +1,33 @@
|
|
|
1
1
|
//#region src/shims/internal/app-page-props-cache-key.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Enumerable page marker, matching Next.js's `$$isPage` prop. It must be an
|
|
4
|
+
* ordinary string key: React's createElement drops symbol and non-enumerable
|
|
5
|
+
* props before a server component is invoked.
|
|
6
|
+
*/
|
|
7
|
+
export declare const APP_PAGE_USE_CACHE_MARKER = "$$isPage";
|
|
2
8
|
export declare function markAppPagePropsForUseCache<T extends object>(props: T): T;
|
|
3
9
|
export declare function isMarkedAppPagePropsObject(value: object): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Whether `fn` is a transformed `"use cache"` function, including a bound
|
|
12
|
+
* one. Like Next.js's `isUseCacheFunction`, this survives `.bind()`: React's
|
|
13
|
+
* server-reference `bind` copies `$$typeof`/`$$id` to the bound function but
|
|
14
|
+
* not vinext's own marker symbol.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isUseCacheFunctionReference(fn: unknown): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Add Next.js's `$$isPage` marker when `fn` is a `"use cache"` function
|
|
19
|
+
* invoked as a page component or as a page's generateMetadata/generateViewport
|
|
20
|
+
* (create-component-tree.tsx, resolve-metadata.ts `createSegmentProps`). The
|
|
21
|
+
* cache wrapper reads and removes it, so page semantics follow the invocation
|
|
22
|
+
* even when the cached function is defined in (or re-exported from) another
|
|
23
|
+
* module, or bound.
|
|
24
|
+
*/
|
|
25
|
+
export declare function withUseCachePageMarker<T extends Record<string, unknown>>(fn: unknown, props: T): T;
|
|
26
|
+
/**
|
|
27
|
+
* Whether `value` is page props carrying the `$$isPage` marker. Only plain
|
|
28
|
+
* objects qualify: the framework always passes a plain props object, and
|
|
29
|
+
* reading the marker through a thenable params/searchParams proxy would be
|
|
30
|
+
* observed as a param access.
|
|
31
|
+
*/
|
|
32
|
+
export declare function hasUseCachePageMarker(value: unknown): value is Record<string, unknown>;
|
|
4
33
|
//#endregion
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
//#region src/shims/internal/app-page-props-cache-key.ts
|
|
2
2
|
const APP_PAGE_PROPS_CACHE_KEY_MARKER = Symbol.for("vinext.appPagePropsCacheKeyMarker");
|
|
3
|
+
const USE_CACHE_FUNCTION_SYMBOL = Symbol.for("vinext.useCacheFunction");
|
|
4
|
+
const SERVER_REFERENCE_TAG = Symbol.for("react.server.reference");
|
|
5
|
+
/**
|
|
6
|
+
* Server reference ids of transformed cache functions end in the export name
|
|
7
|
+
* the use-cache transform assigns (`secureExportName` in
|
|
8
|
+
* plugins/use-cache-callable.ts): `$$vinext_cache_` plus an HMAC-SHA256 hex
|
|
9
|
+
* digest. `"use server"` actions keep their own export names.
|
|
10
|
+
*/
|
|
11
|
+
const USE_CACHE_SERVER_REFERENCE_ID_RE = /#\$\$vinext_cache_[0-9a-f]{64}$/;
|
|
12
|
+
/**
|
|
13
|
+
* Enumerable page marker, matching Next.js's `$$isPage` prop. It must be an
|
|
14
|
+
* ordinary string key: React's createElement drops symbol and non-enumerable
|
|
15
|
+
* props before a server component is invoked.
|
|
16
|
+
*/
|
|
17
|
+
const APP_PAGE_USE_CACHE_MARKER = "$$isPage";
|
|
3
18
|
function markAppPagePropsForUseCache(props) {
|
|
4
19
|
Object.defineProperty(props, APP_PAGE_PROPS_CACHE_KEY_MARKER, {
|
|
5
20
|
configurable: false,
|
|
@@ -12,5 +27,42 @@ function markAppPagePropsForUseCache(props) {
|
|
|
12
27
|
function isMarkedAppPagePropsObject(value) {
|
|
13
28
|
return Reflect.get(value, APP_PAGE_PROPS_CACHE_KEY_MARKER) === true;
|
|
14
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Whether `fn` is a transformed `"use cache"` function, including a bound
|
|
32
|
+
* one. Like Next.js's `isUseCacheFunction`, this survives `.bind()`: React's
|
|
33
|
+
* server-reference `bind` copies `$$typeof`/`$$id` to the bound function but
|
|
34
|
+
* not vinext's own marker symbol.
|
|
35
|
+
*/
|
|
36
|
+
function isUseCacheFunctionReference(fn) {
|
|
37
|
+
if (typeof fn !== "function") return false;
|
|
38
|
+
if (Reflect.get(fn, USE_CACHE_FUNCTION_SYMBOL) === true) return true;
|
|
39
|
+
const reference = fn;
|
|
40
|
+
return reference.$$typeof === SERVER_REFERENCE_TAG && typeof reference.$$id === "string" && USE_CACHE_SERVER_REFERENCE_ID_RE.test(reference.$$id);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Add Next.js's `$$isPage` marker when `fn` is a `"use cache"` function
|
|
44
|
+
* invoked as a page component or as a page's generateMetadata/generateViewport
|
|
45
|
+
* (create-component-tree.tsx, resolve-metadata.ts `createSegmentProps`). The
|
|
46
|
+
* cache wrapper reads and removes it, so page semantics follow the invocation
|
|
47
|
+
* even when the cached function is defined in (or re-exported from) another
|
|
48
|
+
* module, or bound.
|
|
49
|
+
*/
|
|
50
|
+
function withUseCachePageMarker(fn, props) {
|
|
51
|
+
return isUseCacheFunctionReference(fn) ? {
|
|
52
|
+
...props,
|
|
53
|
+
[APP_PAGE_USE_CACHE_MARKER]: true
|
|
54
|
+
} : props;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Whether `value` is page props carrying the `$$isPage` marker. Only plain
|
|
58
|
+
* objects qualify: the framework always passes a plain props object, and
|
|
59
|
+
* reading the marker through a thenable params/searchParams proxy would be
|
|
60
|
+
* observed as a param access.
|
|
61
|
+
*/
|
|
62
|
+
function hasUseCachePageMarker(value) {
|
|
63
|
+
if (value === null || typeof value !== "object") return false;
|
|
64
|
+
const prototype = Object.getPrototypeOf(value);
|
|
65
|
+
return (prototype === Object.prototype || prototype === null) && value["$$isPage"] === true;
|
|
66
|
+
}
|
|
15
67
|
//#endregion
|
|
16
|
-
export { isMarkedAppPagePropsObject, markAppPagePropsForUseCache };
|
|
68
|
+
export { APP_PAGE_USE_CACHE_MARKER, hasUseCachePageMarker, isMarkedAppPagePropsObject, isUseCacheFunctionReference, markAppPagePropsForUseCache, withUseCachePageMarker };
|