@netlify/plugin-nextjs 5.0.0-beta.8 → 5.0.0-beta.9

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 (33) hide show
  1. package/README.md +23 -0
  2. package/dist/build/cache.js +1 -1
  3. package/dist/build/content/prerendered.js +2 -1
  4. package/dist/build/content/server.js +2 -1
  5. package/dist/build/functions/edge.js +2 -3
  6. package/dist/build/functions/server.js +4 -4
  7. package/dist/build/image-cdn.js +1 -1
  8. package/dist/build/plugin-context.js +1 -2
  9. package/dist/build/templates/handler-monorepo.tmpl.js +33 -30
  10. package/dist/build/templates/handler.tmpl.js +31 -25
  11. package/dist/esm-chunks/{chunk-YMFYCTRI.js → chunk-4J4A5OE2.js} +23 -3
  12. package/dist/esm-chunks/{chunk-XFDUV7DP.js → chunk-67EWAGDQ.js} +29 -20
  13. package/dist/esm-chunks/chunk-72ZI2IVI.js +36 -0
  14. package/dist/esm-chunks/{chunk-N23TUUXK.js → chunk-AU7XDLZH.js} +58 -26
  15. package/dist/esm-chunks/{chunk-IZ2AVCVF.js → chunk-DWC6JSN7.js} +48 -24
  16. package/dist/esm-chunks/{chunk-XIP2W57K.js → chunk-NL5YH5N6.js} +5 -4
  17. package/dist/esm-chunks/{chunk-WSPFUAK4.js → chunk-T6AZTXZF.js} +6 -6
  18. package/dist/esm-chunks/{chunk-EPSI5TTB.js → chunk-TOK7TKP3.js} +1 -1
  19. package/dist/esm-chunks/{chunk-K233JI4O.js → chunk-UYKENJEU.js} +4 -2
  20. package/dist/esm-chunks/{chunk-ETPYUOBQ.js → chunk-YIE34LVX.js} +5 -5
  21. package/dist/esm-chunks/{package-2CI3IXK3.js → package-6DFWHFJA.js} +8 -6
  22. package/dist/index.js +12 -13
  23. package/dist/run/config.js +2 -2
  24. package/dist/run/constants.js +5 -3
  25. package/dist/run/handlers/cache.cjs +155 -79
  26. package/dist/run/handlers/request-context.cjs +56 -0
  27. package/dist/run/handlers/server.js +30 -16
  28. package/dist/run/handlers/tracing.js +1 -1
  29. package/dist/run/headers.js +3 -3
  30. package/edge-runtime/lib/response.ts +24 -20
  31. package/edge-runtime/lib/util.ts +6 -1
  32. package/package.json +1 -1
  33. package/dist/esm-chunks/chunk-XA2CZH5Y.js +0 -32
package/README.md CHANGED
@@ -3,6 +3,29 @@
3
3
  Next.js is supported natively on Netlify, and in most cases you will not need to install or
4
4
  configure anything. This repo includes the packages used to support Next.js on Netlify.
5
5
 
6
+ ## Lambda Folder structure:
7
+
8
+ For a simple next.js app
9
+
10
+ ```
11
+ /___netlify-server-handler
12
+ ├── .netlify
13
+ │ ├── tags-manifest.json
14
+ │ └── dist // the compiled runtime code
15
+ │ └── run
16
+ │ ├── handlers
17
+ │ │ ├── server.js
18
+ │ │ └── cache.cjs
19
+ │ └── next.cjs
20
+ ├── .next // or distDir name from the next.config.js
21
+ │ └── // content from standalone
22
+ ├── run-config.json // the config object from the required-server-files.json
23
+ ├── node_modules
24
+ ├── ___netlify-server-handler.json
25
+ ├── ___netlify-server-handler.mjs
26
+ └── package.json
27
+ ```
28
+
6
29
  ## Testing
7
30
 
8
31
  ### Integration testing
@@ -7,7 +7,7 @@
7
7
  import {
8
8
  restoreBuildCache,
9
9
  saveBuildCache
10
- } from "../esm-chunks/chunk-XA2CZH5Y.js";
10
+ } from "../esm-chunks/chunk-72ZI2IVI.js";
11
11
  import "../esm-chunks/chunk-5JVNISGM.js";
