@netlify/plugin-nextjs 5.9.3 → 5.10.0-fetch-patch-logs

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.
@@ -7,14 +7,14 @@
7
7
  import {
8
8
  copyFetchContent,
9
9
  copyPrerenderedContent
10
- } from "../../esm-chunks/chunk-J4D25YDX.js";
10
+ } from "../../esm-chunks/chunk-JPTD4GEB.js";
11
+ import "../../esm-chunks/chunk-TYCYFZ22.js";
11
12
  import "../../esm-chunks/chunk-5QSXBV7L.js";
12
13
  import "../../esm-chunks/chunk-GNGHTHMQ.js";
13
14
  import "../../esm-chunks/chunk-ZSVHJNNY.js";
14
15
  import "../../esm-chunks/chunk-KGYJQ2U2.js";
15
16
  import "../../esm-chunks/chunk-ZENB67PD.js";
16
17
  import "../../esm-chunks/chunk-APO262HE.js";
17
- import "../../esm-chunks/chunk-TYCYFZ22.js";
18
18
  import "../../esm-chunks/chunk-OEQOKJGE.js";
19
19
  export {
20
20
  copyFetchContent,
@@ -9,20 +9,22 @@ import {
9
9
  copyStaticContent,
10
10
  copyStaticExport,
11
11
  publishStaticDir,
12
+ setHeadersConfig,
12
13
  unpublishStaticDir
13
- } from "../../esm-chunks/chunk-NTLBFRPA.js";
14
+ } from "../../esm-chunks/chunk-MKMK7FBF.js";
15
+ import "../../esm-chunks/chunk-TYCYFZ22.js";
14
16
  import "../../esm-chunks/chunk-5QSXBV7L.js";
15
17
  import "../../esm-chunks/chunk-GNGHTHMQ.js";
16
18
  import "../../esm-chunks/chunk-ZSVHJNNY.js";
17
19
  import "../../esm-chunks/chunk-KGYJQ2U2.js";
18
20
  import "../../esm-chunks/chunk-ZENB67PD.js";
19
21
  import "../../esm-chunks/chunk-APO262HE.js";
20
- import "../../esm-chunks/chunk-TYCYFZ22.js";
21
22
  import "../../esm-chunks/chunk-OEQOKJGE.js";
22
23
  export {
23
24
  copyStaticAssets,
24
25
  copyStaticContent,
25
26
  copyStaticExport,
26
27
  publishStaticDir,
28
+ setHeadersConfig,
27
29
  unpublishStaticDir
28
30
  };
@@ -7,7 +7,7 @@
7
7
  import {
8
8
  clearStaleEdgeHandlers,
9
9
  createEdgeHandlers
10
- } from "../../esm-chunks/chunk-HGXG447M.js";
10
+ } from "../../esm-chunks/chunk-3RQSTU2O.js";
11
11
  import "../../esm-chunks/chunk-GFYWJNQR.js";
12
12
  import "../../esm-chunks/chunk-KGYJQ2U2.js";
13
13
  import "../../esm-chunks/chunk-APO262HE.js";
@@ -28,6 +28,7 @@ export default async function (req, context) {
28
28
  'site.id': context.site.id,
29
29
  'http.method': req.method,
30
30
  'http.target': req.url,
31
+ isBackgroundRevalidation: requestContext.isBackgroundRevalidation,
31
32
  monorepo: true,
32
33
  cwd: '{{cwd}}',
33
34
  })
@@ -35,7 +36,7 @@ export default async function (req, context) {
35
36
  const { default: handler } = await import('{{nextServerHandler}}')
36
37
  cachedHandler = handler
37
38
  }
38
- const response = await cachedHandler(req, context)
39
+ const response = await cachedHandler(req, context, span, requestContext)
39
40
  span.setAttributes({
40
41
  'http.status_code': response.status,
41
42
  })
@@ -25,10 +25,11 @@ export default async function handler(req, context) {
25
25
  'site.id': context.site.id,
26
26
  'http.method': req.method,
27
27
  'http.target': req.url,
28
+ isBackgroundRevalidation: requestContext.isBackgroundRevalidation,
28
29
  monorepo: false,
29
30
  cwd: process.cwd(),
30
31
  })
31
- const response = await serverHandler(req, context)
32
+ const response = await serverHandler(req, context, span, requestContext)
32
33
  span.setAttributes({
33
34
  'http.status_code': response.status,
34
35
  })
