vinext 1.0.0-beta.11 → 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.
Files changed (170) hide show
  1. package/README.md +39 -12
  2. package/dist/build/action-owner-manifest.js +23 -5
  3. package/dist/build/lifecycle.d.ts +42 -0
  4. package/dist/build/lifecycle.js +246 -0
  5. package/dist/build/nitro-route-rules.js +1 -1
  6. package/dist/build/pages-client-assets-module.d.ts +0 -3
  7. package/dist/build/pages-client-assets-module.js +1 -13
  8. package/dist/build/precompress.js +1 -1
  9. package/dist/build/prerender-paths.d.ts +1 -1
  10. package/dist/build/prerender-paths.js +6 -6
  11. package/dist/build/prerender-server-pool.js +1 -1
  12. package/dist/build/prerender.d.ts +1 -1
  13. package/dist/build/prerender.js +5 -5
  14. package/dist/build/preview-credentials.d.ts +1 -1
  15. package/dist/build/preview-credentials.js +2 -2
  16. package/dist/build/report.js +1 -1
  17. package/dist/build/run-prerender.d.ts +2 -2
  18. package/dist/build/run-prerender.js +5 -5
  19. package/dist/build/standalone.js +2 -2
  20. package/dist/cli-args.d.ts +5 -13
  21. package/dist/cli-args.js +2 -45
  22. package/dist/cli-dev-config.d.ts +6 -2
  23. package/dist/cli-dev-config.js +160 -3
  24. package/dist/cli.js +104 -439
  25. package/dist/config/config-matchers.js +1 -1
  26. package/dist/config/next-config.d.ts +2 -2
  27. package/dist/config/next-config.js +5 -5
  28. package/dist/config/prerender.d.ts +3 -0
  29. package/dist/config/prerender.js +8 -2
  30. package/dist/entries/app-browser-entry.js +1 -1
  31. package/dist/entries/app-rsc-entry.d.ts +2 -0
  32. package/dist/entries/app-rsc-entry.js +25 -5
  33. package/dist/entries/pages-client-entry.d.ts +1 -0
  34. package/dist/entries/pages-client-entry.js +17 -2
  35. package/dist/index.d.ts +6 -8
  36. package/dist/index.js +282 -105
  37. package/dist/init-cloudflare.d.ts +3 -2
  38. package/dist/init-cloudflare.js +117 -5
  39. package/dist/init-platform.d.ts +2 -0
  40. package/dist/init-platform.js +9 -3
  41. package/dist/init.d.ts +7 -5
  42. package/dist/init.js +41 -22
  43. package/dist/plugins/dynamic-preload-metadata.js +1 -1
  44. package/dist/plugins/extensionless-dynamic-import.js +2 -2
  45. package/dist/plugins/fonts.js +1 -1
  46. package/dist/plugins/ignore-dynamic-requests.js +1 -1
  47. package/dist/plugins/import-meta-url.js +1 -1
  48. package/dist/plugins/instrumentation-client.js +1 -1
  49. package/dist/plugins/og-assets.js +32 -13
  50. package/dist/plugins/og-harfbuzz.d.ts +24 -0
  51. package/dist/plugins/og-harfbuzz.js +260 -0
  52. package/dist/plugins/optimize-imports.js +1 -1
  53. package/dist/plugins/require-condition-resolution.js +2 -2
  54. package/dist/plugins/require-context.js +2 -2
  55. package/dist/plugins/typeof-window.js +1 -1
  56. package/dist/plugins/use-cache-callable.d.ts +0 -2
  57. package/dist/plugins/use-cache-callable.js +40 -22
  58. package/dist/plugins/worker-image-imports.js +1 -1
  59. package/dist/routing/app-route-graph.js +1 -1
  60. package/dist/routing/app-router.js +1 -1
  61. package/dist/routing/pages-router.js +1 -1
  62. package/dist/server/app-action-forwarding.js +1 -1
  63. package/dist/server/app-browser-entry.js +1 -0
  64. package/dist/server/app-browser-error.js +1 -1
  65. package/dist/server/app-elements-wire.js +2 -2
  66. package/dist/server/app-middleware.js +1 -1
  67. package/dist/server/app-optimistic-routing.js +1 -1
  68. package/dist/server/app-page-cache-finalizer.d.ts +1 -0
  69. package/dist/server/app-page-cache-finalizer.js +14 -2
  70. package/dist/server/app-page-dispatch.js +1 -1
  71. package/dist/server/app-page-element-builder.js +3 -2
  72. package/dist/server/app-page-probe.js +3 -3
  73. package/dist/server/app-page-render.js +1 -0
  74. package/dist/server/app-page-request.d.ts +2 -0
  75. package/dist/server/app-page-request.js +16 -12
  76. package/dist/server/app-request-stage-dispatch.js +1 -1
  77. package/dist/server/app-request-stage-independent-entry.js +2 -2
  78. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  79. package/dist/server/app-route-handler-dispatch.js +3 -2
  80. package/dist/server/app-route-handler-execution.d.ts +1 -0
  81. package/dist/server/app-route-handler-execution.js +2 -2
  82. package/dist/server/app-route-handler-runtime.js +1 -1
  83. package/dist/server/app-router-entry.js +2 -2
  84. package/dist/server/app-rsc-handler.d.ts +9 -2
  85. package/dist/server/app-rsc-handler.js +30 -10
  86. package/dist/server/app-rsc-response-stage.js +6 -3
  87. package/dist/server/app-rsc-route-matching.js +1 -1
  88. package/dist/server/app-server-action-execution.js +35 -6
  89. package/dist/server/app-ssr-entry.js +7 -2
  90. package/dist/server/app-ssr-stream.js +25 -6
  91. package/dist/server/app-worker-stages.d.ts +4 -1
  92. package/dist/server/app-worker-stages.js +4 -4
  93. package/dist/server/artifact-compatibility.js +1 -1
  94. package/dist/server/client-reuse-manifest.js +1 -1
  95. package/dist/server/dev-lockfile.d.ts +1 -1
  96. package/dist/server/dev-lockfile.js +4 -1
  97. package/dist/server/dev-server.d.ts +1 -1
  98. package/dist/server/dev-server.js +28 -6
  99. package/dist/server/dev-stack-sourcemap.js +1 -1
  100. package/dist/server/html.d.ts +0 -20
  101. package/dist/server/html.js +16 -1
  102. package/dist/server/instrumentation.d.ts +4 -9
  103. package/dist/server/instrumentation.js +10 -15
  104. package/dist/server/isr-cache.js +3 -3
  105. package/dist/server/metadata-route-response.d.ts +1 -0
  106. package/dist/server/metadata-route-response.js +65 -0
  107. package/dist/server/middleware-runtime.js +3 -3
  108. package/dist/server/pages-asset-tags.d.ts +1 -0
  109. package/dist/server/pages-asset-tags.js +37 -2
  110. package/dist/server/pages-client-assets.d.ts +7 -0
  111. package/dist/server/pages-data-route.js +1 -1
  112. package/dist/server/pages-dev-hydration.d.ts +6 -0
  113. package/dist/server/pages-dev-hydration.js +19 -7
  114. package/dist/server/pages-document-initial-props.d.ts +1 -0
  115. package/dist/server/pages-document-initial-props.js +1 -1
  116. package/dist/server/pages-page-data.js +2 -2
  117. package/dist/server/pages-page-handler.js +5 -2
  118. package/dist/server/pages-page-response.d.ts +1 -0
  119. package/dist/server/pages-page-response.js +3 -2
  120. package/dist/server/pages-request-pipeline.js +3 -3
  121. package/dist/server/pages-request-stage-entry.js +3 -3
  122. package/dist/server/pages-revalidate.js +1 -1
  123. package/dist/server/prerender-route-params.js +1 -1
  124. package/dist/server/prod-server.js +19 -13
  125. package/dist/server/request-pipeline.js +2 -2
  126. package/dist/server/rsc-stream-hints.d.ts +12 -0
  127. package/dist/server/rsc-stream-hints.js +70 -35
  128. package/dist/server/seed-cache.js +3 -4
  129. package/dist/server/socket-error-backstop.js +1 -1
  130. package/dist/shims/cache-callable-runtime.d.ts +38 -0
  131. package/dist/shims/cache-callable-runtime.js +162 -11
  132. package/dist/shims/cache-runtime.d.ts +6 -8
  133. package/dist/shims/cache-runtime.js +89 -24
  134. package/dist/shims/constants.js +1 -1
  135. package/dist/shims/dynamic-preload-chunks.js +6 -2
  136. package/dist/shims/font-local.js +1 -1
  137. package/dist/shims/form.js +1 -1
  138. package/dist/shims/head.js +1 -1
  139. package/dist/shims/headers.js +2 -2
  140. package/dist/shims/image-config.js +2 -0
  141. package/dist/shims/internal/app-page-props-cache-key.d.ts +29 -0
  142. package/dist/shims/internal/app-page-props-cache-key.js +53 -1
  143. package/dist/shims/internal/format-url-object.d.ts +7 -0
  144. package/dist/shims/internal/format-url-object.js +53 -0
  145. package/dist/shims/internal/normalize-router-href.d.ts +15 -0
  146. package/dist/shims/internal/normalize-router-href.js +41 -0
  147. package/dist/shims/internal/pages-data-target.d.ts +4 -1
  148. package/dist/shims/internal/pages-data-target.js +28 -35
  149. package/dist/shims/internal/route-pattern-for-warning.d.ts +2 -2
  150. package/dist/shims/internal/route-pattern-for-warning.js +3 -3
  151. package/dist/shims/link.js +31 -92
  152. package/dist/shims/metadata.js +5 -4
  153. package/dist/shims/navigation.js +2 -2
  154. package/dist/shims/router-state.js +1 -1
  155. package/dist/shims/router.d.ts +8 -7
  156. package/dist/shims/router.js +312 -148
  157. package/dist/shims/script-nonce-context.d.ts +4 -1
  158. package/dist/shims/script-nonce-context.js +15 -6
  159. package/dist/shims/server.js +3 -3
  160. package/dist/utils/built-asset-url.js +1 -1
  161. package/dist/utils/client-runtime-metadata.d.ts +6 -0
  162. package/dist/utils/client-runtime-metadata.js +16 -0
  163. package/dist/utils/lazy-chunks.js +1 -1
  164. package/dist/utils/project.d.ts +1 -11
  165. package/dist/utils/project.js +20 -29
  166. package/dist/utils/react-version.d.ts +3 -1
  167. package/dist/utils/react-version.js +19 -1
  168. package/dist/utils/vite-cli-invocation.d.ts +21 -0
  169. package/dist/utils/vite-cli-invocation.js +144 -0
  170. package/package.json +10 -2