12
12
  export {
13
13
  restoreBuildCache,
@@ -7,8 +7,9 @@
7
7
  import {
8
8
  copyFetchContent,
9
9
  copyPrerenderedContent
10
- } from "../../esm-chunks/chunk-XFDUV7DP.js";
10
+ } from "../../esm-chunks/chunk-67EWAGDQ.js";
11
11
  import "../../esm-chunks/chunk-VZNKO4OO.js";
12
+ import "../../esm-chunks/chunk-TYCYFZ22.js";
12
13
  import "../../esm-chunks/chunk-5JVNISGM.js";
13
14
  export {
14
15
  copyFetchContent,
@@ -8,8 +8,9 @@ import {
8
8
  copyNextDependencies,
9
9
  copyNextServerCode,
10
10
  writeTagsManifest
11
- } from "../../esm-chunks/chunk-YMFYCTRI.js";
11
+ } from "../../esm-chunks/chunk-4J4A5OE2.js";
12
12
  import "../../esm-chunks/chunk-VZNKO4OO.js";
13
+ import "../../esm-chunks/chunk-UYKENJEU.js";
13
14
  import "../../esm-chunks/chunk-5JVNISGM.js";
14
15
  export {
15
16
  copyNextDependencies,
@@ -6,10 +6,9 @@
6
6
 
7
7
  import {
8
8
  createEdgeHandlers
9
- } from "../../esm-chunks/chunk-ETPYUOBQ.js";
10
- import "../../esm-chunks/chunk-IZ2AVCVF.js";
9
+ } from "../../esm-chunks/chunk-YIE34LVX.js";
11
10
  import "../../esm-chunks/chunk-VZNKO4OO.js";
12
- import "../../esm-chunks/chunk-TYCYFZ22.js";
11
+ import "../../esm-chunks/chunk-DWC6JSN7.js";
13
12
  import "../../esm-chunks/chunk-5JVNISGM.js";
14
13
  export {
15
14
  createEdgeHandlers
@@ -6,11 +6,11 @@
6
6
 
7
7
  import {
8
8
  createServerHandler
9
- } from "../../esm-chunks/chunk-WSPFUAK4.js";
10
- import "../../esm-chunks/chunk-YMFYCTRI.js";
11
- import "../../esm-chunks/chunk-IZ2AVCVF.js";
9
+ } from "../../esm-chunks/chunk-T6AZTXZF.js";
10
+ import "../../esm-chunks/chunk-4J4A5OE2.js";
12
11
  import "../../esm-chunks/chunk-VZNKO4OO.js";
13
- import "../../esm-chunks/chunk-TYCYFZ22.js";
12
+ import "../../esm-chunks/chunk-DWC6JSN7.js";
13
+ import "../../esm-chunks/chunk-UYKENJEU.js";
14
14
  import "../../esm-chunks/chunk-5JVNISGM.js";
15
15
  export {
16
16
  createServerHandler
@@ -6,7 +6,7 @@
6
6
 
7
7
  import {
8
8
  setImageConfig
9
- } from "../esm-chunks/chunk-EPSI5TTB.js";
9
+ } from "../esm-chunks/chunk-TOK7TKP3.js";
10
10
  import "../esm-chunks/chunk-5JVNISGM.js";
11
11
  export {
12
12
  setImageConfig
@@ -8,8 +8,7 @@ import {
8
8
  EDGE_HANDLER_NAME,
9
9
  PluginContext,
10
10
  SERVER_HANDLER_NAME
11
- } from "../esm-chunks/chunk-IZ2AVCVF.js";
12
- import "../esm-chunks/chunk-TYCYFZ22.js";
11
+ } from "../esm-chunks/chunk-DWC6JSN7.js";
13
12
  import "../esm-chunks/chunk-5JVNISGM.js";
