vinext 1.4.1 → 1.4.2

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 (83) hide show
  1. package/README.md +1 -1
  2. package/dist/build/action-owner-manifest.js +1 -0
  3. package/dist/check.js +1 -1
  4. package/dist/config/config-matchers.js +2 -2
  5. package/dist/config/destination-params.js +5 -2
  6. package/dist/config/next-config.d.ts +16 -1
  7. package/dist/config/next-config.js +31 -1
  8. package/dist/entries/app-rsc-entry.js +41 -18
  9. package/dist/entries/app-rsc-manifest.js +4 -0
  10. package/dist/entries/pages-server-entry.js +2 -0
  11. package/dist/index.js +15 -12
  12. package/dist/init-cloudflare.js +18 -2
  13. package/dist/plugins/css-target.d.ts +18 -0
  14. package/dist/plugins/css-target.js +93 -0
  15. package/dist/routing/app-route-graph.d.ts +10 -0
  16. package/dist/routing/app-route-graph.js +15 -1
  17. package/dist/server/api-handler.d.ts +2 -0
  18. package/dist/server/api-handler.js +3 -5
  19. package/dist/server/app-action-forwarding.js +3 -1
  20. package/dist/server/app-browser-entry.js +21 -4
  21. package/dist/server/app-elements.d.ts +3 -0
  22. package/dist/server/app-elements.js +14 -1
  23. package/dist/server/app-fallback-renderer.js +2 -2
  24. package/dist/server/app-optimistic-routing.d.ts +12 -1
  25. package/dist/server/app-optimistic-routing.js +38 -16
  26. package/dist/server/app-page-boundary-render.js +2 -2
  27. package/dist/server/app-page-cache-render.js +1 -1
  28. package/dist/server/app-page-dispatch.d.ts +10 -1
  29. package/dist/server/app-page-dispatch.js +73 -30
  30. package/dist/server/app-page-element-builder.d.ts +9 -20
  31. package/dist/server/app-page-element-builder.js +37 -49
  32. package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
  33. package/dist/server/app-page-intercept-segment-config.js +90 -0
  34. package/dist/server/app-page-navigation-params.d.ts +23 -0
  35. package/dist/server/app-page-navigation-params.js +49 -0
  36. package/dist/server/app-page-render.d.ts +5 -2
  37. package/dist/server/app-page-render.js +17 -3
  38. package/dist/server/app-page-request.d.ts +24 -2
  39. package/dist/server/app-page-request.js +10 -10
  40. package/dist/server/app-page-route-wiring.d.ts +10 -0
  41. package/dist/server/app-page-route-wiring.js +16 -12
  42. package/dist/server/app-page-segment-plan.js +9 -1
  43. package/dist/server/app-page-segment-state.d.ts +9 -0
  44. package/dist/server/app-page-segment-state.js +54 -1
  45. package/dist/server/app-response-stage-entry.js +1 -0
  46. package/dist/server/app-route-module-loader.d.ts +2 -0
  47. package/dist/server/app-route-module-loader.js +1 -0
  48. package/dist/server/app-rsc-handler.d.ts +10 -1
  49. package/dist/server/app-rsc-handler.js +75 -43
  50. package/dist/server/app-rsc-response-finalizer.d.ts +3 -0
  51. package/dist/server/app-rsc-response-finalizer.js +11 -1
  52. package/dist/server/app-rsc-route-matching.d.ts +14 -0
  53. package/dist/server/app-rsc-route-matching.js +42 -18
  54. package/dist/server/app-server-action-execution.js +16 -10
  55. package/dist/server/cache-control.d.ts +2 -0
  56. package/dist/server/cache-control.js +3 -1
  57. package/dist/server/image-local-patterns.d.ts +21 -0
  58. package/dist/server/image-local-patterns.js +59 -0
  59. package/dist/server/image-optimization.d.ts +23 -22
  60. package/dist/server/image-optimization.js +12 -2
  61. package/dist/server/metadata-route-response.js +24 -6
  62. package/dist/server/nitro-public-files-plugin.js +20 -8
  63. package/dist/server/pages-data-route.d.ts +5 -1
  64. package/dist/server/pages-data-route.js +14 -6
  65. package/dist/server/pages-request-pipeline.js +6 -5
  66. package/dist/server/pages-request-stage-entry.js +1 -0
  67. package/dist/server/prod-server.js +17 -9
  68. package/dist/server/request-pipeline.d.ts +5 -0
  69. package/dist/server/request-pipeline.js +11 -1
  70. package/dist/server/response-stage-cacheability.d.ts +7 -0
  71. package/dist/server/response-stage-cacheability.js +6 -5
  72. package/dist/server/worker-image-optimization.d.ts +2 -2
  73. package/dist/server/worker-image-optimization.js +18 -3
  74. package/dist/server/worker-utils.d.ts +8 -0
  75. package/dist/server/worker-utils.js +19 -2
  76. package/dist/shims/image-props.d.ts +6 -0
  77. package/dist/shims/image-props.js +39 -3
  78. package/dist/shims/image.js +3 -3
  79. package/dist/shims/slot.d.ts +3 -0
  80. package/dist/shims/slot.js +67 -9
  81. package/dist/utils/base-path.d.ts +7 -0
  82. package/dist/utils/base-path.js +11 -1
  83. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import { MIDDLEWARE_SKIP_HEADER } from "../utils/protocol-headers.js";
2
- import { addBasePathToPathname, hasBasePath, stripBasePath } from "../utils/base-path.js";
2
+ import { hasBasePath, restoreBasePath, stripBasePath } from "../utils/base-path.js";
3
3
  import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
4
- import { cloneRequestWithUrl } from "./request-pipeline.js";
4
+ import { cloneRequestWithUrl, hasUrlParserDotSegment } from "./request-pipeline.js";
5
5
  //#region src/server/pages-data-route.ts
6
6
  /**
7
7
  * Helpers for the Pages Router `/_next/data/{buildId}/{...page}.json` endpoint.
@@ -62,13 +62,19 @@ function encodeUrlParserIgnoredCharacters(pathname) {
62
62
  * `/_next/data/<buildId>/en.json` → `/en`
63
63
  * `/_next/data/<wrong-id>/about.json` → null
64
64
  * `/_next/data/<buildId>/about` → null (missing .json suffix)
65
+ * `/_next/data/<buildId>/blog/...json` → null (strips to a dot segment)
66
+ *
67
+ * `decoded` marks a percent-decoded copy of a pathname whose raw form already
68
+ * parsed: a `%2e` left in it was `%252e` on the wire, an ordinary character.
65
69
  */
