vinext 1.0.0-beta.11 → 1.0.0-beta.12

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 (54) hide show
  1. package/dist/entries/app-rsc-entry.d.ts +2 -0
  2. package/dist/entries/app-rsc-entry.js +24 -4
  3. package/dist/index.js +21 -5
  4. package/dist/plugins/use-cache-callable.js +32 -6
  5. package/dist/server/app-browser-entry.js +1 -0
  6. package/dist/server/app-page-cache-finalizer.d.ts +1 -0
  7. package/dist/server/app-page-cache-finalizer.js +14 -2
  8. package/dist/server/app-page-render.js +1 -0
  9. package/dist/server/app-page-request.d.ts +2 -0
  10. package/dist/server/app-page-request.js +16 -12
  11. package/dist/server/app-request-stage-dispatch.js +1 -1
  12. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  13. package/dist/server/app-route-handler-dispatch.js +2 -1
  14. package/dist/server/app-route-handler-execution.d.ts +1 -0
  15. package/dist/server/app-route-handler-execution.js +2 -2
  16. package/dist/server/app-rsc-handler.d.ts +8 -1
  17. package/dist/server/app-rsc-handler.js +26 -6
  18. package/dist/server/app-rsc-response-stage.js +5 -2
  19. package/dist/server/app-server-action-execution.js +31 -2
  20. package/dist/server/app-ssr-entry.js +7 -2
  21. package/dist/server/app-ssr-stream.js +25 -6
  22. package/dist/server/app-worker-stages.d.ts +4 -1
  23. package/dist/server/app-worker-stages.js +4 -4
  24. package/dist/server/html.d.ts +0 -20
  25. package/dist/server/html.js +16 -1
  26. package/dist/server/instrumentation.d.ts +4 -9
  27. package/dist/server/instrumentation.js +10 -15
  28. package/dist/server/isr-cache.js +2 -2
  29. package/dist/server/metadata-route-response.d.ts +1 -0
  30. package/dist/server/metadata-route-response.js +57 -0
  31. package/dist/server/pages-asset-tags.d.ts +1 -0
  32. package/dist/server/pages-asset-tags.js +37 -2
  33. package/dist/server/pages-client-assets.d.ts +7 -0
  34. package/dist/server/pages-document-initial-props.d.ts +1 -0
  35. package/dist/server/pages-document-initial-props.js +1 -1
  36. package/dist/server/pages-page-handler.js +4 -1
  37. package/dist/server/pages-page-response.d.ts +1 -0
  38. package/dist/server/pages-page-response.js +2 -1
  39. package/dist/server/prod-server.js +12 -6
  40. package/dist/server/rsc-stream-hints.d.ts +12 -0
  41. package/dist/server/rsc-stream-hints.js +70 -35
  42. package/dist/server/seed-cache.js +3 -4
  43. package/dist/shims/cache-runtime.js +7 -2
  44. package/dist/shims/constants.js +1 -1
  45. package/dist/shims/dynamic-preload-chunks.js +6 -2
  46. package/dist/shims/head.js +1 -1
  47. package/dist/shims/image-config.js +2 -0
  48. package/dist/shims/script-nonce-context.d.ts +4 -1
  49. package/dist/shims/script-nonce-context.js +15 -6
  50. package/dist/utils/client-runtime-metadata.d.ts +6 -0
  51. package/dist/utils/client-runtime-metadata.js +16 -0
  52. package/dist/utils/lazy-chunks.js +1 -1
  53. package/dist/utils/project.js +1 -1
  54. package/package.json +1 -1