@@ -409,9 +409,7 @@ var require_dist = __commonJS({
409
409
  var import_fast_glob = __toESM(require_out(), 1);
410
410
  var import_path_to_regexp = __toESM(require_dist(), 1);
411
411
  import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
412
- import { dirname, join, relative, sep } from "node:path";
413
- import { sep as posixSep } from "node:path/posix";
414
- var toPosixPath = (path) => path.split(sep).join(posixSep);
412
+ import { dirname, join } from "node:path";
415
413
  var writeEdgeManifest = async (ctx, manifest) => {
416
414
  await mkdir(ctx.edgeFunctionsDir, { recursive: true });
417
415
  await writeFile(join(ctx.edgeFunctionsDir, "manifest.json"), JSON.stringify(manifest, null, 2));
@@ -429,13 +427,21 @@ var copyRuntime = async (ctx, handlerDirectory) => {
429
427
  );
430
428
  };
431
429
  var augmentMatchers = (matchers, ctx) => {
432
- if (!ctx.buildConfig.i18n) {
430
+ const i18NConfig = ctx.buildConfig.i18n;
431
+ if (!i18NConfig) {
433
432
  return matchers;
434
433
  }
435
434
  return matchers.flatMap((matcher) => {
436
435
  if (matcher.originalSource && matcher.locale !== false) {
437
436
  return [
438
- matcher,
437
+ matcher.regexp ? {
438
+ ...matcher,
439
+ // https://github.com/vercel/next.js/blob/5e236c9909a768dc93856fdfad53d4f4adc2db99/packages/next/src/build/analysis/get-page-static-info.ts#L332-L336
440
+ // Next is producing pretty broad matcher for i18n locale. Presumably rest of their infrastructure protects this broad matcher
441
+ // from matching on non-locale paths. For us this becomes request entry point, so we need to narrow it down to just defined locales
442
+ // otherwise users might get unexpected matches on paths like `/api*`
443
+ regexp: matcher.regexp.replace(/\[\^\/\.]+/g, `(${i18NConfig.locales.join("|")})`)
444
+ } : matcher,
439
445
  {
440
446
  ...matcher,
441
447
  regexp: (0, import_path_to_regexp.pathToRegexp)(matcher.originalSource).source
@@ -471,14 +477,13 @@ var writeHandlerFile = async (ctx, { matchers, name }) => {
471
477
  await writeFile(
472
478
  join(handlerDirectory, `${handlerName}.js`),
473
479
  `
474
- import { decode as _base64Decode } from './edge-runtime/vendor/deno.land/std@0.175.0/encoding/base64.ts';
475
480
  import { init as htmlRewriterInit } from './edge-runtime/vendor/deno.land/x/htmlrewriter@v1.0.0/src/index.ts'
476
- import {handleMiddleware} from './edge-runtime/middleware.ts';
481
+ import { handleMiddleware } from './edge-runtime/middleware.ts';
477
482
  import handler from './server/${name}.js';
478
483
 
479
- await htmlRewriterInit({ module_or_path: _base64Decode(${JSON.stringify(
480
- htmlRewriterWasm.toString("base64")
481
- )}).buffer });
484
+ await htmlRewriterInit({ module_or_path: Uint8Array.from(${JSON.stringify([
485
+ ...htmlRewriterWasm
486
+ ])}) });
482
487
 
483
488
  export default (req, context) => handleMiddleware(req, context, handler);
484
489
  `
@@ -493,21 +498,9 @@ var copyHandlerDependencies = async (ctx, { name, files, wasm }) => {
493
498
  const parts = [shim];
494
499
  const outputFile = join(destDir, `server/${name}.js`);
495
500
  if (wasm?.length) {
496
- const base64ModulePath = join(
497
- destDir,
498
- "edge-runtime/vendor/deno.land/std@0.175.0/encoding/base64.ts"
499
- );
500
- const base64ModulePathRelativeToOutputFile = toPosixPath(
501
- relative(dirname(outputFile), base64ModulePath)
502
- );
503
- parts.push(`import { decode as _base64Decode } from "${base64ModulePathRelativeToOutputFile}";`);
504
501
  for (const wasmChunk of wasm ?? []) {
505
502
  const data = await readFile(join(srcDir, wasmChunk.filePath));
506
- parts.push(
507
- `const ${wasmChunk.name} = _base64Decode(${JSON.stringify(
508
- data.toString("base64")
509
- )}).buffer`
510
- );
503
+ parts.push(`const ${wasmChunk.name} = Uint8Array.from(${JSON.stringify([...data])})`);
511
504
  }
512
505
  }
513
506
  for (const file of files) {
@@ -6,7 +6,10 @@
6
6
 
7
7
  import {
8
8
  getRegionalBlobStore
9
- } from "./chunk-K4RDUZYO.js";
9
+ } from "./chunk-RYJYZQ4X.js";
10
+ import {
11
+ getLogger
12
+ } from "./chunk-SGXRYMYQ.js";
10
13
  import {
11
14
  encodeBlobKey
12
15
  } from "./chunk-TYCYFZ22.js";
@@ -93,15 +96,6 @@ var adjustDateHeader = async ({
93
96
  tracer,
94
97
  requestContext
95
98
  }) => {
96
- const cacheState = headers.get("x-nextjs-cache");
97
- const isServedFromCache = cacheState === "HIT" || cacheState === "STALE";
98
- span.setAttributes({
99
- "x-nextjs-cache": cacheState ?? void 0,
100
- isServedFromCache
101
- });
102
- if (!isServedFromCache) {
103
- return;
104
- }
105
99
  const key = new URL(request.url).pathname;
106
100
  let lastModified;
107
101
  if (requestContext.responseCacheGetLastModified) {
@@ -145,20 +139,33 @@ var adjustDateHeader = async ({
145
139
  headers.set("x-nextjs-date", headers.get("date") ?? lastModifiedDate.toUTCString());
146
140
  headers.set("date", lastModifiedDate.toUTCString());
147
141
  };
148
- var setCacheControlHeaders = ({ headers, status }, request, requestContext) => {
142
+ function setCacheControlFromRequestContext(headers, revalidate) {
143
+ const cdnCacheControl = (
144
+ // if we are serving already stale response, instruct edge to not attempt to cache that response
145
+ headers.get("x-nextjs-cache") === "STALE" ? "public, max-age=0, must-revalidate, durable" : `s-maxage=${revalidate || 31536e3}, stale-while-revalidate=31536000, durable`
146
+ );
147
+ headers.set("netlify-cdn-cache-control", cdnCacheControl);
148
+ }
149
+ var setCacheControlHeaders = ({ headers, status }, request, requestContext, nextConfig) => {
149
150
  if (typeof requestContext.routeHandlerRevalidate !== "undefined" && ["GET", "HEAD"].includes(request.method) && !headers.has("cdn-cache-control") && !headers.has("netlify-cdn-cache-control")) {
150
- const cdnCacheControl = (
151
- // if we are serving already stale response, instruct edge to not attempt to cache that response
152
- headers.get("x-nextjs-cache") === "STALE" ? "public, max-age=0, must-revalidate, durable" : `s-maxage=${requestContext.routeHandlerRevalidate === false ? 31536e3 : requestContext.routeHandlerRevalidate}, stale-while-revalidate=31536000, durable`
153
- );
154
- headers.set("netlify-cdn-cache-control", cdnCacheControl);
151
+ setCacheControlFromRequestContext(headers, requestContext.routeHandlerRevalidate);
155
152
  return;
156
153
  }
157
- if (status === 404 && request.url.endsWith(".php")) {
158
- headers.set("cache-control", "public, max-age=0, must-revalidate");
159
- headers.set("netlify-cdn-cache-control", `max-age=31536000, durable`);
154
+ if (status === 308 && request.url.endsWith("/") !== nextConfig.trailingSlash) {
155
+ getLogger().withFields({ trailingSlash: nextConfig.trailingSlash, location: headers.get("location") }).log("NetlifyHeadersHandler.trailingSlashRedirect");
160
156
  }
161
157
  const cacheControl = headers.get("cache-control");
158
+ if (status === 404) {
159
+ if (request.url.endsWith(".php")) {
160
+ headers.set("cache-control", "public, max-age=0, must-revalidate");
161
+ headers.set("netlify-cdn-cache-control", `max-age=31536000, durable`);
162
+ return;
163
+ }
164
+ if (process.env.CACHE_404_PAGE && request.url.endsWith("/404") && ["GET", "HEAD"].includes(request.method)) {
165
+ setCacheControlFromRequestContext(headers, requestContext.pageHandlerRevalidate);
166
+ return;
167
+ }
168
+ }
162
169
  if (cacheControl !== null && ["GET", "HEAD"].includes(request.method) && !headers.has("cdn-cache-control") && !headers.has("netlify-cdn-cache-control")) {
163
170
  const browserCacheControl = omitHeaderValues(cacheControl, [
164
171
  "s-maxage",
@@ -177,7 +184,7 @@ var setCacheControlHeaders = ({ headers, status }, request, requestContext) => {
177
184
  headers.set("netlify-cdn-cache-control", cdnCacheControl);
178
185
  return;
179
186
  }
180
- if (cacheControl === null && !headers.has("cdn-cache-control") && !headers.has("netlify-cdn-cache-control") && requestContext.usedFsReadForNonFallback) {
187
+ if (cacheControl === null && ["GET", "HEAD"].includes(request.method) && !headers.has("cdn-cache-control") && !headers.has("netlify-cdn-cache-control") && requestContext.usedFsReadForNonFallback) {
181
188
  headers.set("cache-control", "public, max-age=0, must-revalidate");
182
189
  headers.set("netlify-cdn-cache-control", `max-age=31536000, durable`);
183
190
  }
@@ -192,8 +199,7 @@ var NEXT_CACHE_TO_CACHE_STATUS = {
192
199
  MISS: `fwd=miss`,
193
200
  STALE: `hit; fwd=stale`
194
201
  };
195
- var setCacheStatusHeader = (headers) => {
196
- const nextCache = headers.get("x-nextjs-cache");
202
+ var setCacheStatusHeader = (headers, nextCache) => {
197
203
  if (typeof nextCache === "string") {
198
204
  if (nextCache in NEXT_CACHE_TO_CACHE_STATUS) {
199
205
  const cacheStatus = NEXT_CACHE_TO_CACHE_STATUS[nextCache];
@@ -4,6 +4,9 @@
4
4
  return createRequire(import.meta.url);
5
5
  })();
6
6
 
7
+ import {
8
+ encodeBlobKey
9
+ } from "./chunk-TYCYFZ22.js";
7
10
  import {
8
11
  wrapTracer
9
12
  } from "./chunk-5QSXBV7L.js";
@@ -20,9 +23,6 @@ import {
20
23
  import {
21
24
  require_semver
22
25
  } from "./chunk-APO262HE.js";
23
- import {
24
- encodeBlobKey
25
- } from "./chunk-TYCYFZ22.js";
26
26
  import {
27
27
  __toESM
28
28
  } from "./chunk-OEQOKJGE.js";
@@ -160,12 +160,13 @@ var routeToFilePath = (path) => {
160
160
  }
161
161
  return `/${path}`;
162
162
  };
163
- var buildPagesCacheValue = async (path, shouldUseEnumKind, shouldSkipJson = false) => ({
163
+ var buildPagesCacheValue = async (path, initialRevalidateSeconds, shouldUseEnumKind, shouldSkipJson = false) => ({
164
164
  kind: shouldUseEnumKind ? "PAGES" : "PAGE",
165
165
  html: await readFile(`${path}.html`, "utf-8"),
166
166
  pageData: shouldSkipJson ? {} : JSON.parse(await readFile(`${path}.json`, "utf-8")),
167
167
  headers: void 0,
168
- status: void 0
168
+ status: void 0,
169
+ revalidate: initialRevalidateSeconds
169
170
  });
170
171
  var buildAppCacheValue = async (path, shouldUseAppPageKind) => {
171
172
  const meta = JSON.parse(await readFile(`${path}.meta`, "utf-8"));
@@ -231,6 +232,7 @@ var copyPrerenderedContent = async (ctx) => {
231
232
  }
232
233
  value = await buildPagesCacheValue(
233
234
  join(ctx.publishDir, "server/pages", key),
235
+ meta.initialRevalidateSeconds,
234
236
  shouldUseEnumKind
235
237
  );
236
238
  break;
@@ -260,6 +262,7 @@ var copyPrerenderedContent = async (ctx) => {
260
262
  const key = routeToFilePath(route);
261
263
  const value = await buildPagesCacheValue(
262
264
  join(ctx.publishDir, "server/pages", key),
265
+ void 0,
263
266
  shouldUseEnumKind,
264
267
  true
265
268
  // there is no corresponding json file for fallback, so we are skipping it for this entry
@@ -4,6 +4,9 @@
4
4
  return createRequire(import.meta.url);
5
5
  })();
6
6
 
7
+ import {
8
+ encodeBlobKey
9
+ } from "./chunk-TYCYFZ22.js";
7
10
  import {
8
11
  wrapTracer
9
12
  } from "./chunk-5QSXBV7L.js";
@@ -17,9 +20,6 @@ import {
17
20
  import {
18
21
  require_out
19
22
  } from "./chunk-KGYJQ2U2.js";
20
- import {
21
- encodeBlobKey
22
- } from "./chunk-TYCYFZ22.js";
23
23
  import {
24
24
  __toESM
25
25
  } from "./chunk-OEQOKJGE.js";
@@ -80,6 +80,15 @@ var copyStaticAssets = async (ctx) => {
80
80
  }
81
81
  });
82
82
  };
83
+ var setHeadersConfig = async (ctx) => {
84
+ const { basePath } = ctx.buildConfig;
85
+ ctx.netlifyConfig.headers.push({
86
+ for: `${basePath}/_next/static/*`,
87
+ values: {
88
+ "Cache-Control": "public, max-age=31536000, immutable"
89
+ }
90
+ });
91
+ };
83
92
  var copyStaticExport = async (ctx) => {
84
93
  await tracer.withActiveSpan("copyStaticExport", async () => {
85
94
  if (!ctx.exportDetail?.outDirectory) {
@@ -116,6 +125,7 @@ var unpublishStaticDir = async (ctx) => {
116
125
  export {
117
126
  copyStaticContent,
118
127
  copyStaticAssets,
128
+ setHeadersConfig,
119
129
  copyStaticExport,
120
130
  publishStaticDir,
121
131
  unpublishStaticDir
@@ -597,6 +597,7 @@ var getDeployStore = (input = {}) => {
597
597
  // src/run/regional-blob-store.cts
598
598
  var fetchBeforeNextPatchedIt = globalThis.fetch;
599
599
  var getRegionalBlobStore = (args = {}) => {
600
+ console.log("Blob store fetch patched", fetchBeforeNextPatchedIt.__nextPatched);
600
601
  return getDeployStore({
601
602
  ...args,
602
603
  fetch: fetchBeforeNextPatchedIt,
@@ -101,25 +101,6 @@ var init_internal = __esm({
101
101
  init_internal();
102
102
 
103
103
  // src/run/handlers/request-context.cts
104
- function createRequestContext(request, context) {
105
- const backgroundWorkPromises = [];
106
- return {
107
- captureServerTiming: request?.headers.has("x-next-debug-logging") ?? false,
108
- trackBackgroundWork: (promise) => {
109
- if (context?.waitUntil) {
110
- context.waitUntil(promise);
111
- } else {
112
- backgroundWorkPromises.push(promise);
113
- }
114
- },
115
- get backgroundWorkPromise() {
116
- return Promise.allSettled(backgroundWorkPromises);
117
- },
118
- logger: systemLogger.withLogLevel(
119
- request?.headers.has("x-nf-debug-logging") || request?.headers.has("x-next-debug-logging") ? LogLevel.Debug : LogLevel.Log
120
- )
121
- };
122
- }
123
104
  var REQUEST_CONTEXT_GLOBAL_KEY = Symbol.for("nf-request-context-async-local-storage");
124
105
  var requestContextAsyncLocalStorage;
125
106
  function getRequestContextAsyncLocalStorage() {
@@ -141,7 +122,6 @@ function getLogger() {
141
122
  }
142
123
 
143
124
  export {
144
- createRequestContext,
145
125
  getRequestContext,
146
126
  getLogger
147
127
  };
@@ -6,7 +6,7 @@
6
6
 
7
7
  import {
8
8
  getRequestContext
9
- } from "./chunk-LVXJQ2G2.js";
9
+ } from "./chunk-SGXRYMYQ.js";
10
10
  import {
11
11
  wrapTracer
12
12
  } from "./chunk-5QSXBV7L.js";
@@ -6,13 +6,13 @@
6
6
 
7
7
  import {
8
8
  getTracer
9
- } from "./chunk-NEZW7TGG.js";
10
- import {
11
- getRequestContext
12
- } from "./chunk-LVXJQ2G2.js";
9
+ } from "./chunk-WHUPSPWV.js";
13
10
  import {
14
11
  getRegionalBlobStore
15
- } from "./chunk-K4RDUZYO.js";
12
+ } from "./chunk-RYJYZQ4X.js";
13
+ import {
14
+ getRequestContext
15
+ } from "./chunk-SGXRYMYQ.js";
16
16
  import "./chunk-5QSXBV7L.js";
17
17
  import "./chunk-GNGHTHMQ.js";
18
18
  import {
@@ -8,7 +8,7 @@ import "./chunk-OEQOKJGE.js";
8
8
 
9
9
  // package.json
10
10
  var name = "@netlify/plugin-nextjs";
11
- var version = "5.9.3";
11
+ var version = "5.10.0";
12
12
  var description = "Run Next.js seamlessly on Netlify";
13
13
  var main = "./dist/index.js";
14
14
  var type = "module";
@@ -78,7 +78,7 @@ var devDependencies = {
78
78
  "@vercel/nft": "^0.27.0",
79
79
  cheerio: "^1.0.0-rc.12",
80
80
  "clean-package": "^2.2.0",
81
- esbuild: "^0.24.0",
81
+ esbuild: "^0.25.0",
82
82
  execa: "^8.0.1",
83
83
  "fast-glob": "^3.3.2",
84
84
  "fs-monkey": "^1.0.6",
package/dist/index.js CHANGED
@@ -7,22 +7,24 @@
7
7
  import {
8
8
  clearStaleEdgeHandlers,
9
9
  createEdgeHandlers
10
- } from "./esm-chunks/chunk-HGXG447M.js";
10
+ } from "./esm-chunks/chunk-3RQSTU2O.js";
11
11
  import {
12
12
  clearStaleServerHandlers,
13
13
  createServerHandler
14
14
  } from "./esm-chunks/chunk-DLVROEVU.js";
15
15
  import {
16
16
  copyPrerenderedContent
17
- } from "./esm-chunks/chunk-J4D25YDX.js";
17
+ } from "./esm-chunks/chunk-JPTD4GEB.js";
18
18
  import "./esm-chunks/chunk-IJZEDP6B.js";
19
19
  import {
20
20
  copyStaticAssets,
21
21
  copyStaticContent,
22
22
  copyStaticExport,
23
23
  publishStaticDir,
24
+ setHeadersConfig,
24
25
  unpublishStaticDir
25
- } from "./esm-chunks/chunk-NTLBFRPA.js";
26
+ } from "./esm-chunks/chunk-MKMK7FBF.js";
27
+ import "./esm-chunks/chunk-TYCYFZ22.js";
26
28
  import {
27
29
  wrapTracer
28
30
  } from "./esm-chunks/chunk-5QSXBV7L.js";
@@ -48,7 +50,6 @@ import {
48
50
  import "./esm-chunks/chunk-KGYJQ2U2.js";
49
51
  import "./esm-chunks/chunk-ZENB67PD.js";
50
52
  import "./esm-chunks/chunk-APO262HE.js";
51
- import "./esm-chunks/chunk-TYCYFZ22.js";
52
53
  import "./esm-chunks/chunk-UYKENJEU.js";
53
54
  import "./esm-chunks/chunk-OEQOKJGE.js";
54
55
 
@@ -83,7 +84,7 @@ var onBuild = async (options) => {
83
84
  await saveBuildCache(ctx);
84
85
  }
85
86
  if (ctx.buildConfig.output === "export") {
86
- return Promise.all([copyStaticExport(ctx), setImageConfig(ctx)]);
87
+ return Promise.all([copyStaticExport(ctx), setHeadersConfig(ctx), setImageConfig(ctx)]);
87
88
  }
88
89
  await verifyAdvancedAPIRoutes(ctx);
89
90
  await verifyNetlifyFormsWorkaround(ctx);
@@ -93,6 +94,7 @@ var onBuild = async (options) => {
93
94
  copyPrerenderedContent(ctx),
94
95
  createServerHandler(ctx),
95
96
  createEdgeHandlers(ctx),
97
+ setHeadersConfig(ctx),
96
98
  setImageConfig(ctx)
97
99
  ]);
98
100
  });
@@ -203,7 +203,7 @@ var import_constants = require("next/dist/lib/constants.js");
203
203
 
204
204
  // package.json
205
205
  var name = "@netlify/plugin-nextjs";
206
- var version = "5.9.3";
206
+ var version = "5.10.0";
207
207
 
208
208
  // src/shared/cache-types.cts
209
209
  var isCachedPageValue = (value) => value.kind === "PAGE" || value.kind === "PAGES";
@@ -801,6 +801,7 @@ var getDeployStore = (input = {}) => {
801
801
  // src/run/regional-blob-store.cts
802
802
  var fetchBeforeNextPatchedIt = globalThis.fetch;
803
803
  var getRegionalBlobStore = (args = {}) => {
804
+ console.log("Blob store fetch patched", fetchBeforeNextPatchedIt.__nextPatched);
804
805
  return getDeployStore({
805
806
  ...args,
806
807
  fetch: fetchBeforeNextPatchedIt,
@@ -1809,6 +1810,19 @@ var NetlifyCacheHandler = class {
1809
1810
  const { encodeBlobKey: encodeBlobKey2 } = await Promise.resolve().then(() => (init_blobkey(), blobkey_exports));
1810
1811
  return await encodeBlobKey2(key);
1811
1812
  }
1813
+ getTTL(blob) {
1814
+ if (blob.value?.kind === "FETCH" || blob.value?.kind === "ROUTE" || blob.value?.kind === "APP_ROUTE" || blob.value?.kind === "PAGE" || blob.value?.kind === "PAGES" || blob.value?.kind === "APP_PAGE") {
1815
+ const { revalidate } = blob.value;
1816
+ if (typeof revalidate === "number") {
1817
+ const revalidateAfter = revalidate * 1e3 + blob.lastModified;
1818
+ return (revalidateAfter - Date.now()) / 1e3;
1819
+ }
1820
+ if (revalidate === false) {
1821
+ return "PERMANENT";
1822
+ }
1823
+ }
1824
+ return "NOT SET";
1825
+ }
1812
1826
  captureResponseCacheLastModified(cacheValue, key, getCacheKeySpan) {
1813
1827
  if (cacheValue.value?.kind === "FETCH") {
1814
1828
  return;
@@ -1871,28 +1885,38 @@ var NetlifyCacheHandler = class {
1871
1885
  }
1872
1886
  }
1873
1887
  }
1874
- async injectEntryToPrerenderManifest(key, revalidate) {
1875
- if (this.options.serverDistDir && (typeof revalidate === "number" || revalidate === false)) {
1888
+ async injectEntryToPrerenderManifest(key, { revalidate, cacheControl }) {
1889
+ if (this.options.serverDistDir && (typeof revalidate === "number" || revalidate === false || typeof cacheControl !== "undefined")) {
1876
1890
  try {
1877
1891
  const { loadManifest } = await import("next/dist/server/load-manifest.js");
1878
1892
  const prerenderManifest = loadManifest(
1879
1893
  (0, import_node_path.join)(this.options.serverDistDir, "..", "prerender-manifest.json")
1880
1894
  );
1881
- try {
1882
- const { normalizePagePath } = await import("next/dist/shared/lib/page-path/normalize-page-path.js");
1883
- prerenderManifest.routes[key] = {
1884
- experimentalPPR: void 0,
1885
- dataRoute: (0, import_posix.join)("/_next/data", `${normalizePagePath(key)}.json`),
1886
- srcRoute: null,
1887
- // FIXME: provide actual source route, however, when dynamically appending it doesn't really matter
1888
- initialRevalidateSeconds: revalidate,
1889
- // Pages routes do not have a prefetch data route.
1890
- prefetchDataRoute: void 0
1891
- };
1892
- } catch {
1893
- const { SharedRevalidateTimings } = await import("next/dist/server/lib/incremental-cache/shared-revalidate-timings.js");
1894
- const sharedRevalidateTimings = new SharedRevalidateTimings(prerenderManifest);
1895
- sharedRevalidateTimings.set(key, revalidate);
1895
+ if (typeof cacheControl !== "undefined") {
1896
+ const { SharedCacheControls } = await import(
1897
+ // @ts-expect-error supporting multiple next version, this module is not resolvable with currently used dev dependency
1898
+ // eslint-disable-next-line import/no-unresolved, n/no-missing-import
1899
+ "next/dist/server/lib/incremental-cache/shared-cache-controls.js"
1900
+ );
1901
+ const sharedCacheControls = new SharedCacheControls(prerenderManifest);
1902
+ sharedCacheControls.set(key, cacheControl);
1903
+ } else if (typeof revalidate === "number" || revalidate === false) {
1904
+ try {
1905
+ const { normalizePagePath } = await import("next/dist/shared/lib/page-path/normalize-page-path.js");
1906
+ prerenderManifest.routes[key] = {
1907
+ experimentalPPR: void 0,
1908
+ dataRoute: (0, import_posix.join)("/_next/data", `${normalizePagePath(key)}.json`),
1909
+ srcRoute: null,
1910
+ // FIXME: provide actual source route, however, when dynamically appending it doesn't really matter
1911
+ initialRevalidateSeconds: revalidate,
1912
+ // Pages routes do not have a prefetch data route.
1913
+ prefetchDataRoute: void 0
1914
+ };
1915
+ } catch {
1916
+ const { SharedRevalidateTimings } = await import("next/dist/server/lib/incremental-cache/shared-revalidate-timings.js");
1917
+ const sharedRevalidateTimings = new SharedRevalidateTimings(prerenderManifest);
1918
+ sharedRevalidateTimings.set(key, revalidate);
1919
+ }
1896
1920
  }
1897
1921
  } catch {
1898
1922
  }
@@ -1914,16 +1938,35 @@ var NetlifyCacheHandler = class {
1914
1938
  span.addEvent("Cache miss", { key, blobKey });
1915
1939
  return null;
1916
1940
  }
1941
+ const ttl = this.getTTL(blob);
1942
+ if (getRequestContext()?.isBackgroundRevalidation && typeof ttl === "number" && ttl < 0) {
1943
+ span.addEvent("Discarding stale entry due to SWR background revalidation request", {
1944
+ key,
1945
+ blobKey,
1946
+ ttl
1947
+ });
1948
+ getLogger().withFields({
1949
+ ttl,
1950
+ key
1951
+ }).debug(
1952
+ `[NetlifyCacheHandler.get] Discarding stale entry due to SWR background revalidation request: ${key}`
1953
+ );
1954
+ return null;
1955
+ }
1917
1956
  const staleByTags = await this.checkCacheEntryStaleByTags(blob, ctx.tags, ctx.softTags);
1918
1957
  if (staleByTags) {
1919
- span.addEvent("Stale", { staleByTags });
1958
+ span.addEvent("Stale", { staleByTags, key, blobKey, ttl });
1920
1959
  return null;
1921
1960
  }
1922
1961
  this.captureResponseCacheLastModified(blob, key, span);
1923
1962
  this.captureCacheTags(blob.value, key);
1924
1963
  switch (blob.value?.kind) {
1925
1964
  case "FETCH":
1926
- span.addEvent("FETCH", { lastModified: blob.lastModified, revalidate: ctx.revalidate });
1965
+ span.addEvent("FETCH", {
1966
+ lastModified: blob.lastModified,
1967
+ revalidate: ctx.revalidate,
1968
+ ttl
1969
+ });
1927
1970
  return {
1928
1971
  lastModified: blob.lastModified,
1929
1972
  value: blob.value
@@ -1932,7 +1975,9 @@ var NetlifyCacheHandler = class {
1932
1975
  case "APP_ROUTE": {
1933
1976
  span.addEvent(blob.value?.kind, {
1934
1977
  lastModified: blob.lastModified,
1935
- status: blob.value.status
1978
+ status: blob.value.status,
1979
+ revalidate: blob.value.revalidate,
1980
+ ttl
1936
1981
  });
1937
1982
  const valueWithoutRevalidate = this.captureRouteRevalidateAndRemoveFromObject(blob.value);
1938
1983
  return {
@@ -1945,18 +1990,22 @@ var NetlifyCacheHandler = class {
1945
1990
  }
1946
1991
  case "PAGE":
1947
1992
  case "PAGES": {
1948
- span.addEvent(blob.value?.kind, { lastModified: blob.lastModified });
1949
1993
  const { revalidate, ...restOfPageValue } = blob.value;
1950
- await this.injectEntryToPrerenderManifest(key, revalidate);
1994
+ const requestContext = getRequestContext();
1995
+ if (requestContext) {
1996
+ requestContext.pageHandlerRevalidate = revalidate;
1997
+ }
1998
+ span.addEvent(blob.value?.kind, { lastModified: blob.lastModified, revalidate, ttl });
1999
+ await this.injectEntryToPrerenderManifest(key, blob.value);
1951
2000
  return {
1952
2001
  lastModified: blob.lastModified,
1953
2002
  value: restOfPageValue
1954
2003
  };
1955
2004
  }
1956
2005
  case "APP_PAGE": {
1957
- span.addEvent(blob.value?.kind, { lastModified: blob.lastModified });
1958
2006
  const { revalidate, rscData, ...restOfPageValue } = blob.value;
1959
- await this.injectEntryToPrerenderManifest(key, revalidate);
2007
+ span.addEvent(blob.value?.kind, { lastModified: blob.lastModified, revalidate, ttl });
2008
+ await this.injectEntryToPrerenderManifest(key, blob.value);
1960
2009
  return {
1961
2010
  lastModified: blob.lastModified,
1962
2011
  value: {
@@ -1978,20 +2027,23 @@ var NetlifyCacheHandler = class {
1978
2027
  if (isCachedRouteValue(data)) {
1979
2028
  return {
1980
2029
  ...data,
1981
- revalidate: context2.revalidate,
2030
+ revalidate: context2.revalidate ?? context2.cacheControl?.revalidate,
2031
+ cacheControl: context2.cacheControl,
1982
2032
  body: data.body.toString("base64")
1983
2033
  };
1984
2034
  }
1985
2035
  if (isCachedPageValue(data)) {
1986
2036
  return {
1987
2037
  ...data,
1988
- revalidate: context2.revalidate
2038
+ revalidate: context2.revalidate ?? context2.cacheControl?.revalidate,
2039
+ cacheControl: context2.cacheControl
1989
2040
  };
1990
2041
  }
1991
2042
  if (data?.kind === "APP_PAGE") {
1992
2043
  return {
1993
2044
  ...data,
1994
- revalidate: context2.revalidate,
2045
+ revalidate: context2.revalidate ?? context2.cacheControl?.revalidate,
2046
+ cacheControl: context2.cacheControl,
1995
2047
  rscData: data.rscData?.toString("base64")
1996
2048
  };
1997
2049
  }
@@ -123,7 +123,14 @@ init_internal();
123
123
  // src/run/handlers/request-context.cts
124
124
  function createRequestContext(request, context) {
125
125
  const backgroundWorkPromises = [];
126
+ const isDebugRequest = request?.headers.has("x-nf-debug-logging") || request?.headers.has("x-next-debug-logging");
127
+ const logger = systemLogger.withLogLevel(isDebugRequest ? LogLevel.Debug : LogLevel.Log);
128
+ const isBackgroundRevalidation = request?.headers.get("netlify-invocation-source") === "background-revalidation";
129
+ if (isBackgroundRevalidation) {
130
+ logger.debug("[NetlifyNextRuntime] Background revalidation request");
131
+ }
126
132
  return {
133
+ isBackgroundRevalidation,
127
134
  captureServerTiming: request?.headers.has("x-next-debug-logging") ?? false,
128
135
  trackBackgroundWork: (promise) => {
129
136
  if (context?.waitUntil) {
@@ -135,9 +142,7 @@ function createRequestContext(request, context) {
135
142
  get backgroundWorkPromise() {
136
143
  return Promise.allSettled(backgroundWorkPromises);
137
144
  },
138
- logger: systemLogger.withLogLevel(
139
- request?.headers.has("x-nf-debug-logging") || request?.headers.has("x-next-debug-logging") ? LogLevel.Debug : LogLevel.Log
140
- )
145
+ logger
141
146
  };
142
147
  }
143
148
  var REQUEST_CONTEXT_GLOBAL_KEY = Symbol.for("nf-request-context-async-local-storage");
@@ -6,26 +6,25 @@
6
6
 
7
7
  import {
8
8
  getTracer
9
- } from "../../esm-chunks/chunk-NEZW7TGG.js";
10
- import {
11
- createRequestContext,
12
- getLogger,
13
- getRequestContext
14
- } from "../../esm-chunks/chunk-LVXJQ2G2.js";
9
+ } from "../../esm-chunks/chunk-WHUPSPWV.js";
15
10
  import {
16
11
  adjustDateHeader,
17
12
  setCacheControlHeaders,
18
13
  setCacheStatusHeader,
19
14
  setCacheTagsHeaders,
20
15
  setVaryHeaders
21
- } from "../../esm-chunks/chunk-BEIUVQZK.js";
22
- import "../../esm-chunks/chunk-K4RDUZYO.js";
16
+ } from "../../esm-chunks/chunk-HXVWGXWM.js";
17
+ import "../../esm-chunks/chunk-RYJYZQ4X.js";
18
+ import {
19
+ getLogger,
20
+ getRequestContext
21
+ } from "../../esm-chunks/chunk-SGXRYMYQ.js";
23
22
  import {
24
23
  nextResponseProxy
25
24
  } from "../../esm-chunks/chunk-XS27YRA5.js";
25
+ import "../../esm-chunks/chunk-TYCYFZ22.js";
26
26
  import "../../esm-chunks/chunk-5QSXBV7L.js";
27
27
  import "../../esm-chunks/chunk-GNGHTHMQ.js";
28
- import "../../esm-chunks/chunk-TYCYFZ22.js";
29
28
  import {
30
29
  __commonJS,
31
30
  __toESM
@@ -3124,7 +3123,7 @@ function setupWaitUntil() {
3124
3123
  }
3125
3124
 
3126
3125
  // src/run/handlers/server.ts
3127
- var nextImportPromise = import("../../esm-chunks/next-H2VMRX5P.js");
3126
+ var nextImportPromise = import("../../esm-chunks/next-LDOXJ7XH.js");
3128
3127
  setupWaitUntil();
3129
3128
  var nextHandler;
3130
3129
  var nextConfig;
@@ -3141,7 +3140,7 @@ var disableFaultyTransferEncodingHandling = (res) => {
3141
3140
  return originalStoreHeader.call(this, firstLine, headers);
3142
3141
  };
3143
3142
  };
3144
- var server_default = async (request) => {
3143
+ var server_default = async (request, _context, topLevelSpan, requestContext) => {
3145
3144
  const tracer = getTracer();
3146
3145
  if (!nextHandler) {
3147
3146
  await tracer.withActiveSpan("initialize next server", async () => {
@@ -3171,7 +3170,6 @@ var server_default = async (request) => {
3171
3170
  }
3172
3171
  });
3173
3172
  disableFaultyTransferEncodingHandling(res);
3174
- const requestContext = getRequestContext() ?? createRequestContext();
3175
3173
  const resProxy = nextResponseProxy(res, requestContext);
3176
3174
  const nextHandlerPromise = nextHandler(req, resProxy).catch((error) => {
3177
3175
  getLogger().withError(error).error("next handler error");
@@ -3182,24 +3180,40 @@ var server_default = async (request) => {
3182
3180
  });
3183
3181
  const response = await toComputeResponse(resProxy);
3184
3182
  if (requestContext.responseCacheKey) {
3185
- span.setAttribute("responseCacheKey", requestContext.responseCacheKey);
3183
+ topLevelSpan.setAttribute("responseCacheKey", requestContext.responseCacheKey);
3184
+ }
3185
+ const nextCache = response.headers.get("x-nextjs-cache");
3186
+ const isServedFromNextCache = nextCache === "HIT" || nextCache === "STALE";
3187
+ topLevelSpan.setAttributes({
3188
+ "x-nextjs-cache": nextCache ?? void 0,
3189
+ isServedFromNextCache
3190
+ });
3191
+ if (isServedFromNextCache) {
3192
+ await adjustDateHeader({
3193
+ headers: response.headers,
3194
+ request,
3195
+ span,
3196
+ tracer,
3197
+ requestContext
3198
+ });
3186
3199
  }
3187
- await adjustDateHeader({ headers: response.headers, request, span, tracer, requestContext });
3188
- setCacheControlHeaders(response, request, requestContext);
3200
+ setCacheControlHeaders(response, request, requestContext, nextConfig);
3189
3201
  setCacheTagsHeaders(response.headers, requestContext);
3190
3202
  setVaryHeaders(response.headers, request, nextConfig);
3191
- setCacheStatusHeader(response.headers);
3192
- if (response.status > 300 && response.status < 400 || response.status >= 500) {
3193
- const body = await response.text();
3194
- return new Response(body || null, response);
3203
+ setCacheStatusHeader(response.headers, nextCache);
3204
+ async function waitForBackgroundWork() {
3205
+ await nextHandlerPromise;
3206
+ res.emit("close");
3207
+ await requestContext.backgroundWorkPromise;
3195
3208
  }
3196
3209
  const keepOpenUntilNextFullyRendered = new TransformStream({
3197
3210
  async flush() {
3198
- await nextHandlerPromise;
3199
- res.emit("close");
3200
- await requestContext.backgroundWorkPromise;
3211
+ await waitForBackgroundWork();
3201
3212
  }
3202
3213
  });
3214
+ if (!response.body) {
3215
+ await waitForBackgroundWork();
3216
+ }
3203
3217
  return new Response(response.body?.pipeThrough(keepOpenUntilNextFullyRendered), response);
3204
3218
  });
3205
3219
  };
@@ -6,7 +6,7 @@
6
6
 
7
7
  import {
8
8
  getLogger
9
- } from "../../esm-chunks/chunk-LVXJQ2G2.js";
9
+ } from "../../esm-chunks/chunk-SGXRYMYQ.js";
10
10
  import {
11
11
  esm_exports,
12
12
  init_esm
@@ -68868,7 +68868,7 @@ var import_sdk_trace_node = __toESM(require_src20(), 1);
68868
68868
  var import_semantic_conventions = __toESM(require_src(), 1);
68869
68869
  var {
68870
68870
  default: { version, name }
68871
- } = await import("../../esm-chunks/package-MI2MBCHL.js");
68871
+ } = await import("../../esm-chunks/package-F536DQ6H.js");
68872
68872
  var sdk = new import_sdk_node.NodeSDK({
68873
68873
  resource: new import_resources.Resource({
68874
68874
  [import_semantic_conventions.SEMRESATTRS_SERVICE_NAME]: name,
@@ -10,8 +10,9 @@ import {
10
10
  setCacheStatusHeader,
11
11
  setCacheTagsHeaders,
12
12
  setVaryHeaders
13
- } from "../esm-chunks/chunk-BEIUVQZK.js";
14
- import "../esm-chunks/chunk-K4RDUZYO.js";
13
+ } from "../esm-chunks/chunk-HXVWGXWM.js";
14
+ import "../esm-chunks/chunk-RYJYZQ4X.js";
15
+ import "../esm-chunks/chunk-SGXRYMYQ.js";
15
16
  import "../esm-chunks/chunk-TYCYFZ22.js";
16
17
  import "../esm-chunks/chunk-OEQOKJGE.js";
17
18
  export {
package/dist/run/next.cjs CHANGED
@@ -2098,6 +2098,7 @@ var getDeployStore = (input = {}) => {
2098
2098
  // src/run/regional-blob-store.cts
2099
2099
  var fetchBeforeNextPatchedIt = globalThis.fetch;
2100
2100
  var getRegionalBlobStore = (args = {}) => {
2101
+ console.log("Blob store fetch patched", fetchBeforeNextPatchedIt.__nextPatched);
2101
2102
  return getDeployStore({
2102
2103
  ...args,
2103
2104
  fetch: fetchBeforeNextPatchedIt,
@@ -616,6 +616,7 @@ var getDeployStore = (input = {}) => {
616
616
  // src/run/regional-blob-store.cts
617
617
  var fetchBeforeNextPatchedIt = globalThis.fetch;
618
618
  var getRegionalBlobStore = (args = {}) => {
619
+ console.log("Blob store fetch patched", fetchBeforeNextPatchedIt.__nextPatched);
619
620
  return getDeployStore({
620
621
  ...args,
621
622
  fetch: fetchBeforeNextPatchedIt,
@@ -2,7 +2,6 @@
2
2
  // It acts as a seed that populates the `vendor/` directory and should not be
3
3
  // imported directly.
4
4
 
5
- import 'https://deno.land/std@0.175.0/encoding/base64.ts'
6
5
  import 'https://deno.land/std@0.175.0/http/cookie.ts'
7
6
  import 'https://deno.land/std@0.175.0/node/buffer.ts'
8
7
  import 'https://deno.land/std@0.175.0/node/events.ts'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-nextjs",
3
- "version": "5.9.3",
3
+ "version": "5.10.0-fetch-patch-logs",
4
4
  "description": "Run Next.js seamlessly on Netlify",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",