66
- function parseNextDataPathname(pathname, buildId) {
70
+ function parseNextDataPathname(pathname, buildId, decoded = false) {
67
71
  if (!buildId) return null;
68
72
  if (!isNextDataPathname(pathname)) return null;
69
73
  const expectedPrefix = `${NEXT_DATA_PREFIX}${buildId}/`;
70
74
  if (!pathname.startsWith(expectedPrefix)) return null;
71
- return pageFromNextDataRest(pathname.slice(expectedPrefix.length, -5));
75
+ const rest = pathname.slice(expectedPrefix.length, -5);
76
+ if (!decoded && hasUrlParserDotSegment(rest)) return null;
77
+ return pageFromNextDataRest(rest);
72
78
  }
73
79
  /**
74
80
  * The page pathname a `_next/data` URL addresses, whatever its buildId. Next.js
@@ -83,7 +89,9 @@ function nextDataMiddlewareMatchPathname(pathname) {
83
89
  if (!isNextDataPathname(pathname)) return null;
84
90
  const buildIdEnd = pathname.indexOf("/", 12);
85
91
  if (buildIdEnd <= 12) return null;
86
- return pageFromNextDataRest(pathname.slice(buildIdEnd + 1, -5))?.pagePathname ?? null;
92
+ const rest = pathname.slice(buildIdEnd + 1, -5);
93
+ if (hasUrlParserDotSegment(rest)) return null;
94
+ return pageFromNextDataRest(rest)?.pagePathname ?? null;
87
95
  }
88
96
  function pageFromNextDataRest(rest) {
89
97
  if (rest.length === 0) return null;
@@ -189,7 +197,7 @@ function normalizePagesDataRequest(request, buildId, basePath = "", trailingSlas
189
197
  };
190
198
  const pagePathname = normalizeNextDataPagePathname(dataMatch.pagePathname, trailingSlash);
191
199
  const normalizedUrl = new URL(reqUrl);
192
- normalizedUrl.pathname = hadBasePath ? addBasePathToPathname(pagePathname, basePath) : pagePathname;
200
+ normalizedUrl.pathname = hadBasePath ? restoreBasePath(pagePathname, basePath) : pagePathname;
193
201
  return {
194
202
  isDataReq: true,
195
203
  request: cloneRequestWithUrl(request, normalizedUrl.href),
@@ -1,7 +1,7 @@
1
1
  import { PRERENDER_REVALIDATE_HEADER } from "../utils/protocol-headers.js";
2
2
  import { requestContextFromRequest } from "../config/request-context.js";
3
3
  import { isExternalUrl } from "../utils/external-url.js";
4
- import { addBasePathToPathname } from "../utils/base-path.js";
4
+ import { restoreBasePath } from "../utils/base-path.js";
5
5
  import { VINEXT_PRERENDER_REWRITTEN_HEADER } from "./headers.js";
6
6
  import { methodNotAllowedResponse, notFoundStaticAssetResponse, sanitizeMethodNotAllowedHeaders } from "./http-error-responses.js";
7
7
  import { cloneRequestWithUrl, normalizeTrailingSlash } from "./request-pipeline.js";
@@ -14,10 +14,11 @@ import { buildMiddlewarePrefetchSkipResponse } from "./pages-data-route.js";
14
14
  import { markRouteCacheabilityDynamic } from "../shims/cacheability-classification.js";
15
15
  import { isOnDemandRevalidateRequest } from "./revalidation-request.js";
16
16
  import { mergeRewriteQuery } from "../utils/query.js";
17
+ import { isImageOptimizationPath } from "./image-optimization.js";
17
18
  import { setFrameworkRequestRoute } from "./request-tracing.js";
18
19
  import { withExternalRewriteResponseHeaders } from "./middleware-response-headers.js";
19
20
  import { applyConfigHeadersToHeaderRecord } from "./config-headers.js";
20
- import { finalizeMissingStaticAssetResponse, mergeHeaders } from "./worker-utils.js";
21
+ import { finalizeMissingStaticAssetResponse, mergeHeaders, mergeStaticFileHeaders } from "./worker-utils.js";
21
22
  //#region src/server/pages-request-pipeline.ts
22
23
  function ruleUsesUnkeyedRequestCondition(rule) {
23
24
  return [...rule.has ?? [], ...rule.missing ?? []].some((condition) => condition.type === "header" || condition.type === "cookie" || condition.type === "host");
@@ -80,7 +81,7 @@ function wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) {
80
81
  if (!hadBasePath || !basePath) return runMiddleware;
81
82
  return (request, ctx, opts) => {
82
83
  const mwUrl = new URL(request.url);
83
- mwUrl.pathname = addBasePathToPathname(mwUrl.pathname, basePath);
84
+ mwUrl.pathname = restoreBasePath(mwUrl.pathname, basePath);
84
85
  return runMiddleware(cloneRequestWithUrl(request, mwUrl.toString()), ctx, opts);
85
86
  };
86
87
  }
@@ -166,7 +167,7 @@ async function runPagesRequest(request, deps) {
166
167
  const served = await deps.serveFilesystemRoute(requestPathname, middlewareHeaders, phase, resolvedUrl, request, resolvedPathnameHasBasePath);
167
168
  if (served instanceof Response) {
168
169
  const isStaticMethodNotAllowed = served.status === 405 && served.headers.get("allow") === "GET, HEAD";
169
- const response = mergeHeaders(served, middlewareHeaders, isStaticMethodNotAllowed || !served.ok || served.status === 206 ? void 0 : middlewareStatus);
170
+ const response = (resolvedPathnameHasBasePath && isImageOptimizationPath(requestPathname) ? mergeHeaders : mergeStaticFileHeaders)(served, middlewareHeaders, isStaticMethodNotAllowed || !served.ok || served.status === 206 ? void 0 : middlewareStatus);
170
171
  if (isStaticMethodNotAllowed) sanitizeMethodNotAllowedHeaders(response.headers, "GET, HEAD");
171
172
  return {
172
173
  type: "response",
@@ -306,7 +307,7 @@ async function runPagesRequest(request, deps) {
306
307
  let apiRequest = request;
307
308
  if (basePath && hadBasePath) {
308
309
  const apiRequestUrl = new URL(request.url);
309
- apiRequestUrl.pathname = addBasePathToPathname(apiRequestUrl.pathname, basePath);
310
+ apiRequestUrl.pathname = restoreBasePath(apiRequestUrl.pathname, basePath);
310
311
  apiRequest = cloneRequestWithUrl(request, apiRequestUrl.toString());
311
312
  }
312
313
  const response = await deps.handleApi(apiRequest, apiLookupUrl, deps.ctx ?? null, headersFromRecord(middlewareHeaders));
@@ -69,6 +69,7 @@ const configRewrites = vinextConfig?.rewrites ?? {
69
69
  const configHeaders = vinextConfig?.headers ?? [];
70
70
  const imageConfig = vinextConfig?.images ? {
71
71
  qualities: vinextConfig.images.qualities,
72
+ localPatterns: vinextConfig.images.localPatterns,
72
73
  dangerouslyAllowSVG: vinextConfig.images.dangerouslyAllowSVG,
73
74
  dangerouslyAllowLocalIP: vinextConfig.images.dangerouslyAllowLocalIP,
74
75
  contentDispositionType: vinextConfig.images.contentDispositionType,
@@ -270,6 +270,7 @@ const OMIT_STATIC_RESPONSE_HEADERS = /* @__PURE__ */ new Set([
270
270
  "content-type",
271
271
  VINEXT_PRERENDER_SPECIAL_ERROR_HEADER
272
272
  ]);
273
+ const IMAGE_ERROR_SIGNAL_HEADERS = ["content-type", "content-encoding"];
273
274
  function omitHeadersCaseInsensitive(headersRecord, targets) {
274
275
  const filtered = {};
275
276
  for (const [key, value] of Object.entries(headersRecord)) {
@@ -1195,18 +1196,25 @@ async function startAppRouterServer(options) {
1195
1196
  if (seededRoutes > 0) console.log(`[vinext] Seeded ${seededRoutes} pre-rendered route${seededRoutes !== 1 ? "s" : ""} into memory cache`);
1196
1197
  const staticCache = await StaticFileCache.create(clientDir);
1197
1198
  const serveImageOptimization = async (req, res, request, signal) => {
1198
- const stagedHeaders = omitHeadersCaseInsensitive(mergeResponseHeaders({}, new Response(null, { headers: signal.middlewareHeaders })), OMIT_STATIC_RESPONSE_HEADERS);
1199
+ const signalHeaders = mergeResponseHeaders({}, new Response(null, { headers: signal.middlewareHeaders }));
1200
+ const stagedHeaders = omitHeadersCaseInsensitive(signalHeaders, OMIT_STATIC_RESPONSE_HEADERS);
1199
1201
  for (const [name, value] of Object.entries(stagedHeaders)) res.setHeader(name, value);
1202
+ const sendBadRequest = (body) => {
1203
+ for (const name of IMAGE_ERROR_SIGNAL_HEADERS) {
1204
+ const value = signalHeaders[name];
1205
+ if (value !== void 0) res.setHeader(name, value);
1206
+ }
1207
+ res.writeHead(400);
1208
+ res.end(body);
1209
+ };
1200
1210
  const parsedUrl = new URL(`/${signal.search}`, "http://localhost");
1201
- const params = parseImageParams(parsedUrl, appImageAllowedWidths, imageConfig?.qualities);
1211
+ const params = parseImageParams(parsedUrl, appImageAllowedWidths, imageConfig?.qualities, { localPatterns: imageConfig?.localPatterns });
1202
1212
  if (!params) {
1203
- res.writeHead(400);
1204
- res.end("Bad Request");
1213
+ sendBadRequest("Bad Request");
1205
1214
  return;
1206
1215
  }
1207
1216
  const sendUnsafeContentType = () => {
1208
- res.writeHead(400);
1209
- res.end("The requested resource is not an allowed image type");
1217
+ sendBadRequest("The requested resource is not an allowed image type");
1210
1218
  };
1211
1219
  const isSafeSourcePath = (sourcePath) => isSafeImageContentType(contentTypeForPath(sourcePath), imageConfig?.dangerouslyAllowSVG);
1212
1220
  const imageSecurityHeaders = {
@@ -1243,8 +1251,7 @@ async function startAppRouterServer(options) {
1243
1251
  if (await serveSource(sourcePath)) return;
1244
1252
  }
1245
1253
  }
1246
- res.writeHead(400);
1247
- res.end(INVALID_IMAGE_SOURCE_MESSAGE);
1254
+ sendBadRequest(INVALID_IMAGE_SOURCE_MESSAGE);
1248
1255
  };
1249
1256
  const clientBuildFiles = new Set(Array.isArray(rscModule.__clientBuildFiles) ? rscModule.__clientBuildFiles : []);
1250
1257
  const handleRequestImpl = async (req, res) => {
@@ -1455,6 +1462,7 @@ async function createPagesRouterRequestHandler(options) {
1455
1462
  dangerouslyAllowSVG: vinextConfig.images.dangerouslyAllowSVG,
1456
1463
  dangerouslyAllowLocalIP: vinextConfig.images.dangerouslyAllowLocalIP,
1457
1464
  qualities: vinextConfig.images.qualities,
1465
+ localPatterns: vinextConfig.images.localPatterns,
1458
1466
  contentDispositionType: vinextConfig.images.contentDispositionType,
1459
1467
  contentSecurityPolicy: vinextConfig.images.contentSecurityPolicy
1460
1468
  } : void 0;
@@ -1689,7 +1697,7 @@ async function createPagesRouterRequestHandler(options) {
1689
1697
  if (resolvedPathnameHasBasePath && isImageOptimizationPath(requestPathname)) {
1690
1698
  for (const [name, value] of Object.entries(stagedHeaders)) res.setHeader(name, value);
1691
1699
  const parsedUrl = new URL(resolvedUrl, "http://localhost");
1692
- const params = parseImageParams(parsedUrl, allowedImageWidths, pagesImageConfig?.qualities);
1700
+ const params = parseImageParams(parsedUrl, allowedImageWidths, pagesImageConfig?.qualities, { localPatterns: pagesImageConfig?.localPatterns });
1693
1701
  if (!params) {
1694
1702
  res.writeHead(400);
1695
1703
  res.end("Bad Request");
@@ -16,6 +16,11 @@ import { getRepeatedSlashRedirect, isOpenRedirectShaped, sendRepeatedSlashRedire
16
16
  export declare function canonicalizeRequestPathname(pathname: string): string;
17
17
  /** Canonicalize only the pathname portion while preserving the raw query. */
18
18
  export declare function canonicalizeRequestUrlPathname(url: string): string;
19
+ /**
20
+ * Whether the pathname has a segment the URL parser resolves as `.` or `..`,
21
+ * literally or percent-encoded. Such a pathname changes when it becomes a URL.
22
+ */
23
+ export declare function hasUrlParserDotSegment(pathname: string): boolean;
19
24
  /**
20
25
  * Shared request pipeline utilities.
21
26
  *
@@ -30,6 +30,16 @@ function canonicalizeRequestUrlPathname(url) {
30
30
  return canonicalizeRequestPathname(pathname) + search;
31
31
  }
32
32
  /**
33
+ * Whether the pathname has a segment the URL parser resolves as `.` or `..`,
34
+ * literally or percent-encoded. Such a pathname changes when it becomes a URL.
35
+ */
36
+ function hasUrlParserDotSegment(pathname) {
37
+ return pathname.split("/").some((segment) => {
38
+ const decodedDots = segment.replaceAll(/%2e/gi, ".");
39
+ return decodedDots === "." || decodedDots === "..";
40
+ });
41
+ }
42
+ /**
33
43
  * Shared request pipeline utilities.
34
44
  *
35
45
  * Extracted from generated entries and server hot paths to keep codegen focused
@@ -472,4 +482,4 @@ function stripRequestUrlFragment(request) {
472
482
  return attachRequestCfMetadata(new Request(stripped, { signal }), request);
473
483
  }
474
484
  //#endregion
475
- export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, attachRequestCfMetadata, canonicalizeRequestPathname, canonicalizeRequestUrlPathname, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, dropEmptyMiddlewareSetCookie, filterInternalHeaders, getRepeatedSlashRedirect, guardProtocolRelativeUrl, hasBasePath, haveSameHeaders, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, sendRepeatedSlashRedirect, stripBasePath, stripRequestUrlFragment, validateCsrfOrigin, validateServerActionPayload, withRequestHeaders };
485
+ export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, attachRequestCfMetadata, canonicalizeRequestPathname, canonicalizeRequestUrlPathname, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, dropEmptyMiddlewareSetCookie, filterInternalHeaders, getRepeatedSlashRedirect, guardProtocolRelativeUrl, hasBasePath, hasUrlParserDotSegment, haveSameHeaders, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, sendRepeatedSlashRedirect, stripBasePath, stripRequestUrlFragment, validateCsrfOrigin, validateServerActionPayload, withRequestHeaders };
@@ -23,6 +23,13 @@ export type ResponseStageCacheabilityOptions = {
23
23
  * shared response drops them before admission and storage.
24
24
  */
25
25
  recomposesRequestScopedHeaders?: boolean;
26
+ /**
27
+ * Keeps `X-Vinext-Params` when the request scoped headers are dropped. An
28
+ * intercepted response's params include its target's, which the request
29
+ * stage doesn't match. Its identity holds the interception context, so the
30
+ * stored params are its own.
31
+ */
32
+ keepsParamsHeader?: boolean;
26
33
  /** Generated adapter registration, deferred until the response stage executes. */
27
34
  registerCacheAdapters(): void;
28
35
  request: Request;
@@ -24,13 +24,13 @@ async function withResponseStageCacheability(options, render) {
24
24
  const stripsRequestScopedHeaders = options.recomposesRequestScopedHeaders === true && options.cache === "shared" && !options.probeMode;
25
25
  if (!cacheability) {
26
26
  const rendered = await render(context);
27
- return stripsRequestScopedHeaders ? withoutRequestScopedHeaders(rendered) : rendered;
27
+ return stripsRequestScopedHeaders ? withoutRequestScopedHeaders(rendered, options.keepsParamsHeader === true) : rendered;
28
28
  }
29
29
  if (options.policyHeadersAppliedBeforeRender) cacheability.recordResponseStageCachePolicy(context, options.policyHeaders);
30
30
  const state = Reflect.get(context, CACHEABILITY_REQUEST_STATE);
31
31
  if (options.probeMode && options.forceDynamic && !options.policyHeaders?.length && state) state.patternDynamicReason = "dynamic = \"force-dynamic\"";
32
32
  const renderedResponse = await render(context);
33
- const rendered = stripsRequestScopedHeaders ? withoutRequestScopedHeaders(renderedResponse) : renderedResponse;
33
+ const rendered = stripsRequestScopedHeaders ? withoutRequestScopedHeaders(renderedResponse, options.keepsParamsHeader === true) : renderedResponse;
34
34
  const response = options.policyHeadersAppliedBeforeRender ? rendered : cacheability.applyResponseStageCachePolicy(rendered, context, options.policyHeaders);
35
35
  const complete = (candidate) => cacheability.finalizeWorkerCacheabilityResponse(candidate, context);
36
36
  const route = state?.route;
@@ -40,10 +40,11 @@ async function withResponseStageCacheability(options, render) {
40
40
  }
41
41
  return complete(response);
42
42
  }
43
- function withoutRequestScopedHeaders(response) {
44
- if (!response.headers.has("X-Vinext-Params") && !response.headers.has("X-Vinext-Rendered-Path-And-Search")) return response;
43
+ function withoutRequestScopedHeaders(response, keepsParams) {
44
+ const dropsParams = !keepsParams && response.headers.has("X-Vinext-Params");
45
+ if (!dropsParams && !response.headers.has("X-Vinext-Rendered-Path-And-Search")) return response;
45
46
  const headers = new Headers(response.headers);
46
- headers.delete(VINEXT_PARAMS_HEADER);
47
+ if (dropsParams) headers.delete(VINEXT_PARAMS_HEADER);
47
48
  headers.delete(VINEXT_RENDERED_PATH_AND_SEARCH_HEADER);
48
49
  return preserveFullyBufferedBodyMetadata(response, new Response(response.body, {
49
50
  headers,
@@ -27,8 +27,8 @@ export declare function fetchWorkerImageSource(imageUrl: string, request: Reques
27
27
  export declare function fetchSignalledPublicFile(response: Response, sourceRequest: Request, assets: VinextAssetFetcher): Promise<Response> | null;
28
28
  /**
29
29
  * Serve the App Router image endpoint once the request handler admitted it
30
- * through middleware and rewrites. Middleware's response headers are sent with
31
- * the image, as Next.js does.
30
+ * through middleware and rewrites. Config and middleware response headers are
31
+ * sent with the image, as Next.js does.
32
32
  */
33
33
  export declare function serveWorkerImageOptimization(request: Request, signal: ImageOptimizationSignal, allowedWidths: number[] | undefined, imageConfig: ImageConfig | undefined, options: WorkerImageSourceOptions): Promise<Response>;
34
34
  //#endregion
@@ -78,8 +78,8 @@ function fetchSignalledPublicFile(response, sourceRequest, assets) {
78
78
  }
79
79
  /**
80
80
  * Serve the App Router image endpoint once the request handler admitted it
81
- * through middleware and rewrites. Middleware's response headers are sent with
82
- * the image, as Next.js does.
81
+ * through middleware and rewrites. Config and middleware response headers are
82
+ * sent with the image, as Next.js does.
83
83
  */
84
84
  async function serveWorkerImageOptimization(request, signal, allowedWidths, imageConfig, options) {
85
85
  const imageUrl = new URL(request.url);
@@ -93,7 +93,22 @@ async function serveWorkerImageOptimization(request, signal, allowedWidths, imag
93
93
  });
94
94
  const cookies = signal.middlewareHeaders.getSetCookie();
95
95
  if (cookies.length > 0) middlewareHeaders["set-cookie"] = cookies;
96
- return mergeHeaders(response, middlewareHeaders);
96
+ const merged = mergeHeaders(response, middlewareHeaders);
97
+ if (response.status < 400) return merged;
98
+ const errorHeaders = new Headers(merged.headers);
99
+ let replaced = false;
100
+ for (const name of ["content-type", "content-encoding"]) {
101
+ const value = signal.middlewareHeaders.get(name);
102
+ if (value === null) continue;
103
+ errorHeaders.set(name, value);
104
+ replaced = true;
105
+ }
106
+ if (!replaced) return merged;
107
+ return new Response(merged.body, {
108
+ headers: errorHeaders,
109
+ status: merged.status,
110
+ statusText: merged.statusText
111
+ });
97
112
  }
98
113
  //#endregion
99
114
  export { fetchSignalledPublicFile, fetchWorkerImageSource, serveWorkerImageOptimization };
@@ -1,6 +1,14 @@
1
1
  //#region src/server/worker-utils.d.ts
2
2
  export declare function finalizeMissingStaticAssetResponse(response: Response, missingBuildAsset: boolean): Response;
3
3
  export declare function mergeHeaders(response: Response, extraHeaders: Record<string, string | string[]>, statusOverride?: number): Response;
4
+ /**
5
+ * Merge middleware/config headers into a file served from the asset layer.
6
+ * Its Cache-Control is only the layer's default (Workers Static Assets sends
7
+ * `public, max-age=0, must-revalidate` with every file), so one set by
8
+ * middleware or `headers()` wins, as Next.js's static file server sets its
9
+ * own only when the response has none.
10
+ */
11
+ export declare function mergeStaticFileHeaders(response: Response, extraHeaders: Record<string, string | string[]>, statusOverride?: number): Response;
4
12
  export declare function resolveStaticAssetSignal(signalResponse: Response, options: {
5
13
  /** `requestHeaders` are the post-middleware request headers, when middleware set any. */
6
14
  fetchAsset(path: string, requestHeaders: Headers | null): Promise<Response>;
@@ -82,6 +82,23 @@ function mergeHeaders(response, extraHeaders, statusOverride) {
82
82
  headers: merged
83
83
  });
84
84
  }
85
+ /**
86
+ * Merge middleware/config headers into a file served from the asset layer.
87
+ * Its Cache-Control is only the layer's default (Workers Static Assets sends
88
+ * `public, max-age=0, must-revalidate` with every file), so one set by
89
+ * middleware or `headers()` wins, as Next.js's static file server sets its
90
+ * own only when the response has none.
91
+ */
92
+ function mergeStaticFileHeaders(response, extraHeaders, statusOverride) {
93
+ const merged = mergeHeaders(response, extraHeaders, statusOverride);
94
+ for (const [name, value] of Object.entries(extraHeaders)) {
95
+ if (name.toLowerCase() !== "cache-control") continue;
96
+ const cacheControl = (Array.isArray(value) ? value.join(", ") : value).trim();
97
+ if (!cacheControl) continue;
98
+ merged.headers.set(name, cacheControl);
99
+ }
100
+ return merged;
101
+ }
85
102
  async function resolveStaticAssetSignal(signalResponse, options) {
86
103
  const signal = readStaticFileSignal(signalResponse);
87
104
  if (!signal) return null;
@@ -100,7 +117,7 @@ async function resolveStaticAssetSignal(signalResponse, options) {
100
117
  const requestHeaders = readStaticFileSignalRequestHeaders(signalResponse);
101
118
  cancelResponseBody(signalResponse);
102
119
  const assetResponse = await options.fetchAsset(assetPath, requestHeaders);
103
- return mergeHeaders(assetResponse, extraHeaders, assetResponse.ok && assetResponse.status !== 206 && signalResponse.status !== 200 ? signalResponse.status : void 0);
120
+ return mergeStaticFileHeaders(assetResponse, extraHeaders, assetResponse.ok && assetResponse.status !== 206 && signalResponse.status !== 200 ? signalResponse.status : void 0);
104
121
  }
105
122
  /**
106
123
  * Retarget a Worker asset request without dropping its conditional/range
@@ -116,4 +133,4 @@ function createStaticAssetRequest(assetPath, sourceRequest, headers = null) {
116
133
  });
117
134
  }
118
135
  //#endregion
119
- export { createStaticAssetRequest, finalizeMissingStaticAssetResponse, mergeHeaders, resolveStaticAssetSignal };
136
+ export { createStaticAssetRequest, finalizeMissingStaticAssetResponse, mergeHeaders, mergeStaticFileHeaders, resolveStaticAssetSignal };
@@ -71,6 +71,12 @@ export declare function resolveImageSource(v: {
71
71
  * Configurable via `images.deviceSizes` in next.config.js.
72
72
  */
73
73
  export declare const RESPONSIVE_WIDTHS: number[];
74
+ /**
75
+ * Parse a `quality` prop like Next.js's `getInt` (shared/lib/get-img-props.ts):
76
+ * finite numbers and digit-only strings pass, anything else becomes NaN, which
77
+ * the default loader treats as 75.
78
+ */
79
+ export declare function parseImageQuality(quality: unknown): number | undefined;
74
80
  /**
75
81
  * Build a `/_next/image` optimization URL.
76
82
  *
@@ -52,6 +52,14 @@ const __imageSizes = (() => {
52
52
  ];
53
53
  }
54
54
  })();
55
+ const __imageQualities = (() => {
56
+ try {
57
+ const qualities = JSON.parse(process.env.__VINEXT_IMAGE_QUALITIES ?? "null");
58
+ return Array.isArray(qualities) ? qualities.sort((a, b) => a - b) : null;
59
+ } catch {
60
+ return null;
61
+ }
62
+ })();
55
63
  /**
56
64
  * Whether dangerouslyAllowSVG is enabled in next.config.js.
57
65
  * When false (default), .svg sources auto-skip the optimization endpoint
@@ -195,6 +203,33 @@ function extractLocalDeploymentId(src) {
195
203
  }
196
204
  const __imageEndpoint = `${process.env.__NEXT_ROUTER_BASEPATH ?? ""}/_next/image${process.env.__VINEXT_TRAILING_SLASH === "true" ? "/" : ""}`;
197
205
  /**
206
+ * Parse a `quality` prop like Next.js's `getInt` (shared/lib/get-img-props.ts):
207
+ * finite numbers and digit-only strings pass, anything else becomes NaN, which
208
+ * the default loader treats as 75.
209
+ */
210
+ function parseImageQuality(quality) {
211
+ if (quality === void 0) return void 0;
212
+ if (typeof quality === "number") return Number.isFinite(quality) ? quality : NaN;
213
+ if (typeof quality === "string" && /^[0-9]+$/.test(quality)) return parseInt(quality, 10);
214
+ return NaN;
215
+ }
216
+ /**
217
+ * Round a `quality` prop to the closest entry of `images.qualities`, as the
218
+ * Next.js default loader does (shared/lib/find-closest-quality.ts), so the
219
+ * URL is one the optimization endpoint accepts.
220
+ *
221
+ * With no list configured the prop is used as written (75 when absent). This
222
+ * is deliberate: Next.js 16 defaults the list to `[75]` and would round every
223
+ * quality to 75, but vinext keeps the Next.js 14/15 behaviour for an unset
224
+ * list so migrating apps keep the qualities they ask for (see
225
+ * ImageConfig.qualities in server/image-optimization.ts).
226
+ */
227
+ function findClosestQuality(quality) {
228
+ const q = quality || 75;
229
+ if (!__imageQualities?.length) return q;
230
+ return __imageQualities.reduce((prev, cur) => Math.abs(cur - q) < Math.abs(prev - q) ? cur : prev, __imageQualities[0]);
231
+ }
232
+ /**
198
233
  * Build a `/_next/image` optimization URL.
199
234
  *
200
235
  * In production (Cloudflare Workers), the worker intercepts this path and uses
@@ -202,9 +237,10 @@ const __imageEndpoint = `${process.env.__NEXT_ROUTER_BASEPATH ?? ""}/_next/image
202
237
  * server handles it as a passthrough (serves the original file).
203
238
  */
204
239
  function imageOptimizationUrl(src, width, quality = 75) {
240
+ const q = findClosestQuality(quality);
205
241
  const source = extractLocalDeploymentId(src);
206
242
  const deploymentQuery = source.src.startsWith("/") && source.deploymentId ? `&dpl=${source.deploymentId}` : "";
207
- return `${__imageEndpoint}?url=${encodeURIComponent(source.src)}&w=${width}&q=${quality}${deploymentQuery}`;
243
+ return `${__imageEndpoint}?url=${encodeURIComponent(source.src)}&w=${width}&q=${q}${deploymentQuery}`;
208
244
  }
209
245
  /**
210
246
  * Generate a srcSet string for responsive images.
@@ -283,7 +319,7 @@ function getImageProps(props) {
283
319
  }
284
320
  }
285
321
  }
286
- const imgQuality = typeof _quality === "string" ? Number(_quality) : _quality ?? 75;
322
+ const imgQuality = parseImageQuality(_quality) ?? 75;
287
323
  const resolvedSrc = blockedInProd ? "" : loader ? loader({
288
324
  src,
289
325
  width: imgWidth ?? 0,
@@ -320,4 +356,4 @@ function getImageProps(props) {
320
356
  return { props: Object.assign(imageProps, { "data-nimg": fill ? "fill" : "1" }) };
321
357
  }
322
358
  //#endregion
323
- export { RESPONSIVE_WIDTHS, __dangerouslyAllowSVG, __globallyUnoptimized, __isDev, generateImageAttributes, getFillStyle, getImageProps, imageOptimizationUrl, isRemoteUrl, isSvgUrl, resolveImageSource, sanitizeBlurDataURL, validateRemoteUrl };
359
+ export { RESPONSIVE_WIDTHS, __dangerouslyAllowSVG, __globallyUnoptimized, __isDev, generateImageAttributes, getFillStyle, getImageProps, imageOptimizationUrl, isRemoteUrl, isSvgUrl, parseImageQuality, resolveImageSource, sanitizeBlurDataURL, validateRemoteUrl };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { useMergedRef } from "./use-merged-ref.js";
3
- import { RESPONSIVE_WIDTHS, __dangerouslyAllowSVG, __globallyUnoptimized, __isDev, generateImageAttributes, getFillStyle, getImageProps, imageOptimizationUrl, isRemoteUrl, isSvgUrl, resolveImageSource, sanitizeBlurDataURL, validateRemoteUrl } from "./image-props.js";
3
+ import { RESPONSIVE_WIDTHS, __dangerouslyAllowSVG, __globallyUnoptimized, __isDev, generateImageAttributes, getFillStyle, getImageProps, imageOptimizationUrl, isRemoteUrl, isSvgUrl, parseImageQuality, resolveImageSource, sanitizeBlurDataURL, validateRemoteUrl } from "./image-props.js";
4
4
  import { forwardRef, useEffect, useLayoutEffect, useRef, useState } from "react";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  import * as ReactDOM from "react-dom";
@@ -197,7 +197,7 @@ const Image = forwardRef(function Image({ src: srcProp, alt, width, height, fill
197
197
  });
198
198
  }
199
199
  if (loader) {
200
- const resolvedQuality = typeof quality === "string" ? Number(quality) : quality ?? 75;
200
+ const resolvedQuality = parseImageQuality(quality) ?? 75;
201
201
  const resolvedSrc = loader({
202
202
  src,
203
203
  width: imgWidth ?? 0,
@@ -293,7 +293,7 @@ const Image = forwardRef(function Image({ src: srcProp, alt, width, height, fill
293
293
  });
294
294
  }
295
295
  }
296
- const imgQuality = typeof quality === "string" ? Number(quality) : quality ?? 75;
296
+ const imgQuality = parseImageQuality(quality) ?? 75;
297
297
  const skipOptimization = isSvgUrl(src) && !__dangerouslyAllowSVG;
298
298
  const optimizedAttributes = imgWidth && !fill && !skipOptimization ? generateImageAttributes(src, imgWidth, imgQuality, sizes) : void 0;
299
299
  const srcSet = optimizedAttributes ? optimizedAttributes.srcSet : imgWidth && !fill ? RESPONSIVE_WIDTHS.filter((w) => w <= imgWidth * 2).map((w) => `${src} ${w}w`).join(", ") || `${src} ${imgWidth}w` : void 0;
@@ -28,10 +28,13 @@ export declare function setAppElementsRenderedSearch(elements: AppElements, sear
28
28
  * that arrives after the head (initial hydration) is still seen.
29
29
  */
30
30
  export declare function bindAppElementsRenderedSearch(elements: AppElements, snapshot: Pick<ClientNavigationRenderSnapshot, "renderedSearch" | "search">): void;
31
+ export declare function isBfcacheIdentityRetained(identity: string): boolean;
31
32
  export declare const BfcacheIdentityMapContext: React$1.Context<Readonly<Record<string, string>>>;
32
33
  export type BfcacheSlotEntry = {
33
34
  content: React$1.ReactNode;
34
35
  elements?: AppElements;
36
+ /** Every entry key inside a hidden entry, its own included. */
37
+ retainedKeys?: readonly string[];
35
38
  segmentId?: string;
36
39
  stateKey: string;
37
40
  stateKeyMap?: Readonly<Record<string, string>>;
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { APP_BFCACHE_SEGMENT_IDENTITIES_KEY, AppElementsWire, UNMATCHED_SLOT } from "../server/app-elements-wire.js";
3
- import "../server/app-elements.js";
3
+ import { readOptimisticStateKeyRebinder } from "../server/app-elements.js";
4
4
  import { isNestedBfcacheSlotSegmentId, isNestedBfcacheSlotSegmentIdFor } from "../server/bfcache-identity.js";
5
5
  import { notFound } from "./navigation-errors.js";
6
6
  import { getBfcacheIdMapContext, getBfcacheSegmentIdContext } from "./navigation-context-state.js";
@@ -51,6 +51,25 @@ const BfcacheSegmentIdContext = getBfcacheSegmentIdContext();
51
51
  const EMPTY_BFCACHE_STATE_KEYS = Object.freeze({});
52
52
  const MAX_BFCACHE_SLOT_ENTRIES_WITH_CACHE_COMPONENTS = 3;
53
53
  const MAX_BFCACHE_SLOT_ENTRIES_WITHOUT_CACHE_COMPONENTS = 1;
54
+ const rootBfcacheRetainedKeysReports = /* @__PURE__ */ new Map();
55
+ function isBfcacheIdentityRetained(identity) {
56
+ return hasBfcacheRetainedKey(rootBfcacheRetainedKeysReports, identity);
57
+ }
58
+ function hasBfcacheRetainedKey(reports, identity) {
59
+ for (const report of reports.values()) {
60
+ if (report.keys.includes(identity) || report.hiddenKeys.includes(identity)) return true;
61
+ if (hasBfcacheRetainedKey(report.activeReports, identity)) return true;
62
+ }
63
+ return false;
64
+ }
65
+ const BfcacheRetainedKeysContext = React$1.createContext(rootBfcacheRetainedKeysReports);
66
+ function collectBfcacheRetainedKeys(reports, keys) {
67
+ for (const report of reports.values()) {
68
+ for (const key of report.keys) keys.add(key);
69
+ for (const key of report.hiddenKeys) keys.add(key);
70
+ collectBfcacheRetainedKeys(report.activeReports, keys);
71
+ }
72
+ }
54
73
  const BfcacheIdentityMapContext = React$1.createContext(EMPTY_BFCACHE_STATE_KEYS);
55
74
  function isCacheComponentsEnabled() {
56
75
  return String(process.env.__NEXT_CACHE_COMPONENTS) === "true";
@@ -136,34 +155,71 @@ function warnTransportMetadataEntry(id) {
136
155
  * falling back to the live boundary values for entries that predate per-entry
137
156
  * capture.
138
157
  */
139
- function BfcacheEntryProviders({ entry, fallbackElements, fallbackSegmentId, fallbackStateKeyMap, SegmentContext }) {
158
+ function BfcacheEntryProviders({ entry, fallbackElements, fallbackSegmentId, fallbackStateKeyMap, retainedKeysReports, SegmentContext }) {
140
159
  return /* @__PURE__ */ jsx(BfcacheIdentityMapContext.Provider, {
141
160
  value: entry.stateKeyMap ?? fallbackStateKeyMap,
142
161
  children: /* @__PURE__ */ jsx(ElementsContext.Provider, {
143
162
  value: entry.elements ?? fallbackElements,
144
163
  children: /* @__PURE__ */ jsx(SegmentContext.Provider, {
145
164
  value: entry.segmentId ?? fallbackSegmentId,
146
- children: entry.content
165
+ children: /* @__PURE__ */ jsx(BfcacheRetainedKeysContext.Provider, {
166
+ value: retainedKeysReports,
167
+ children: entry.content
168
+ })
147
169
  })
148
170
  })
149
171
  });
150
172
  }
151
- function useBfcacheSlotEntries(activeEntry) {
173
+ function useBfcacheSlotEntries(listId, activeEntry) {
152
174
  const snapshotsByStateKey = React$1.useRef(/* @__PURE__ */ new Map());
153
175
  const [entryOrder, setEntryOrder] = React$1.useState(() => [activeEntry.stateKey]);
176
+ const [retainedKeysReportsByStateKey] = React$1.useState(() => /* @__PURE__ */ new Map());
177
+ const getRetainedKeysReports = (stateKey) => {
178
+ let reports = retainedKeysReportsByStateKey.get(stateKey);
179
+ if (!reports) {
180
+ reports = /* @__PURE__ */ new Map();
181
+ retainedKeysReportsByStateKey.set(stateKey, reports);
182
+ }
183
+ return reports;
184
+ };
154
185
  const committedSnapshots = snapshotsByStateKey.current;
155
186
  const staged = stageBfcacheSlotEntryForRender(committedSnapshots, entryOrder, activeEntry);
187
+ for (const [index, entry] of staged.entries.entries()) {
188
+ if (entry.stateKey === activeEntry.stateKey || entry.retainedKeys) continue;
189
+ const retainedKeys = /* @__PURE__ */ new Set([entry.stateKey]);
190
+ collectBfcacheRetainedKeys(getRetainedKeysReports(entry.stateKey), retainedKeys);
191
+ const hiddenEntry = {
192
+ ...entry,
193
+ retainedKeys: [...retainedKeys]
194
+ };
195
+ staged.entries[index] = hiddenEntry;
196
+ staged.snapshots.set(entry.stateKey, hiddenEntry);
197
+ }
156
198
  const nextOrder = staged.order;
157
199
  const orderChanged = !haveSameBfcacheSlotEntryOrder(entryOrder, nextOrder);
158
200
  React$1.useLayoutEffect(() => {
159
201
  snapshotsByStateKey.current = staged.snapshots;
160
- }, [staged.snapshots]);
202
+ for (const stateKey of retainedKeysReportsByStateKey.keys()) if (!staged.snapshots.has(stateKey)) retainedKeysReportsByStateKey.delete(stateKey);
203
+ }, [retainedKeysReportsByStateKey, staged.snapshots]);
204
+ const hiddenKeys = [...new Set(staged.entries.flatMap((entry) => entry.retainedKeys ?? []))];
205
+ const enclosingReports = React$1.useContext(BfcacheRetainedKeysContext);
206
+ const report = {
207
+ activeReports: getRetainedKeysReports(activeEntry.stateKey),
208
+ hiddenKeys,
209
+ keys: staged.entries.map((entry) => entry.stateKey)
210
+ };
211
+ React$1.useLayoutEffect(() => {
212
+ enclosingReports.set(listId, report);
213
+ });
161
214
  if (process.env.NODE_ENV !== "production" && !staged.snapshots.has(activeEntry.stateKey)) throw new Error("BFCache Activity slot is missing the active entry snapshot");
162
215
  if (orderChanged) setEntryOrder(nextOrder);
163
- return staged.entries;
216
+ return {
217
+ entries: staged.entries,
218
+ getRetainedKeysReports
219
+ };
164
220
  }
165
221
  function BfcacheActivitySlotBoundary({ activeStateKey, content, elements, id, SegmentContext, stateKeyMap }) {
166
- const renderEntries = useBfcacheSlotEntries({
222
+ const { entries: renderEntries, getRetainedKeysReports } = useBfcacheSlotEntries(id, {
167
223
  content,
168
224
  elements,
169
225
  segmentId: id,
@@ -177,6 +233,7 @@ function BfcacheActivitySlotBoundary({ activeStateKey, content, elements, id, Se
177
233
  fallbackElements: elements,
178
234
  fallbackSegmentId: id,
179
235
  fallbackStateKeyMap: stateKeyMap,
236
+ retainedKeysReports: getRetainedKeysReports(entry.stateKey),
180
237
  SegmentContext
181
238
  })
182
239
  }, entry.stateKey)) });
@@ -228,7 +285,8 @@ function BfcacheChildrenActivityBoundary({ children, id, IdMapContext, SegmentCo
228
285
  const identityMap = React$1.useContext(BfcacheIdentityMapContext);
229
286
  const bfcacheIdMap = React$1.useContext(IdMapContext);
230
287
  const segmentId = React$1.useContext(SegmentContext);
231
- const activeStateKey = stateKey ?? (id === void 0 ? void 0 : resolveBfcacheSegmentStateKey(id, identityMap, bfcacheIdMap));
288
+ const rebindStateKey = readOptimisticStateKeyRebinder(elements);
289
+ const activeStateKey = stateKey !== void 0 ? rebindStateKey?.(stateKey) ?? stateKey : id === void 0 ? void 0 : resolveBfcacheSegmentStateKey(id, identityMap, bfcacheIdMap);
232
290
  if (segmentId === null || activeStateKey === void 0) return /* @__PURE__ */ jsx(Fragment$1, { children });
233
291
  return /* @__PURE__ */ jsx(BfcacheActivitySlotBoundary, {
234
292
  activeStateKey,
@@ -375,4 +433,4 @@ function ParallelSlot({ name }) {
375
433
  return React$1.useContext(ParallelSlotsContext)?.[name] ?? null;
376
434
  }
377
435
  //#endregion
378
- export { BfcacheChildrenBoundary, BfcacheIdentityMapContext, BfcacheSegmentBoundary, Children, ChildrenContext, ElementsContext, ParallelSlot, ParallelSlotsContext, RenderedSearchContext, Slot, UNMATCHED_SLOT, bindAppElementsRenderedSearch, getNonCacheComponentsSegmentKey, mergeElements, resolveBfcacheSegmentStateKey, setAppElementsRenderedSearch, stageBfcacheSlotEntryForRender, updateBfcacheSlotEntryOrder };
436
+ export { BfcacheChildrenBoundary, BfcacheIdentityMapContext, BfcacheSegmentBoundary, Children, ChildrenContext, ElementsContext, ParallelSlot, ParallelSlotsContext, RenderedSearchContext, Slot, UNMATCHED_SLOT, bindAppElementsRenderedSearch, getNonCacheComponentsSegmentKey, isBfcacheIdentityRetained, mergeElements, resolveBfcacheSegmentStateKey, setAppElementsRenderedSearch, stageBfcacheSlotEntryForRender, updateBfcacheSlotEntryOrder };