@@ -43,6 +43,8 @@ type AppRouterConfig = {
43
43
  * @see https://nextjs.org/docs/app/api-reference/config/next-config-js/assetPrefix
44
44
  */
45
45
  assetPrefix?: string;
46
+ /** CORS mode for framework-managed assets from next.config. */
47
+ crossOrigin?: "anonymous" | "use-credentials";
46
48
  /** Route-level expire fallback in seconds for ISR entries with numeric revalidate. */
47
49
  expireTime?: number;
48
50
  /**
@@ -190,6 +190,7 @@ const __trailingSlash = ${JSON.stringify(ts)};
190
190
  const __draftModeSecret = ${JSON.stringify(config?.draftModeSecret ?? "")};
191
191
  export const __prerenderSecret = ${JSON.stringify(config?.prerenderSecret ?? "")};
192
192
  export const __assetPrefix = ${JSON.stringify(config?.assetPrefix ?? "")};
193
+ export const __crossOrigin = ${JSON.stringify(config?.crossOrigin ?? "")};
193
194
  export { __basePath };
194
195
  export const __imageAllowedWidths = ${JSON.stringify([...config?.imageConfig?.deviceSizes ?? DEFAULT_DEVICE_SIZES, ...config?.imageConfig?.imageSizes ?? DEFAULT_IMAGE_SIZES])};
195
196
  export const __imageConfig = ${JSON.stringify({
@@ -371,6 +372,7 @@ function generateRscEntry(appDir, routes, middlewarePath, metadataRoutes, global
371
372
  const htmlLimitedBots = config?.htmlLimitedBots;
372
373
  const clientTraceMetadata = config?.clientTraceMetadata;
373
374
  const assetPrefix = config?.assetPrefix ?? "";
375
+ const crossOrigin = config?.crossOrigin ?? "";
374
376
  const expireTime = config?.expireTime ?? DEFAULT_EXPIRE_TIME;
375
377
  const reactMaxHeadersLength = config?.reactMaxHeadersLength ?? DEFAULT_REACT_MAX_HEADERS_LENGTH;
376
378
  const cacheMaxMemorySize = config?.cacheMaxMemorySize;
@@ -820,6 +822,7 @@ const __reactMaxHeadersLength = ${JSON.stringify(reactMaxHeadersLength)};
820
822
  // mirrors the embedded \`__basePath\` pattern (and Pages Router's
821
823
  // \`vinextConfig\` export). Empty string when unset.
822
824
  export const __assetPrefix = ${JSON.stringify(assetPrefix)};
825
+ export const __crossOrigin = ${JSON.stringify(crossOrigin)};
823
826
  export const __imageAllowedWidths = ${JSON.stringify(imageAllowedWidths)};
824
827
  export const __imageConfig = ${JSON.stringify(imageConfig)};
825
828
  export const __inlineCss = ${JSON.stringify(inlineCss)};
@@ -898,6 +901,7 @@ ${responseStageOnly ? "const __responseStageOptions = {" : "const __appRscHandle
898
901
  draftModeSecret: __draftModeSecret,
899
902
  dispatchMatchedPage({
900
903
  bypassInterceptionContextCache,
904
+ cachePathname,
901
905
  clientReuseManifest,
902
906
  cleanPathname,
903
907
  displayPathname,
@@ -1025,8 +1029,18 @@ ${responseStageOnly ? "const __responseStageOptions = {" : "const __appRscHandle
1025
1029
  isRscRequest,
1026
1030
  isrDebug: __isrDebug,
1027
1031
  isrGet: __isrGet,
1028
- isrHtmlKey: __isrHtmlKey,
1029
- isrRscKey: __isrRscKey,
1032
+ isrHtmlKey(pathname) {
1033
+ return __isrHtmlKey(pathname === cleanPathname ? cachePathname : pathname);
1034
+ },
1035
+ isrRscKey(pathname, mountedSlots, requestedRenderMode, requestedInterceptionContext, requestedInterceptionId) {
1036
+ return __isrRscKey(
1037
+ pathname === cleanPathname ? cachePathname : pathname,
1038
+ mountedSlots,
1039
+ requestedRenderMode,
1040
+ requestedInterceptionContext,
1041
+ requestedInterceptionId,
1042
+ );
1043
+ },
1030
1044
  isrSet: __isrSet,
1031
1045
  loadSsrHandler() {
1032
1046
  return import.meta.viteRsc.loadModule("ssr", "index");
@@ -1133,6 +1147,8 @@ ${responseStageOnly ? "const __responseStageOptions = {" : "const __appRscHandle
1133
1147
  });
1134
1148
  },
1135
1149
  async dispatchMatchedRouteHandler({
1150
+ bypassInterceptionContextCache,
1151
+ cachePathname,
1136
1152
  cleanPathname,
1137
1153
  middlewareContext,
1138
1154
  params,
@@ -1144,6 +1160,7 @@ ${responseStageOnly ? "const __responseStageOptions = {" : "const __appRscHandle
1144
1160
  await __loadAppRouteHandlerDispatch();
1145
1161
  return __dispatchAppRouteHandler({
1146
1162
  basePath: __basePath,
1163
+ bypassSharedCache: bypassInterceptionContextCache,
1147
1164
  cleanPathname,
1148
1165
  clearRequestContext() {
1149
1166
  __clearRequestContext();
@@ -1153,7 +1170,9 @@ ${responseStageOnly ? "const __responseStageOptions = {" : "const __appRscHandle
1153
1170
  trailingSlash: __trailingSlash,
1154
1171
  isrDebug: __isrDebug,
1155
1172
  isrGet: __isrGet,
1156
- isrRouteKey: __isrRouteKey,
1173
+ isrRouteKey(pathname) {
1174
+ return __isrRouteKey(pathname === cleanPathname ? cachePathname : pathname);
1175
+ },
1157
1176
  isrSet: __isrSet,
1158
1177
  middlewareContext,
1159
1178
  middlewareRequestHeaders: middlewareContext.requestHeaders,
@@ -1461,7 +1480,7 @@ ${responseStageOnly ? "const __responseStageOptions = {" : "const __appRscHandle
1461
1480
  await __loadMetadataRouteResponse();
1462
1481
  return __isMetadataRouteRequestPath(metadataRoutes, cleanPathname);
1463
1482
  },` : ""}
1464
- ${(metadataRoutes?.length ?? 0) > 0 ? `async handleMetadataRouteRequest(cleanPathname) {
1483
+ ${(metadataRoutes?.length ?? 0) > 0 ? `async handleMetadataRouteRequest(cleanPathname, routePathname) {
1465
1484
  const { handleMetadataRouteRequest: __handleMetadataRouteRequest } =
1466
1485
  await __loadMetadataRouteResponse();
1467
1486
  return __handleMetadataRouteRequest({
@@ -1471,6 +1490,7 @@ ${responseStageOnly ? "const __responseStageOptions = {" : "const __appRscHandle
1471
1490
  isrRouteKey: __isrRouteKey,
1472
1491
  isrSet: __isrSet,
1473
1492
  makeThenableParams,
1493
+ routePathname,
1474
1494
  scheduleBackgroundRegeneration: __triggerBackgroundRegeneration,
1475
1495
  });
1476
1496
  },` : ""}
package/dist/index.js CHANGED
@@ -115,7 +115,7 @@ import { VINEXT_PRERENDER_CONFIG_PLUGIN_PROPERTY, VINEXT_ROUTE_ROOT_CONFIG_PLUGI
115
115
  import { staticExportApp, staticExportPages } from "./build/static-export.js";
116
116
  import { createRequire } from "node:module";
117
117
  import fs from "node:fs";
118
- import { createIdResolver, createLogger, parseAst, transformWithOxc } from "vite";
118
+ import { createIdResolver, createLogger, isRunnableDevEnvironment, parseAst, transformWithOxc } from "vite";
119
119
  import { pathToFileURL } from "node:url";
120
120
  import { createHash, randomBytes } from "node:crypto";
121
121
  import commonjs from "vite-plugin-commonjs";
@@ -316,7 +316,7 @@ function createDevPagesModuleDependencyReader(root, resolve) {
316
316
  if (typeof specifier !== "string") continue;
317
317
  const resolved = await resolve(specifier, cleanModulePath, { skipSelf: true });
318
318
  if (!resolved?.id) continue;
319
- if (isStylesheetSpecifier(specifier)) {
319
+ if (isStylesheetSpecifier(specifier) || isStylesheetSpecifier(resolved.id)) {
320
320
  const asset = resolvedStylesheetToDevManifestAsset(root, resolved.id);
321
321
  if (asset) dependencies.push({
322
322
  type: "stylesheet",
@@ -1550,6 +1550,7 @@ function vinext(options = {}) {
1550
1550
  }
1551
1551
  };
1552
1552
  pagesOptimizeEntries = !hasAppDir ? [...hasPagesDir ? [toRelativeFileEntry(root, pagesDir) + "/**/*.{tsx,ts,jsx,js}"] : [], ...[instrumentationPath, instrumentationClientPath].flatMap((entry) => entry ? [toRelativeFileEntry(root, entry)] : [])] : [];
1553
+ if (env.command === "serve" && !hasAppDir && !hasCloudflarePlugin && !shouldInjectPlainPagesEnvironments) viteConfig.optimizeDeps.include = [.../* @__PURE__ */ new Set([...incomingInclude, "react-dom/client"])];
1553
1554
  if (hasAppDir) {
1554
1555
  const appEntries = [`${path.relative(root, appDir)}/**/*.{tsx,ts,jsx,js}`];
1555
1556
  const explicitInstrumentationEntries = [instrumentationPath, instrumentationClientPath].flatMap((entry) => entry ? [toRelativeFileEntry(root, entry)] : []);
@@ -1631,6 +1632,7 @@ function vinext(options = {}) {
1631
1632
  } else if (hasCloudflarePlugin) viteConfig.environments = { client: {
1632
1633
  consumer: "client",
1633
1634
  optimizeDeps: {
1635
+ include: ["react-dom/client"],
1634
1636
  ...pagesOptimizeEntries.length > 0 ? { entries: pagesOptimizeEntries } : {},
1635
1637
  ...depOptimizeNodeEnvOptions
1636
1638
  },
@@ -1649,6 +1651,7 @@ function vinext(options = {}) {
1649
1651
  client: {
1650
1652
  consumer: "client",
1651
1653
  optimizeDeps: {
1654
+ include: ["react-dom/client"],
1652
1655
  ...pagesOptimizeEntries.length > 0 ? { entries: pagesOptimizeEntries } : {},
1653
1656
  ...depOptimizeNodeEnvOptions
1654
1657
  },
@@ -1913,7 +1916,10 @@ function vinext(options = {}) {
1913
1916
  }
1914
1917
  if (id === RESOLVED_CLIENT_ENTRY) return await generateClientEntry$1();
1915
1918
  if (id === RESOLVED_PAGES_CLIENT_ASSETS) {
1916
- const metadata = { clientEntry: DEV_PAGES_CLIENT_ENTRY };
1919
+ const metadata = {
1920
+ clientEntry: DEV_PAGES_CLIENT_ENTRY,
1921
+ crossOrigin: nextConfig?.crossOrigin ?? ""
1922
+ };
1917
1923
  const ssrManifest = {};
1918
1924
  const appFilePath = findFileWithExts(pagesDir, "_app", fileMatcher);
1919
1925
  const pagesRoutes = await pagesRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher);
@@ -1952,6 +1958,7 @@ function vinext(options = {}) {
1952
1958
  clientTraceMetadata: nextConfig?.clientTraceMetadata,
1953
1959
  nodeOpenTelemetryLoader: registerNodeOpenTelemetryLoader,
1954
1960
  assetPrefix: nextConfig?.assetPrefix,
1961
+ crossOrigin: nextConfig?.crossOrigin,
1955
1962
  expireTime: nextConfig?.expireTime,
1956
1963
  reactMaxHeadersLength: nextConfig?.reactMaxHeadersLength,
1957
1964
  cacheMaxMemorySize: nextConfig?.cacheMaxMemorySize,
@@ -2668,7 +2675,14 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
2668
2675
  restore();
2669
2676
  return false;
2670
2677
  };
2671
- const pagesInstrumentationReady = instrumentationPath && (!hasAppDir || !hasCloudflarePlugin) ? runInstrumentation(getPagesRunner(), instrumentationPath) : Promise.resolve();
2678
+ const pagesInstrumentationReady = (async () => {
2679
+ if (!instrumentationPath || hasAppDir && hasCloudflarePlugin) return;
2680
+ if (hasAppDir) {
2681
+ const environment = server.environments["rsc"];
2682
+ if (!isRunnableDevEnvironment(environment)) return;
2683
+ return runInstrumentation(environment.runner, instrumentationPath);
2684
+ } else return runInstrumentation(getPagesRunner(), instrumentationPath);
2685
+ })();
2672
2686
  pagesInstrumentationReady.catch(() => {});
2673
2687
  if (hasAppDir) server.middlewares.use((req, res, next) => {
2674
2688
  const url = req.url ?? "/";
@@ -3645,8 +3659,10 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
3645
3659
  clientEntry: runtimeMetadata.clientEntryFile ?? void 0,
3646
3660
  appBootstrapPreinitModules: runtimeMetadata.appBootstrapPreinitModules,
3647
3661
  ssrManifest,
3662
+ cssGraph: runtimeMetadata.cssGraph,
3648
3663
  lazyChunks: runtimeMetadata.lazyChunks ?? void 0,
3649
- dynamicPreloads: runtimeMetadata.dynamicPreloads ?? void 0
3664
+ dynamicPreloads: runtimeMetadata.dynamicPreloads ?? void 0,
3665
+ crossOrigin: nextConfig.crossOrigin ?? ""
3650
3666
  });
3651
3667
  const buildSession = process.env.__VINEXT_PAGES_CLIENT_ASSETS_BUILD_SESSION;
3652
3668
  if (hasAppDir && hasPagesDir && buildSession) setPagesClientAssetsBuildMetadata(buildSession, pagesClientAssetsModule);
@@ -4,6 +4,7 @@ import { magicStringTransformResult } from "./transform-result.js";
4
4
  import { createRequire } from "node:module";
5
5
  import { parseAstAsync } from "vite";
6
6
  import { pathToFileURL } from "node:url";
7
+ import { createHmac, randomBytes } from "node:crypto";
7
8
  //#region src/plugins/use-cache-callable.ts
8
9
  const PLUGIN_NAME = "vinext:server-function-directives";
9
10
  const SOURCE_MODULE_ID_RE = /\.(?:tsx?|jsx?|mjs)(?:\?.*)?$/;
@@ -65,6 +66,17 @@ function hasFunctionDirective(meta, directive) {
65
66
  if (node?.type !== "FunctionDeclaration" && node?.type !== "FunctionExpression" && node?.type !== "ArrowFunctionExpression" || node.body.type !== "BlockStatement") return false;
66
67
  return node.body.body.some((statement) => statement.type === "ExpressionStatement" && "directive" in statement && statement.directive === directive);
67
68
  }
69
+ function getFunctionDirectiveExportNames(transforms, ast, directive) {
70
+ const names = /* @__PURE__ */ new Set();
71
+ for (const group of transforms.scanModuleExports(ast)) {
72
+ const entries = group.type === "declaration" ? [group.export] : group.type === "variable-declaration" ? group.declarators.flatMap((declarator) => declarator.exports) : group.type === "specifiers" ? group.exports : group.type === "default" ? [{
73
+ exportName: "default",
74
+ meta: group.meta
75
+ }] : [];
76
+ for (const entry of entries) if (hasFunctionDirective(entry.meta, directive)) names.add(entry.exportName);
77
+ }
78
+ return names;
79
+ }
68
80
  function getCacheWrapperOptions(options, id, name, isModuleDirective, meta) {
69
81
  const argumentCount = getArgumentCount(meta);
70
82
  return {
@@ -78,6 +90,7 @@ async function createUseCacheCallablePlugin(options) {
78
90
  const transformsPath = resolvePluginRscModule(options.projectRoot, "@vitejs/plugin-rsc/transforms");
79
91
  const rscModule = await import(pathToFileURL(rscModulePath).href);
80
92
  const transforms = await import(pathToFileURL(transformsPath).href);
93
+ const referenceSecret = randomBytes(32);
81
94
  let manager;
82
95
  return {
83
96
  name: PLUGIN_NAME,
@@ -109,6 +122,8 @@ async function createUseCacheCallablePlugin(options) {
109
122
  const useServerBoundary = transforms.hasDirective(ast.body, "use server");
110
123
  if (moduleDirective && useServerBoundary) throw new Error(`A module cannot contain both ${JSON.stringify(moduleDirective)} and "use server" directives.`);
111
124
  const reference = manager.serverReferences.resolve(id, "rsc");
125
+ const relativeImportId = manager.toRelativeId(reference.importId);
126
+ const secureExportName = (name) => `$$vinext_cache_${createHmac("sha256", referenceSecret).update(relativeImportId).update("\0").update(name).digest("hex")}`;
112
127
  if (!(this.environment.name === "rsc")) {
113
128
  if (useServerBoundary) {
114
129
  manager.serverReferences.deleteClaim(PLUGIN_NAME, id);
@@ -126,6 +141,7 @@ async function createUseCacheCallablePlugin(options) {
126
141
  manager.serverReferences.deleteClaim(PLUGIN_NAME, id);
127
142
  return;
128
143
  }
144
+ const useServerExportNames = getFunctionDirectiveExportNames(transforms, ast, "use server");
129
145
  const result = transforms.transformDirectiveProxyExport(ast, {
130
146
  code,
131
147
  directive: moduleDirective,
@@ -134,7 +150,7 @@ async function createUseCacheCallablePlugin(options) {
134
150
  validateModuleExport(transforms, meta);
135
151
  return true;
136
152
  },
137
- runtime: (name) => `$$ReactClient.createServerReference(${JSON.stringify(`${reference.referenceKey}#${name}`)},$$ReactClient.callServer,undefined,${this.environment.mode === "dev" ? "$$ReactClient.findSourceMapURL" : "undefined"},${JSON.stringify(name)})`
153
+ runtime: (name) => `$$ReactClient.createServerReference(${JSON.stringify(`${reference.referenceKey}#${useServerExportNames.has(name) ? name : secureExportName(name)}`)},$$ReactClient.callServer,undefined,${this.environment.mode === "dev" ? "$$ReactClient.findSourceMapURL" : "undefined"},${JSON.stringify(name)})`
138
154
  });
139
155
  if (!result?.output.hasChanged()) {
140
156
  manager.serverReferences.deleteClaim(PLUGIN_NAME, id);
@@ -142,7 +158,7 @@ async function createUseCacheCallablePlugin(options) {
142
158
  }
143
159
  manager.serverReferences.replaceClaim(PLUGIN_NAME, id, {
144
160
  ...reference,
145
- exportNames: result.exportNames
161
+ exportNames: result.exportNames.map((name) => useServerExportNames.has(name) ? name : secureExportName(name))
146
162
  });
147
163
  const runtimeEnvironment = this.environment.name === "client" ? "browser" : "ssr";
148
164
  result.output.prepend(`import * as $$ReactClient from "@vitejs/plugin-rsc/react/${runtimeEnvironment}";\n`);
@@ -151,19 +167,23 @@ async function createUseCacheCallablePlugin(options) {
151
167
  source: id
152
168
  });
153
169
  }
170
+ const secureExports = /* @__PURE__ */ new Set();
154
171
  const wrap = (value, name, directiveMatch, meta, isModuleDirective) => {
155
172
  const variant = directiveMatch[1] ?? "";
173
+ const secureName = secureExportName(name);
174
+ secureExports.add(secureName);
156
175
  const wrapperOptions = {
157
176
  ...getCacheWrapperOptions(options, id, name, isModuleDirective, meta),
158
- serverReferenceId: `${reference.referenceKey}#${name}`
177
+ serverReferenceId: `${reference.referenceKey}#${secureName}`
159
178
  };
160
179
  return `$$cacheRuntime.registerCachedFunction(${value}, ${JSON.stringify(`${id}:${name}`)}, ${JSON.stringify(variant)}, ${JSON.stringify(wrapperOptions)})`;
161
180
  };
162
181
  let needsReactServer = false;
163
182
  const runtime = (value, name, directiveMatch, meta, isModuleDirective) => {
164
183
  const cached = wrap(value, name, directiveMatch, meta, isModuleDirective);
184
+ const secureName = secureExportName(name);
165
185
  needsReactServer = true;
166
- return `$$VinextReactServer.registerServerReference(${cached}, ${JSON.stringify(reference.referenceKey)}, ${JSON.stringify(name)})`;
186
+ return `(${secureName} = $$VinextReactServer.registerServerReference(${cached}, ${JSON.stringify(reference.referenceKey)}, ${JSON.stringify(secureName)}))`;
167
187
  };
168
188
  const result = moduleDirective ? transforms.transformWrapExport(code, ast, {
169
189
  filter: (name, meta) => {
@@ -176,6 +196,7 @@ async function createUseCacheCallablePlugin(options) {
176
196
  directive: USE_CACHE_DIRECTIVE_CANDIDATE,
177
197
  rejectNonAsyncFunction: true,
178
198
  hoistRuntime: true,
199
+ noExport: true,
179
200
  runtime: (value, name, meta) => runtime(value, name, matchUseCacheDirective(meta.directiveMatch[0]), meta, false),
180
201
  encode: (value) => `$$cacheRuntime.encryptCacheCaptures(${value})`,
181
202
  decode: (value) => value
@@ -186,10 +207,15 @@ async function createUseCacheCallablePlugin(options) {
186
207
  }
187
208
  manager.serverReferences.replaceClaim(PLUGIN_NAME, id, {
188
209
  ...reference,
189
- exportNames: "names" in result ? result.names : result.exportNames
210
+ exportNames: [...secureExports]
190
211
  });
191
212
  const importPosition = ast.body.find((node) => !("directive" in node))?.start ?? code.length;
192
- result.output.prependLeft(importPosition, [`import * as $$cacheRuntime from ${JSON.stringify(options.cacheRuntime)};`, needsReactServer && `import * as $$VinextReactServer from "@vitejs/plugin-rsc/react/rsc/server";`].filter(Boolean).join("\n") + "\n");
213
+ result.output.prependLeft(importPosition, [
214
+ `import * as $$cacheRuntime from ${JSON.stringify(options.cacheRuntime)};`,
215
+ needsReactServer && `import * as $$VinextReactServer from "@vitejs/plugin-rsc/react/rsc/server";`,
216
+ secureExports.size > 0 && `let ${[...secureExports].join(",")};`
217
+ ].filter(Boolean).join("\n") + "\n");
218
+ if (secureExports.size > 0) result.output.append(`\nexport { ${[...secureExports].join(",")} };\n`);
193
219
  return magicStringTransformResult(result.output, {
194
220
  hires: "boundary",
195
221
  source: id
@@ -848,6 +848,7 @@ function registerServerActionCallback() {
848
848
  navigationPlanner,
849
849
  performHardNavigation: (url, historyMode) => browserNavigationController.performHardNavigation(url, historyMode),
850
850
  renderRedirectPayload(elements, target, actionInitiation, revalidation) {
851
+ if (browserNavigationController.isCurrentNavigation(actionInitiation.navigationId)) getNavigationRuntime()?.functions.notifyLinkNavigationStart?.();
851
852
  const hashIdx = target.href.indexOf("#");
852
853
  const hash = hashIdx !== -1 ? target.href.slice(hashIdx) : "";
853
854
  const actionScrollIntent = beginAppRouterScrollIntent(hash || null);
@@ -24,6 +24,7 @@ type FinalizeAppPageCacheabilityEvaluationOptions = {
24
24
  revalidateSeconds: number | null;
25
25
  };
26
26
  type FinalizeAppPageHtmlCacheResponseOptions = {
27
+ bypassInterceptionContextCache?: boolean;
27
28
  capturedDynamicUsageBeforeContextCleanup?: () => boolean;
28
29
  capturedRscDataPromise: Promise<ArrayBuffer> | null;
29
30
  cleanPathname: string;
@@ -19,7 +19,7 @@ function applyPendingDynamicCdnHeaders(headers, tags, options = {}) {
19
19
  });
20
20
  finalizePendingCacheStateHeaders(headers, options);
21
21
  }
22
- function applyUncacheableRscVariantNoStoreHeaders(headers, options = {}) {
22
+ function applyUncacheableVariantNoStoreHeaders(headers, options = {}) {
23
23
  applyCdnResponseHeaders(headers, { cacheControl: NO_STORE_CACHE_CONTROL });
24
24
  finalizePendingCacheStateHeaders(headers, {
25
25
  ...options,
@@ -114,6 +114,18 @@ function finalizeAppPageHtmlCacheResponse(response, options) {
114
114
  }
115
115
  return probeResponse;
116
116
  }
117
+ if (options.bypassInterceptionContextCache === true) {
118
+ options.capturedRscDataPromise?.catch(() => {});
119
+ const headers = new Headers(response.headers);
120
+ applyUncacheableVariantNoStoreHeaders(headers, { omitCacheState: options.omitPendingDynamicCacheState === true });
121
+ const clientResponse = new Response(response.body, {
122
+ status: response.status,
123
+ statusText: response.statusText,
124
+ headers
125
+ });
126
+ markFrameworkLinkHeaders(clientResponse.headers, options.linkHeader);
127
+ return clientResponse;
128
+ }
117
129
  if (!response.body) return response;
118
130
  const [streamForClient, streamForCache] = response.body.tee();
119
131
  const htmlKey = options.isrHtmlKey(options.cleanPathname);
@@ -184,7 +196,7 @@ function finalizeAppPageRscCacheResponse(response, options) {
184
196
  const isUncacheableVariant = Boolean(options.mountedSlotsHeader) || options.bypassInterceptionContextCache === true;
185
197
  if (options.preserveClientResponseHeaders === true && !isUncacheableVariant) return response;
186
198
  const clientHeaders = new Headers(response.headers);
187
- if (isUncacheableVariant) applyUncacheableRscVariantNoStoreHeaders(clientHeaders, { omitCacheState: options.omitPendingDynamicCacheState === true });
199
+ if (isUncacheableVariant) applyUncacheableVariantNoStoreHeaders(clientHeaders, { omitCacheState: options.omitPendingDynamicCacheState === true });
188
200
  else applyPendingDynamicCdnHeaders(clientHeaders, options.getPageTags(), { omitCacheState: options.omitPendingDynamicCacheState === true });
189
201
  return new Response(response.body, {
190
202
  status: response.status,
@@ -705,6 +705,7 @@ async function renderAppPageLifecycleImpl(options, renderSpan) {
705
705
  });
706
706
  if (options.isPrerender === true) return isrResponse;
707
707
  return finalizeAppPageHtmlCacheResponse(isrResponse, {
708
+ bypassInterceptionContextCache: options.bypassInterceptionContextCache,
708
709
  capturedDynamicUsageBeforeContextCleanup() {
709
710
  return dynamicUsedBeforeContextCleanup;
710
711
  },
@@ -35,7 +35,9 @@ export type ValidateAppPageDynamicParamsOptions = {
35
35
  enforceStaticParamsOnly: boolean;
36
36
  generateStaticParams?: GenerateStaticParams | GenerateStaticParamsSource | readonly (GenerateStaticParams | GenerateStaticParamsSource | null | undefined)[] | null;
37
37
  isDynamicRoute: boolean;
38
+ optionalCatchAllParamNames?: readonly string[];
38
39
  params: AppPageParams;
40
+ requiredParamNames?: readonly string[];
39
41
  };
40
42
  type ResolveAppPageGenerateStaticParamsSourcesOptions = {
41
43
  layouts?: readonly (GenerateStaticParamsModule | null | undefined)[];
@@ -110,18 +110,21 @@ function resolveAppPageGenerateStaticParamsSources(options) {
110
110
  }
111
111
  return sources;
112
112
  }
113
- function areStaticParamsAllowed(params, staticParams, allowMissingValues = false) {
114
- const paramKeys = Object.keys(params);
113
+ function areStaticParamsAllowed(params, staticParams, allowMissingValues = false, paramKeys = Object.keys(params), optionalCatchAllParamNames = []) {
115
114
  const stringParamMatches = (value, staticValue) => value === encodeURIComponent(staticValue);
116
- return staticParams.some((staticParamSet) => paramKeys.every((key) => {
117
- const value = params[key];
118
- const staticValue = staticParamSet[key];
119
- if (!Object.hasOwn(staticParamSet, key)) return allowMissingValues;
120
- if (Array.isArray(value)) return Array.isArray(staticValue) && value.length === staticValue.length && value.every((part, index) => typeof staticValue[index] === "string" ? stringParamMatches(part, staticValue[index]) : part === staticValue[index]);
121
- if (typeof staticValue === "string") return stringParamMatches(value, staticValue);
122
- if (typeof staticValue === "number" || typeof staticValue === "boolean") return String(value) === String(staticValue);
123
- return JSON.stringify(value) === JSON.stringify(staticValue);
124
- }));
115
+ return staticParams.some((staticParamSet) => {
116
+ if (!staticParamSet || typeof staticParamSet !== "object" || Array.isArray(staticParamSet)) return false;
117
+ return paramKeys.every((key) => {
118
+ const value = params[key];
119
+ const staticValue = staticParamSet[key];
120
+ if (!Object.hasOwn(staticParamSet, key)) return allowMissingValues;
121
+ if (value === void 0) return optionalCatchAllParamNames.includes(key) && (staticValue === void 0 || staticValue === null || staticValue === false || Array.isArray(staticValue) && staticValue.length === 0);
122
+ if (Array.isArray(value)) return Array.isArray(staticValue) && value.length === staticValue.length && value.every((part, index) => typeof staticValue[index] === "string" ? stringParamMatches(part, staticValue[index]) : part === staticValue[index]);
123
+ if (typeof staticValue === "string") return stringParamMatches(value, staticValue);
124
+ if (typeof staticValue === "number" || typeof staticValue === "boolean") return String(value) === String(staticValue);
125
+ return JSON.stringify(value) === JSON.stringify(staticValue);
126
+ });
127
+ });
125
128
  }
126
129
  function remapStaticParamsToRouteParams(staticParams, source) {
127
130
  if (!source.paramAliases) return [...staticParams];
@@ -227,9 +230,10 @@ async function validateAppPageDynamicParams(options) {
227
230
  const result = await generateIndependentStaticParams(sources, chainedStaticParams, options.params);
228
231
  if (result.validated) {
229
232
  validatedIndependentResults = true;
230
- if (!areStaticParamsAllowed(options.params, result.staticParams, true)) return notFoundResponse();
233
+ if (!areStaticParamsAllowed(options.params, result.staticParams, options.requiredParamNames === void 0, options.requiredParamNames, options.optionalCatchAllParamNames)) return notFoundResponse();
231
234
  }
232
235
  }
236
+ if (options.requiredParamNames && !validatedIndependentResults) return notFoundResponse();
233
237
  if (chainedStaticParams && !validatedIndependentResults) {
234
238
  if (!areStaticParamsAllowed(options.params, chainedStaticParams)) return notFoundResponse();
235
239
  }
@@ -40,7 +40,7 @@ async function dispatchAppRequestStage(request, ctx, dispatchResponseStage, opti
40
40
  draftModeCookie: null,
41
41
  middlewareCookieOverlay: null,
42
42
  prerenderDiscovery: options.prerenderDiscovery,
43
- protocolVersion: 8,
43
+ protocolVersion: 10,
44
44
  requestOrigin: new URL(request.url).origin,
45
45
  scriptNonce: null,
46
46
  staticFileSignalToken,
@@ -18,6 +18,7 @@ type RouteHandlerBackgroundRegenerationErrorContext = {
18
18
  type RouteHandlerBackgroundRegenerator = (key: string, renderFn: () => Promise<void>, errorContext?: RouteHandlerBackgroundRegenerationErrorContext) => void;
19
19
  type DispatchAppRouteHandlerOptions = {
20
20
  basePath?: string;
21
+ bypassSharedCache?: boolean;
21
22
  cleanPathname: string;
22
23
  clearRequestContext: () => void;
23
24
  draftModeSecret: string;
@@ -91,7 +91,7 @@ async function dispatchAppRouteHandlerImpl(options) {
91
91
  headers: { Allow: allowHeaderForOptions }
92
92
  }));
93
93
  const resolvedHandlerFn = isAppRouteHandlerFunction(handlerFn) ? handlerFn : void 0;
94
- const shouldReadRouteCache = revalidateSeconds !== null && !getRouteCacheabilityDynamicReason() && shouldReadAppRouteHandlerCache({
94
+ const shouldReadRouteCache = options.bypassSharedCache !== true && revalidateSeconds !== null && !getRouteCacheabilityDynamicReason() && shouldReadAppRouteHandlerCache({
95
95
  dynamicConfig: handler.dynamic,
96
96
  handlerFn: resolvedHandlerFn,
97
97
  isAutoHead,
@@ -160,6 +160,7 @@ async function dispatchAppRouteHandlerImpl(options) {
160
160
  if (resolvedHandlerFn) {
161
161
  const response = await executeAppRouteHandler({
162
162
  basePath: options.basePath,
163
+ bypassSharedCache: options.bypassSharedCache,
163
164
  buildPageCacheTags(pathname, extraTags) {
164
165
  return buildRouteHandlerPageCacheTags(pathname, extraTags, route.routeSegments);
165
166
  },
@@ -65,6 +65,7 @@ type RunAppRouteHandlerResult = {
65
65
  export declare function applyDraftModeCachePolicy(response: Response, isDraftMode: boolean): Response;
66
66
  type ExecuteAppRouteHandlerOptions = {
67
67
  buildPageCacheTags: (pathname: string, extraTags: string[]) => string[];
68
+ bypassSharedCache?: boolean;
68
69
  clearRequestContext: () => void;
69
70
  cleanPathname: string;
70
71
  executionContext: ExecutionContextLike | null;
@@ -214,7 +214,7 @@ async function executeAppRouteHandlerImpl(options) {
214
214
  dynamicUsedInHandler = handlerResult.didAccessDynamicRequest() || dynamicUsedDuringCompletion || dynamicUsedInHandler;
215
215
  }
216
216
  const requestCacheabilityVeto = getRouteCacheabilityDynamicReason();
217
- const responseMustStayPrivate = Boolean(options.handler.dynamic === "force-dynamic" || dynamicUsedInHandler || requestCacheabilityVeto || cleanupDeferredToBody);
217
+ const responseMustStayPrivate = Boolean(options.bypassSharedCache === true || options.handler.dynamic === "force-dynamic" || dynamicUsedInHandler || requestCacheabilityVeto || cleanupDeferredToBody);
218
218
  if (dynamicUsedInHandler) markKnownDynamicAppRoute(options.routePattern);
219
219
  const pendingCookies = options.getAndClearPendingCookies();
220
220
  const handlerDraftCookie = options.getDraftModeCookieHeader() ?? handlerDraftCookieBeforeCompletion;
@@ -269,7 +269,7 @@ async function executeAppRouteHandlerImpl(options) {
269
269
  isHead: options.isAutoHead
270
270
  }), options.middlewareContext, middlewareMergeOptions), shouldApplyDraftPolicy);
271
271
  const preserveHandlerPolicy = isRouteCacheabilityEvaluation() ? hasExplicitCacheablePolicy : handlerSetCachePolicy;
272
- if (responseMustStayPrivate && !preserveHandlerPolicy) {
272
+ if (options.bypassSharedCache === true || responseMustStayPrivate && !preserveHandlerPolicy) {
273
273
  const headers = new Headers(finalized.headers);
274
274
  applyCdnResponseHeaders(headers, { cacheControl: NEVER_CACHE_CONTROL });
275
275
  finalized = new Response(finalized.body, {
@@ -50,6 +50,7 @@ type AppRscRouteMatch<TRoute> = {
50
50
  };
51
51
  type DispatchMatchedPageOptions<TRoute> = {
52
52
  bypassInterceptionContextCache: boolean;
53
+ cachePathname: string;
53
54
  clientReuseManifest: ClientReuseManifestParseResult;
54
55
  cleanPathname: string;
55
56
  displayPathname: string;
@@ -86,6 +87,12 @@ type DispatchMatchedPageOptions<TRoute> = {
86
87
  renderMode: AppRscRenderMode;
87
88
  };
88
89
  type DispatchMatchedRouteHandlerOptions<TRoute> = {
90
+ /**
91
+ * Legacy transported cache-bypass bit. Also covers request/cache route-identity
92
+ * divergence so the response cannot be published under another route's key.
93
+ */
94
+ bypassInterceptionContextCache: boolean;
95
+ cachePathname: string;
89
96
  cleanPathname: string;
90
97
  middlewareContext: AppRscMiddlewareContext;
91
98
  /**
@@ -207,7 +214,7 @@ export type CreateAppRscHandlerOptions<TRoute extends AppRscHandlerRoute> = {
207
214
  /** Request-only entries dynamically load the response graph for local bypasses. */
208
215
  renderResponseStageLocally?: RenderAppWorkerResponseStageLocally;
209
216
  handleProgressiveActionRequest?: (options: HandleProgressiveActionRequestOptions<TRoute>) => Promise<Response | ProgressiveActionFormStateResult | null>;
210
- handleMetadataRouteRequest?: (cleanPathname: string) => Promise<Response | null>;
217
+ handleMetadataRouteRequest?: (cleanPathname: string, routePathname: string) => Promise<Response | null>;
211
218
  isMetadataRoutePath?: (cleanPathname: string) => boolean | Promise<boolean>;
212
219
  getPrerenderMetadataRoutePaths?: () => Promise<unknown>;
213
220
  createPprFallbackShells?: (route: Pick<AppRscHandlerRoute, "params" | "pattern" | "rootParamNames">, params: AppPageParams) => AppPagePprFallbackCacheShell[];