@@ -51,7 +51,7 @@ function concatBytes(first, second) {
51
51
  combined.set(second, first.byteLength);
52
52
  return combined;
53
53
  }
54
- function indexOfByte(bytes, byte, from = 0) {
54
+ function indexOfByte(bytes, byte, from) {
55
55
  for (let index = from; index < bytes.byteLength; index++) if (bytes[index] === byte) return index;
56
56
  return -1;
57
57
  }
@@ -70,8 +70,35 @@ function parseHexBytes(bytes, start, end) {
70
70
  function isUntaggedJsonRowStart(byte) {
71
71
  return byte === 34 || byte === 45 || byte >= 48 && byte <= 57 || byte === 91 || byte === 102 || byte === 110 || byte === 116 || byte === 123;
72
72
  }
73
+ const COLON_BYTE = 58;
74
+ const COMMA_BYTE = 44;
75
+ const NEWLINE_BYTE = 10;
76
+ const HINT_TAG_BYTE = 72;
77
+ const LINK_HINT_CODE_BYTE = 76;
78
+ function createAsciiTagTable(tags) {
79
+ const table = /* @__PURE__ */ new Uint8Array(128);
80
+ for (const tag of tags) table[tag.charCodeAt(0)] = 1;
81
+ return table;
82
+ }
83
+ const LENGTH_PREFIXED_ROW_TAG_TABLE = createAsciiTagTable(LENGTH_PREFIXED_ROW_TAGS);
84
+ const NEWLINE_PREFIXED_ROW_TAG_TABLE = createAsciiTagTable(NEWLINE_PREFIXED_ROW_TAGS);
85
+ function isTagInTable(table, byte) {
86
+ return byte < 128 && table[byte] === 1;
87
+ }
88
+ /**
89
+ * Rewrite stylesheet preload hints in a React Flight stream.
90
+ *
91
+ * The rewrite is byte-length preserving, so this transform keeps React's
92
+ * text chunk boundaries instead of re-emitting one chunk per Flight row. Every
93
+ * downstream consumer (the tee, the SSR Flight client, the inline RSC embed,
94
+ * RSC response wrappers) pays a per-chunk cost, and splitting per row
95
+ * multiplied the chunk count roughly 8x on content-heavy pages. Chunks with
96
+ * no stylesheet hint are forwarded as-is without copying; only rows whose
97
+ * tag is `HL` are decoded and inspected. Length-prefixed bodies remain separate
98
+ * so binary bytes do not force adjacent text through the embed's base64 path.
99
+ */
73
100
  function normalizeReactFlightPreloadHints(stream) {
74
- let carry = /* @__PURE__ */ new Uint8Array();
101
+ let carry = null;
75
102
  let rawBytesRemaining = 0;
76
103
  let passThrough = false;
77
104
  return stream.pipeThrough(new TransformStream({
@@ -80,56 +107,64 @@ function normalizeReactFlightPreloadHints(stream) {
80
107
  controller.enqueue(chunk);
81
108
  return;
82
109
  }
83
- let bytes = concatBytes(carry, chunk);
84
- carry = /* @__PURE__ */ new Uint8Array();
85
- while (bytes.byteLength > 0) {
110
+ const bytes = carry === null ? chunk : concatBytes(carry, chunk);
111
+ carry = null;
112
+ const byteLength = bytes.byteLength;
113
+ let offset = 0;
114
+ let emittedThrough = 0;
115
+ let output = bytes;
116
+ let ownsOutput = bytes !== chunk;
117
+ while (offset < byteLength) {
86
118
  if (rawBytesRemaining > 0) {
87
- const length = Math.min(rawBytesRemaining, bytes.byteLength);
88
- controller.enqueue(bytes.slice(0, length));
119
+ const length = Math.min(rawBytesRemaining, byteLength - offset);
120
+ if (offset > emittedThrough) controller.enqueue(output.subarray(emittedThrough, offset));
121
+ controller.enqueue(bytes.subarray(offset, offset + length));
89
122
  rawBytesRemaining -= length;
90
- bytes = bytes.subarray(length);
123
+ offset += length;
124
+ emittedThrough = offset;
91
125
  continue;
92
126
  }
93
- const colon = indexOfByte(bytes, 58);
94
- if (colon === -1 || colon + 1 === bytes.byteLength) {
95
- carry = bytes.slice();
96
- return;
97
- }
98
- const tag = String.fromCharCode(bytes[colon + 1]);
99
- if (LENGTH_PREFIXED_ROW_TAGS.has(tag)) {
100
- const comma = indexOfByte(bytes, 44, colon + 2);
101
- if (comma === -1) {
102
- carry = bytes.slice();
103
- return;
104
- }
127
+ const colon = indexOfByte(bytes, COLON_BYTE, offset);
128
+ if (colon === -1 || colon + 1 === byteLength) break;
129
+ const tagByte = bytes[colon + 1];
130
+ if (isTagInTable(LENGTH_PREFIXED_ROW_TAG_TABLE, tagByte)) {
131
+ const comma = indexOfByte(bytes, COMMA_BYTE, colon + 2);
132
+ if (comma === -1) break;
105
133
  const length = parseHexBytes(bytes, colon + 2, comma);
106
134
  if (length != null) {
107
- controller.enqueue(bytes.slice(0, comma + 1));
108
135
  rawBytesRemaining = length;
109
- bytes = bytes.subarray(comma + 1);
136
+ offset = comma + 1;
110
137
  continue;
111
138
  }
112
139
  passThrough = true;
113
- controller.enqueue(bytes);
114
- return;
140
+ offset = byteLength;
141
+ break;
115
142
  }
116
- const tagByte = bytes[colon + 1];
117
- if (!NEWLINE_PREFIXED_ROW_TAGS.has(tag) && !isUntaggedJsonRowStart(tagByte)) {
143
+ if (!isTagInTable(NEWLINE_PREFIXED_ROW_TAG_TABLE, tagByte) && !isUntaggedJsonRowStart(tagByte)) {
118
144
  passThrough = true;
119
- controller.enqueue(bytes);
120
- return;
145
+ offset = byteLength;
146
+ break;
121
147
  }
122
- const newline = indexOfByte(bytes, 10);
123
- if (newline === -1) {
124
- carry = bytes.slice();
125
- return;
148
+ const newline = indexOfByte(bytes, NEWLINE_BYTE, offset);
149
+ if (newline === -1) break;
150
+ if (tagByte === HINT_TAG_BYTE && bytes[colon + 2] === LINK_HINT_CODE_BYTE) {
151
+ const line = bytes.subarray(offset, newline + 1);
152
+ const normalized = normalizeReactFlightHintLine(line);
153
+ if (normalized !== line) {
154
+ if (!ownsOutput) {
155
+ output = bytes.slice();
156
+ ownsOutput = true;
157
+ }
158
+ output.set(normalized, offset);
159
+ }
126
160
  }
127
- controller.enqueue(normalizeReactFlightHintLine(bytes.slice(0, newline + 1)));
128
- bytes = bytes.subarray(newline + 1);
161
+ offset = newline + 1;
129
162
  }
163
+ if (offset < byteLength) carry = bytes.slice(offset);
164
+ if (offset > emittedThrough) controller.enqueue(offset === byteLength && emittedThrough === 0 ? output : output.subarray(emittedThrough, offset));
130
165
  },
131
166
  flush(controller) {
132
- if (carry.byteLength > 0) controller.enqueue(rawBytesRemaining > 0 ? carry : normalizeReactFlightHintLine(carry));
167
+ if (carry !== null && carry.byteLength > 0) controller.enqueue(rawBytesRemaining > 0 ? carry : normalizeReactFlightHintLine(carry));
133
168
  }
134
169
  }));
135
170
  }
@@ -1,5 +1,5 @@
1
1
  import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
- import { appIsrCacheKey, isrCacheKey, isrSet, isrSetPrerenderedAppPage } from "./isr-cache.js";
2
+ import { appIsrCacheKey, isrSet, isrSetPrerenderedAppPage } from "./isr-cache.js";
3
3
  import { addPregeneratedConcretePath, addPregeneratedRoute, clearPregeneratedConcretePaths, normalizePregeneratedPathname } from "./pregenerated-concrete-paths.js";
4
4
  import { getRenderedAppRoutes, getRenderedMetadataRoutes, isFallbackShellArtifactPath, readPrerenderManifest } from "./prerender-manifest.js";
5
5
  import { buildAppPageCacheTags, buildAppRouteCacheTags } from "./app-page-cache.js";
@@ -64,9 +64,8 @@ async function seedMemoryCacheFromPrerender(serverDir, options) {
64
64
  if (!isFallbackShellArtifactPath(concretePathname, route)) addPregeneratedConcretePath(route.route, concretePathname);
65
65
  const artifactPathname = route.path ?? route.route;
66
66
  const cachePathname = normalizePregeneratedPathname(artifactPathname);
67
- const baseKey = isrCacheKey("app", cachePathname, buildId);
68
- const htmlKey = options?.buildAppPageHtmlKey?.(cachePathname) ?? baseKey + ":html";
69
- const rscKey = options?.buildAppPageRscKey?.(cachePathname) ?? baseKey + ":rsc";
67
+ const htmlKey = options?.buildAppPageHtmlKey?.(cachePathname) ?? appIsrCacheKey(cachePathname, "html", buildId);
68
+ const rscKey = options?.buildAppPageRscKey?.(cachePathname) ?? appIsrCacheKey(cachePathname, "rsc", buildId);
70
69
  const revalidateSeconds = typeof route.revalidate === "number" ? route.revalidate : void 0;
71
70
  const expireSeconds = typeof route.expire === "number" ? route.expire : void 0;
72
71
  const staleSeconds = typeof route.stale === "number" && route.stale >= 0 ? route.stale : void 0;
@@ -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 `vinext build` to render pages
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: encryptActionBoundArgs(captures)
8
+ encrypted: encryptCaptures(captures)
9
9
  };
10
10
  }
11
11
  async function decryptCacheCaptures(value) {
12
12
  if (!isCacheCaptureEnvelope(value)) return;
13
- const captures = await decryptActionBoundArgs(Promise.resolve(value.encrypted));
14
- if (!Array.isArray(captures)) throw new Error("Invalid cache capture payload");
15
- return captures;
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 && "then" in encrypted && typeof encrypted.then === "function";
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: encryptActionBoundArgs
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
- const args = await decryptActionBoundArgs(Promise.resolve(invocation.encryptedArgs));
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 { isMarkedAppPagePropsObject, markAppPagePropsForUseCache } from "./internal/app-page-props-cache-key.js";
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,8 @@ function propagateRootParamNamesToParent(names) {
309
310
  */
310
311
  function registerCachedFunction(fn, id, variant, options = {}) {
311
312
  const cacheVariant = variant ?? "";
312
- const omitAppPageSearchParamsFromFirstArg = options.appPageDefaultExport === true;
313
+ const omitAppPageSearchParams = cacheVariant !== "private";
314
+ const cacheFunctionId = options.serverReferenceId ? JSON.stringify([id, options.serverReferenceId]) : id;
313
315
  const isDev = typeof process !== "undefined" && process.env.NODE_ENV === "development";
314
316
  const cachedFn = (...args) => {
315
317
  if (cacheVariant === "private") {
@@ -346,16 +348,27 @@ function registerCachedFunction(fn, id, variant, options = {}) {
346
348
  const keySeed = getUseCacheKeySeed();
347
349
  const captures = options.decryptCaptures ? await options.decryptCaptures(args[0]) : void 0;
348
350
  const hasCaptureEnvelope = captures !== void 0;
349
- const admittedArgs = options.argumentCount === void 0 ? args : hasCaptureEnvelope ? [args[0], ...args.slice(1, 1 + options.argumentCount)] : args.slice(0, options.argumentCount);
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);
350
355
  const executionArgs = hasCaptureEnvelope ? [captures, ...admittedArgs.slice(1)] : admittedArgs;
351
- const callArgs = executionArgs;
356
+ const pagePropsIndex = omitAppPageSearchParams && isPageInvocation ? pagePropsArgIndex : void 0;
357
+ const callArgs = pagePropsIndex === void 0 ? executionArgs : withErroringPageSearchParams(executionArgs, pagePropsIndex);
352
358
  let cacheKey;
353
359
  try {
354
- const processedArgs = executionArgs.length > 0 ? unwrapThenableObjectArray(executionArgs, { omitAppPageSearchParamsFromFirstArg }) : [];
360
+ const processedArgs = executionArgs.length > 0 ? unwrapThenableObjectArray(executionArgs, {
361
+ pagePropsIndex,
362
+ omitMarkedAppPageSearchParams: omitAppPageSearchParams
363
+ }) : [];
355
364
  if (rsc && executionArgs.length > 0) {
356
365
  const tempRefs = rsc.createClientTemporaryReferenceSet();
357
- cacheKey = buildUseCacheKey(id, keySeed, await replyToCacheKey(await rsc.encodeReply(processedArgs, { temporaryReferences: tempRefs })));
358
- } else cacheKey = buildUseCacheKey(id, keySeed, processedArgs.length > 0 ? stableStringify(processedArgs) : void 0);
366
+ const encoded = await rsc.encodeReply(processedArgs, { temporaryReferences: tempRefs });
367
+ cacheKey = buildUseCacheKey(cacheFunctionId, keySeed, await replyToCacheKey(encoded));
368
+ } else {
369
+ const argsKey = processedArgs.length > 0 ? stableStringify(processedArgs) : void 0;
370
+ cacheKey = buildUseCacheKey(cacheFunctionId, keySeed, argsKey);
371
+ }
359
372
  } catch {
360
373
  return (await executeWithContext(fn, callArgs, cacheVariant)).result;
361
374
  }
@@ -418,7 +431,7 @@ function registerCachedFunction(fn, id, variant, options = {}) {
418
431
  let cacheFunctionInvocation;
419
432
  if (options.serverReferenceId && options.encodeInvocationArgs) try {
420
433
  cacheFunctionInvocation = {
421
- encryptedArgs: await options.encodeInvocationArgs(admittedArgs),
434
+ encryptedArgs: await options.encodeInvocationArgs(pagePropsIndex === void 0 ? admittedArgs : toReplayablePageArgs(admittedArgs, pagePropsIndex)),
422
435
  referenceId: options.serverReferenceId,
423
436
  rootParams: Object.fromEntries(Object.entries(rootParams ?? {}).filter((entry) => entry[1] !== void 0)),
424
437
  softTags
@@ -482,7 +495,7 @@ const USE_CACHE_FUNCTION_SYMBOL = Symbol.for("vinext.useCacheFunction");
482
495
  const USE_CACHE_ACCEPTS_SECOND_ARGUMENT_SYMBOL = Symbol.for("vinext.useCacheAcceptsSecondArgument");
483
496
  /** Whether a loaded server reference is a transformed `"use cache"` function. */
484
497
  function isUseCacheFunction(value) {
485
- return typeof value === "function" && Reflect.get(value, USE_CACHE_FUNCTION_SYMBOL) === true;
498
+ return isUseCacheFunctionReference(value);
486
499
  }
487
500
  function throwPrivateUseCacheInsidePublicUseCacheError() {
488
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\".");
@@ -590,27 +603,79 @@ async function runCachedFunctionWithContext(fn, args, variant, collectResult) {
590
603
  collectedResult
591
604
  };
592
605
  }
593
- function unwrapThenableObjects(value, options = {}) {
606
+ function unwrapThenableObjects(value, options) {
594
607
  if (value === null || value === void 0 || typeof value !== "object") return value;
595
- if (Array.isArray(value)) return value.map((item) => unwrapThenableObjects(item));
596
- if (typeof value.then === "function") {
597
- const keys = Object.keys(value);
598
- if (keys.length > 0) {
599
- const plain = {};
600
- for (const key of keys) plain[key] = unwrapThenableObjects(value[key]);
601
- return plain;
602
- }
603
- 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;
604
614
  }
615
+ if (typeof value.then === "function") return value;
605
616
  const result = {};
606
617
  for (const key of Object.keys(value)) {
607
- if (key === "searchParams" && (options.omitAppPageSearchParamsAtRoot || isMarkedAppPagePropsObject(value))) continue;
608
- result[key] = unwrapThenableObjects(value[key]);
618
+ if (key === "searchParams" && (options.omitAppPageSearchParamsAtRoot || options.omitMarkedAppPageSearchParams && isMarkedAppPagePropsObject(value))) continue;
619
+ result[key] = unwrapThenableObjects(value[key], childOptions);
609
620
  }
610
621
  return result;
611
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
+ }
612
674
  function unwrapThenableObjectArray(values, options) {
613
- return values.map((value, index) => unwrapThenableObjects(value, { omitAppPageSearchParamsAtRoot: index === 0 && options.omitAppPageSearchParamsFromFirstArg }));
675
+ return values.map((value, index) => unwrapThenableObjects(value, {
676
+ omitAppPageSearchParamsAtRoot: index === options.pagePropsIndex,
677
+ omitMarkedAppPageSearchParams: options.omitMarkedAppPageSearchParams
678
+ }));
614
679
  }
615
680
  function stableStringify(value, seen) {
616
681
  if (value === void 0) return "undefined";
@@ -637,4 +702,4 @@ function stableStringify(value, seen) {
637
702
  return JSON.stringify(value);
638
703
  }
639
704
  //#endregion
640
- export { NestedDynamicUseCacheError, buildUseCacheKey, cacheContextStorage, clearPrivateCache, getCacheContext, isUseCacheFunction, markAppPagePropsForUseCache, registerCachedFunction, replyToCacheKey, runWithPrivateCache };
705
+ export { NestedDynamicUseCacheError, buildUseCacheKey, cacheContextStorage, clearPrivateCache, getCacheContext, isThenableObject, isUseCacheFunction, markAppPagePropsForUseCache, registerCachedFunction, replyToCacheKey, runWithPrivateCache };
@@ -91,7 +91,7 @@ const CONFIG_FILES = [
91
91
  "next.config.js",
92
92
  "next.config.mjs",
93
93
  "next.config.ts",
94
- ...process?.features?.typescript ? ["next.config.mts"] : []
94
+ ...typeof process !== "undefined" && process.features?.typescript ? ["next.config.mts"] : []
95
95
  ];
96
96
  const BUILD_ID_FILE = "BUILD_ID";
97
97
  const BLOCKED_PAGES = [