14
13
  export {
15
14
  EDGE_HANDLER_NAME,
@@ -1,4 +1,4 @@
1
- import tracing, { trace } from '{{cwd}}/dist/run/handlers/tracing.js'
1
+ import tracing, { trace } from '{{cwd}}/.netlify/dist/run/handlers/tracing.js'
2
2
 
3
3
  process.chdir('{{cwd}}')
4
4
 
@@ -8,36 +8,39 @@ export default async function (req, context) {
8
8
  tracing.start()
9
9
  }
10
10
 
11
- return trace
12
- .getTracer('Next.js Runtime')
13
- .startActiveSpan('Next.js Server Handler', async (span) => {
14
- try {
15
- span.setAttributes({
16
- 'account.id': context.account.id,
17
- 'deploy.id': context.deploy.id,
18
- 'request.id': context.requestId,
19
- 'site.id': context.site.id,
20
- 'http.method': req.method,
21
- 'http.target': req.url,
22
- monorepo: true,
23
- cwd: '{{cwd}}',
24
- })
25
- if (!cachedHandler) {
26
- const { default: handler } = await import('./{{nextServerHandler}}')
27
- cachedHandler = handler
28
- }
29
- const response = await cachedHandler(req, context)
30
- span.setAttributes({
31
- 'http.status_code': response.status,
32
- })
33
- return response
34
- } catch (error) {
35
- span.recordException(error)
36
- throw error
37
- } finally {
38
- span.end()
11
+ /** @type {import('@opentelemetry/api').Tracer} */
12
+ const tracer = trace.getTracer('Next.js Runtime')
13
+ return tracer.startActiveSpan('Next.js Server Handler', async (span) => {
14
+ try {
15
+ span.setAttributes({
16
+ 'account.id': context.account.id,
17
+ 'deploy.id': context.deploy.id,
18
+ 'request.id': context.requestId,
19
+ 'site.id': context.site.id,
20
+ 'http.method': req.method,
21
+ 'http.target': req.url,
22
+ monorepo: true,
23
+ cwd: '{{cwd}}',
24
+ })
25
+ if (!cachedHandler) {
26
+ const { default: handler } = await import('{{nextServerHandler}}')
27
+ cachedHandler = handler
39
28
  }
40
- })
29
+ const response = await cachedHandler(req, context)
30
+ span.setAttributes({
31
+ 'http.status_code': response.status,
32
+ })
33
+ return response
34
+ } catch (error) {
35
+ span.recordException(error)
36
+ if (error instanceof Error) {
37
+ span.addEvent({ name: error.name, message: error.message })
38
+ }
39
+ throw error
40
+ } finally {
41
+ span.end()
42
+ }
43
+ })
41
44
  }
42
45
 
43
46
  export const config = {
@@ -1,34 +1,40 @@
1
- import serverHandler from './dist/run/handlers/server.js'
2
- import tracing, { trace } from './dist/run/handlers/tracing.js'
1
+ import serverHandler from './.netlify/dist/run/handlers/server.js'
2
+ import tracing, { trace } from './.netlify/dist/run/handlers/tracing.js'
3
3
 
4
4
  export default async function handler(req, context) {
5
5
  if (process.env.NETLIFY_OTLP_TRACE_EXPORTER_URL) {
6
6
  tracing.start()
7
7
  }
8
- return trace
9
- .getTracer('Next.js Runtime')
10
- .startActiveSpan('Next.js Server Handler', async (span) => {
11
- try {
12
- span.setAttributes({
13
- 'account.id': context.account.id,
14
- 'deploy.id': context.deploy.id,
15
- 'request.id': context.requestId,
16
- 'site.id': context.site.id,
17
- 'http.method': req.method,
18
- 'http.target': req.url,
19
- })
20
- const response = await serverHandler(req, context)
21
- span.setAttributes({
22
- 'http.status_code': response.status,
23
- })
24
- return response
25
- } catch (error) {
26
- span.recordException(error)
27
- throw error
28
- } finally {
29
- span.end()
8
+
9
+ /** @type {import('@opentelemetry/api').Tracer} */
10
+ const tracer = trace.getTracer('Next.js Runtime')
11
+ return tracer.startActiveSpan('Next.js Server Handler', async (span) => {
12
+ try {
13
+ span.setAttributes({
14
+ 'account.id': context.account.id,
15
+ 'deploy.id': context.deploy.id,
16
+ 'request.id': context.requestId,
17
+ 'site.id': context.site.id,
18
+ 'http.method': req.method,
19
+ 'http.target': req.url,
20
+ monorepo: false,
21
+ cwd: process.cwd(),
22
+ })
23
+ const response = await serverHandler(req, context)
24
+ span.setAttributes({
25
+ 'http.status_code': response.status,
26
+ })
27
+ return response
28
+ } catch (error) {
29
+ span.recordException(error)
30
+ if (error instanceof Error) {
31
+ span.addEvent({ name: error.name, message: error.message })
30
32
  }
31
- })
33
+ throw error
34
+ } finally {
35
+ span.end()
36
+ }
37
+ })
32
38
  }
33
39
 
34
40
  export const config = {
@@ -7,6 +7,9 @@
7
7
  import {
8
8
  require_out
9
9
  } from "./chunk-VZNKO4OO.js";
10
+ import {
11
+ RUN_CONFIG
12
+ } from "./chunk-UYKENJEU.js";
10
13
  import {
11
14
  __toESM
12
15
  } from "./chunk-5JVNISGM.js";
@@ -18,8 +21,25 @@ import { cp, mkdir, readFile, readdir, readlink, symlink, writeFile } from "node
18
21
  import { createRequire } from "node:module";
19
22
  import { dirname, join, resolve } from "node:path";
20
23
  var copyNextServerCode = async (ctx) => {
21
- const srcDir = join(ctx.standaloneDir, ".next");
22
- const destDir = join(ctx.serverHandlerDir, ".next");
24
+ const reqServerFilesPath = join(
25
+ ctx.standaloneRootDir,
26
+ ctx.relPublishDir,
27
+ "required-server-files.json"
28
+ );
29
+ const reqServerFiles = JSON.parse(await readFile(reqServerFilesPath, "utf-8"));
30
+ if (ctx.distDir.replace(new RegExp(`^${ctx.packagePath}/?`), "") !== reqServerFiles.config.distDir) {
31
+ reqServerFiles.config.distDir = ctx.nextDistDir;
32
+ await writeFile(reqServerFilesPath, JSON.stringify(reqServerFiles));
33
+ }
34
+ await mkdir(ctx.serverHandlerDir, { recursive: true });
35
+ await writeFile(
36
+ join(ctx.serverHandlerDir, RUN_CONFIG),
37
+ JSON.stringify(reqServerFiles.config),
38
+ "utf-8"
39
+ );
40
+ const srcDir = join(ctx.standaloneDir, ctx.nextDistDir);
41
+ const nextFolder = ctx.distDir === ctx.buildConfig.distDir ? ctx.distDir : ctx.nextDistDir;
42
+ const destDir = join(ctx.serverHandlerDir, nextFolder);
23
43
  const paths = await (0, import_fast_glob.default)(
24
44
  [`*`, `server/*`, `server/chunks/*`, `server/edge-chunks/*`, `server/+(app|pages)/**/*.js`],
25
45
  {
@@ -67,7 +87,7 @@ async function recreateNodeModuleSymlinks(src, dest, org) {
67
87
  var copyNextDependencies = async (ctx) => {
68
88
  const entries = await readdir(ctx.standaloneDir);
69
89
  const promises = entries.map(async (entry) => {
70
- if (entry === "package.json" || entry === ".next") {
90
+ if (entry === "package.json" || entry === ctx.nextDistDir) {
71
91
  return;
72
92
  }
73
93
  const src = join(ctx.standaloneDir, entry);
@@ -7,6 +7,9 @@
7
7
  import {
8
8
  require_out
9
9
  } from "./chunk-VZNKO4OO.js";
10
+ import {
11
+ encodeBlobKey
12
+ } from "./chunk-TYCYFZ22.js";
10
13
  import {
11
14
  __toESM
12
15
  } from "./chunk-5JVNISGM.js";
@@ -14,13 +17,25 @@ import {
14
17
  // src/build/content/prerendered.ts
15
18
  var import_fast_glob = __toESM(require_out(), 1);
16
19
  import { existsSync } from "node:fs";
17
- import { readFile } from "node:fs/promises";
18
- import { join } from "node:path";
20
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
21
+ import { dirname, join } from "node:path";
22
+ var writeCacheEntry = async (route, value, lastModified, ctx) => {
23
+ const path = join(ctx.blobDir, await encodeBlobKey(route));
24
+ const entry = JSON.stringify({
25
+ lastModified,
26
+ value
27
+ });
28
+ await mkdir(dirname(path), { recursive: true });
29
+ await writeFile(path, entry, "utf-8");
30
+ };
19
31
  var routeToFilePath = (path) => path === "/" ? "/index" : path;
20
32
  var buildPagesCacheValue = async (path) => ({
21
33
  kind: "PAGE",
22
34
  html: await readFile(`${path}.html`, "utf-8"),
23
- pageData: JSON.parse(await readFile(`${path}.json`, "utf-8"))
35
+ pageData: JSON.parse(await readFile(`${path}.json`, "utf-8")),
36
+ postponed: void 0,
37
+ headers: void 0,
38
+ status: void 0
24
39
  });
25
40
  var buildAppCacheValue = async (path) => ({
26
41
  kind: "PAGE",
@@ -42,39 +57,32 @@ var copyPrerenderedContent = async (ctx) => {
42
57
  const manifest = await ctx.getPrerenderManifest();
43
58
  await Promise.all(
44
59
  Object.entries(manifest.routes).map(async ([route, meta]) => {
60
+ const lastModified = meta.initialRevalidateSeconds ? Date.now() - 31536e6 : Date.now();
45
61
  const key = routeToFilePath(route);
46
62
  let value;
47
- let path;
48
63
  switch (true) {
49
64
  case (meta.dataRoute?.endsWith("/default.rsc") && !existsSync(join(ctx.publishDir, "server/app", `${key}.html`))):
50
65
  return;
51
66
  case meta.dataRoute?.endsWith(".json"):
52
- path = join(ctx.publishDir, "server/pages", key);
53
- value = await buildPagesCacheValue(path);
67
+ value = await buildPagesCacheValue(join(ctx.publishDir, "server/pages", key));
54
68
  break;
55
69
  case meta.dataRoute?.endsWith(".rsc"):
56
- path = join(ctx.publishDir, "server/app", key);
57
- value = await buildAppCacheValue(path);
70
+ value = await buildAppCacheValue(join(ctx.publishDir, "server/app", key));
58
71
  break;
59
72
  case meta.dataRoute === null:
60
- path = join(ctx.publishDir, "server/app", key);
61
- value = await buildRouteCacheValue(path);
73
+ value = await buildRouteCacheValue(join(ctx.publishDir, "server/app", key));
62
74
  break;
63
75
  default:
64
76
  throw new Error(`Unrecognized content: ${route}`);
65
77
  }
66
- await ctx.writeCacheEntry(
67
- key,
68
- value,
69
- meta.dataRoute === null ? `${path}.body` : `${path}.html`
70
- );
78
+ await writeCacheEntry(key, value, lastModified, ctx);
71
79
  })
72
80
  );
73
81
  if (existsSync(join(ctx.publishDir, `server/app/_not-found.html`))) {
82
+ const lastModified = Date.now();
74
83
  const key = "/404";
75
- const path = join(ctx.publishDir, "server/app/_not-found");
76
- const value = await buildAppCacheValue(path);
77
- await ctx.writeCacheEntry(key, value, `${path}.html`);
84
+ const value = await buildAppCacheValue(join(ctx.publishDir, "server/app/_not-found"));
85
+ await writeCacheEntry(key, value, lastModified, ctx);
78
86
  }
79
87
  } catch (error) {
80
88
  ctx.failBuild("Failed assembling prerendered content for upload", error);
@@ -82,15 +90,16 @@ var copyPrerenderedContent = async (ctx) => {
82
90
  };
83
91
  var copyFetchContent = async (ctx) => {
84
92
  try {
85
- const paths = await (0, import_fast_glob.default)(["!(*.*)"], {
93
+ const paths = await (0, import_fast_glob.glob)(["!(*.*)"], {
86
94
  cwd: join(ctx.publishDir, "cache/fetch-cache"),
87
95
  extglob: true
88
96
  });
89
97
  await Promise.all(
90
98
  paths.map(async (key) => {
99
+ const lastModified = Date.now() - 31536e6;
91
100
  const path = join(ctx.publishDir, "cache/fetch-cache", key);
92
101
  const value = await buildFetchCacheValue(path);
93
- await ctx.writeCacheEntry(key, value, path);
102
+ await writeCacheEntry(key, value, lastModified, ctx);
94
103
  })
95
104
  );
96
105
  } catch (error) {
@@ -0,0 +1,36 @@
1
+
2
+ var require = await (async () => {
3
+ var { createRequire } = await import("node:module");
4
+ return createRequire(import.meta.url);
5
+ })();
6
+
7
+
8
+ // src/build/cache.ts
9
+ import { existsSync } from "node:fs";
10
+ import { rm } from "node:fs/promises";
11
+ import { join } from "node:path";
12
+ var saveBuildCache = async (ctx) => {
13
+ const { cache } = ctx.utils;
14
+ const cacheDir = join(ctx.publishDir, "cache");
15
+ if (existsSync(cacheDir)) {
16
+ await rm(join(cacheDir, "fetch-cache"), { recursive: true, force: true });
17
+ await cache.save(cacheDir);
18
+ console.log("Next.js cache saved");
19
+ } else {
20
+ console.log("No Next.js cache to save");
21
+ }
22
+ };
23
+ var restoreBuildCache = async (ctx) => {
24
+ const { cache } = ctx.utils;
25
+ const cacheDir = join(ctx.publishDir, "cache");
26
+ if (await cache.restore(cacheDir)) {
27
+ console.log("Next.js cache restored");
28
+ } else {
29
+ console.log("No Next.js cache to restore");
30
+ }
31
+ };
32
+
33
+ export {
34
+ saveBuildCache,
35
+ restoreBuildCache
36
+ };
@@ -536,7 +536,7 @@ var mapHeaderValues = (header, callback) => {
536
536
  };
537
537
  var setVaryHeaders = (headers, request, { basePath, i18n }) => {
538
538
  const netlifyVaryValues = {
539
- headers: [],
539
+ headers: ["x-nextjs-data"],
540
540
  languages: [],
541
541
  cookies: ["__prerender_bypass", "__next_preview_data"]
542
542
  };
@@ -553,7 +553,13 @@ var setVaryHeaders = (headers, request, { basePath, i18n }) => {
553
553
  headers.set(`netlify-vary`, generateNetlifyVaryValues(netlifyVaryValues));
554
554
  };
555
555
  var fetchBeforeNextPatchedIt = globalThis.fetch;
556
- var adjustDateHeader = async (headers, request, span, tracer) => {
556
+ var adjustDateHeader = async ({
557
+ headers,
558
+ request,
559
+ span,
560
+ tracer,
561
+ requestContext
562
+ }) => {
557
563
  const cacheState = headers.get("x-nextjs-cache");
558
564
  const isServedFromCache = cacheState === "HIT" || cacheState === "STALE";
559
565
  span.setAttributes({
@@ -564,22 +570,43 @@ var adjustDateHeader = async (headers, request, span, tracer) => {
564
570
  return;
565
571
  }
566
572
  const key = new URL(request.url).pathname;
567
- const blobKey = await encodeBlobKey(key);
568
- const blobStore = getDeployStore({ fetch: fetchBeforeNextPatchedIt });
569
- const { lastModified } = await tracer.startActiveSpan(
570
- "get cache to calculate date header",
571
- async (getBlobForDateSpan) => {
572
- getBlobForDateSpan.setAttributes({
573
- key,
574
- blobKey
575
- });
576
- const blob = await blobStore.get(blobKey, { type: "json" }) ?? {};
577
- getBlobForDateSpan.addEvent(blob ? "Cache hit" : "Cache miss");
578
- getBlobForDateSpan.end();
579
- return blob;
580
- }
581
- );
573
+ let lastModified;
574
+ if (requestContext.responseCacheGetLastModified) {
575
+ lastModified = requestContext.responseCacheGetLastModified;
576
+ } else {
577
+ span.recordException(
578
+ new Error("lastModified not found in requestContext, falling back to trying blobs")
579
+ );
580
+ span.setAttributes({
581
+ severity: "alert",
582
+ warning: true
583
+ });
584
+ const blobKey = await encodeBlobKey(key);
585
+ const blobStore = getDeployStore({ fetch: fetchBeforeNextPatchedIt, consistency: "strong" });
586
+ lastModified = await tracer.startActiveSpan(
587
+ "get cache to calculate date header",
588
+ async (getBlobForDateSpan) => {
589
+ getBlobForDateSpan.setAttributes({
590
+ key,
591
+ blobKey
592
+ });
593
+ const blob = await blobStore.get(blobKey, { type: "json" }) ?? {};
594
+ getBlobForDateSpan.addEvent(blob ? "Cache hit" : "Cache miss");
595
+ getBlobForDateSpan.end();
596
+ return blob.lastModified;
597
+ }
598
+ );
599
+ }
582
600
  if (!lastModified) {
601
+ span.recordException(
602
+ new Error(
603
+ "lastModified not found in either requestContext or blobs, date header for cached response is not set"
604
+ )
605
+ );
606
+ span.setAttributes({
607
+ severity: "alert",
608
+ warning: true
609
+ });
583
610
  return;
584
611
  }
585
612
  const lastModifiedDate = new Date(lastModified);
@@ -589,29 +616,34 @@ var adjustDateHeader = async (headers, request, span, tracer) => {
589
616
  var setCacheControlHeaders = (headers, request) => {
590
617
  const cacheControl = headers.get("cache-control");
591
618
  if (cacheControl !== null && ["GET", "HEAD"].includes(request.method) && !headers.has("cdn-cache-control") && !headers.has("netlify-cdn-cache-control")) {
592
- const privateCacheControl = omitHeaderValues(cacheControl, [
619
+ const browserCacheControl = omitHeaderValues(cacheControl, [
593
620
  "s-maxage",
594
621
  "stale-while-revalidate"
595
622
  ]);
596
- const sharedCacheControl = mapHeaderValues(
597
- cacheControl,
598
- (value) => value === "stale-while-revalidate" ? "stale-while-revalidate=31536000" : value
623
+ const cdnCacheControl = (
624
+ // if we are serving already stale response, instruct edge to not attempt to cache that response
625
+ headers.get("x-nextjs-cache") === "STALE" ? "public, max-age=0, must-revalidate" : mapHeaderValues(
626
+ cacheControl,
627
+ (value) => value === "stale-while-revalidate" ? "stale-while-revalidate=31536000" : value
628
+ )
599
629
  );
600
- headers.set("cache-control", privateCacheControl || "public, max-age=0, must-revalidate");
601
- headers.set("netlify-cdn-cache-control", sharedCacheControl);
630
+ headers.set("cache-control", browserCacheControl || "public, max-age=0, must-revalidate");
631
+ headers.set("netlify-cdn-cache-control", cdnCacheControl);
602
632
  }
603
633
  };
604
634
  var setCacheTagsHeaders = (headers, request, manifest) => {
605
635
  const path = new URL(request.url).pathname;
606
636
  const tags = manifest[path];
607
- headers.set("cache-tag", tags);
637
+ if (tags !== void 0) {
638
+ headers.set("netlify-cache-tag", tags);
639
+ }
608
640
  };
609
641
  var NEXT_CACHE_TO_CACHE_STATUS = {
610
642
  HIT: `hit`,
611
643
  MISS: `fwd=miss`,
612
644
  STALE: `hit; fwd=stale`
613
645
  };
614
- var handleNextCacheHeader = (headers) => {
646
+ var setCacheStatusHeader = (headers) => {
615
647
  const nextCache = headers.get("x-nextjs-cache");
616
648
  if (typeof nextCache === "string") {
617
649
  if (nextCache in NEXT_CACHE_TO_CACHE_STATUS) {
@@ -627,5 +659,5 @@ export {
627
659
  adjustDateHeader,
628
660
  setCacheControlHeaders,
629
661
  setCacheTagsHeaders,
630
- handleNextCacheHeader
662
+ setCacheStatusHeader
631
663
  };