@opennextjs/aws 3.8.5 → 3.9.1

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.
@@ -102,6 +102,12 @@ export default class Cache {
102
102
  }
103
103
  if (cacheData?.type === "page" || cacheData?.type === "app") {
104
104
  if (globalThis.isNextAfter15 && cacheData?.type === "app") {
105
+ const segmentData = new Map();
106
+ if (cacheData.segmentData) {
107
+ for (const [segmentPath, segmentContent] of Object.entries(cacheData.segmentData ?? {})) {
108
+ segmentData.set(segmentPath, Buffer.from(segmentContent));
109
+ }
110
+ }
105
111
  return {
106
112
  lastModified: _lastModified,
107
113
  value: {
@@ -111,6 +117,7 @@ export default class Cache {
111
117
  status: meta?.status,
112
118
  headers: meta?.headers,
113
119
  postponed: meta?.postponed,
120
+ segmentData,
114
121
  },
115
122
  };
116
123
  }
@@ -22,7 +22,7 @@ export declare const RoutesManifest: {
22
22
  locales: string[];
23
23
  };
24
24
  export declare const ConfigHeaders: import("../../types/next-types").Header[] | undefined;
25
- export declare const PrerenderManifest: import("../../types/next-types").PrerenderManifest;
25
+ export declare const PrerenderManifest: import("../../types/next-types").PrerenderManifest | undefined;
26
26
  export declare const PagesManifest: Record<string, string>;
27
27
  export declare const AppPathsManifestKeys: string[];
28
28
  export declare const MiddlewareManifest: import("../../types/next-types").MiddlewareManifest;
@@ -22,3 +22,5 @@ export const AppPathRoutesManifest =
22
22
  /* @__PURE__ */ loadAppPathRoutesManifest(NEXT_DIR);
23
23
  export const FunctionsConfigManifest =
24
24
  /* @__PURE__ */ loadFunctionsConfigManifest(NEXT_DIR);
25
+ process.env.NEXT_BUILD_ID = BuildId;
26
+ process.env.NEXT_PREVIEW_MODE_ID = PrerenderManifest?.preview?.previewModeId;
@@ -24,7 +24,7 @@ export declare function loadRoutesManifest(nextDir: string): {
24
24
  locales: string[];
25
25
  };
26
26
  export declare function loadConfigHeaders(nextDir: string): import("../../types/next-types").Header[] | undefined;
27
- export declare function loadPrerenderManifest(nextDir: string): PrerenderManifest;
27
+ export declare function loadPrerenderManifest(nextDir: string): PrerenderManifest | undefined;
28
28
  export declare function loadAppPathsManifest(nextDir: string): Record<string, string>;
29
29
  export declare function loadAppPathRoutesManifest(nextDir: string): Record<string, string>;
30
30
  export declare function loadAppPathsManifestKeys(nextDir: string): string[];
@@ -60,6 +60,9 @@ export function loadConfigHeaders(nextDir) {
60
60
  }
61
61
  export function loadPrerenderManifest(nextDir) {
62
62
  const filePath = path.join(nextDir, "prerender-manifest.json");
63
+ if (!fs.existsSync(filePath)) {
64
+ return undefined;
65
+ }
63
66
  const json = fs.readFileSync(filePath, "utf-8");
64
67
  return JSON.parse(json);
65
68
  }
@@ -5,7 +5,9 @@ import { debug } from "../../logger.js";
5
5
  export async function optimizeImage(headers, imageParams, nextConfig, handleRequest) {
6
6
  const { isAbsolute, href } = imageParams;
7
7
  const imageUpstream = isAbsolute
8
- ? await fetchExternalImage(href)
8
+ ? //@ts-expect-error - fetchExternalImage signature has changed in Next.js 16, it has an extra boolean parameter.
9
+ // https://github.com/vercel/next.js/blob/bfe2ab4/packages/next/src/server/image-optimizer.ts#L711
10
+ await fetchExternalImage(href)
9
11
  : await fetchInternalImage(href,
10
12
  // @ts-expect-error - It is supposed to be an IncomingMessage object, but only the headers are used.
11
13
  { headers }, {}, // res object is not necessary as it's not actually used.
@@ -1,12 +1,8 @@
1
- // We load every config here so that they are only loaded once
2
- // and during cold starts
3
- import { BuildId } from "./config/index.js";
4
1
  import { createMainHandler } from "../core/createMainHandler.js";
5
2
  import { setNodeEnv } from "./util.js";
6
3
  // We load every config here so that they are only loaded once
7
4
  // and during cold starts
8
5
  setNodeEnv();
9
- setBuildIdEnv();
10
6
  setNextjsServerWorkingDirectory();
11
7
  // Because next is messing with fetch, we have to make sure that we use an untouched version of fetch
12
8
  globalThis.internalFetch = fetch;
@@ -22,8 +18,3 @@ function setNextjsServerWorkingDirectory() {
22
18
  // See https://opennext.js.org/aws/v2/advanced/workaround#workaround-set-nextserver-working-directory-aws-specific
23
19
  process.chdir(__dirname);
24
20
  }
25
- function setBuildIdEnv() {
26
- // This allows users to access the CloudFront invalidating path when doing on-demand
27
- // invalidations. ie. `/_next/data/${process.env.NEXT_BUILD_ID}/foo.json`
28
- process.env.NEXT_BUILD_ID = BuildId;
29
- }
@@ -10,7 +10,7 @@ interface CopyTracedFilesOptions {
10
10
  export declare function getManifests(nextDir: string): {
11
11
  buildId: string;
12
12
  config: import("../types/next-types.js").NextConfig;
13
- prerenderManifest: import("../types/next-types.js").PrerenderManifest;
13
+ prerenderManifest: import("../types/next-types.js").PrerenderManifest | undefined;
14
14
  pagesManifest: Record<string, string>;
15
15
  appPathsManifest: Record<string, string>;
16
16
  middlewareManifest: import("../types/next-types.js").MiddlewareManifest;
@@ -22,7 +22,7 @@ export declare function copyTracedFiles({ buildOutputPath, packagePath, outputDi
22
22
  manifests: {
23
23
  buildId: string;
24
24
  config: import("../types/next-types.js").NextConfig;
25
- prerenderManifest: import("../types/next-types.js").PrerenderManifest;
25
+ prerenderManifest: import("../types/next-types.js").PrerenderManifest | undefined;
26
26
  pagesManifest: Record<string, string>;
27
27
  appPathsManifest: Record<string, string>;
28
28
  middlewareManifest: import("../types/next-types.js").MiddlewareManifest;
@@ -174,6 +174,7 @@ File ${serverPath} does not exist
174
174
  });
175
175
  // Only files that are actually copied
176
176
  const tracedFiles = [];
177
+ const erroredFiles = [];
177
178
  //Actually copy the files
178
179
  filesToCopy.forEach((to, from) => {
179
180
  // We don't want to copy excluded packages (e.g. sharp)
@@ -202,7 +203,16 @@ File ${serverPath} does not exist
202
203
  }
203
204
  }
204
205
  else {
205
- copyFileSync(from, to);
206
+ // Adding this inside a try-catch to handle errors on Next 16+
207
+ // where some files listed in the .nft.json might not be present in the standalone folder
208
+ // TODO: investigate that further - is it expected?
209
+ try {
210
+ copyFileSync(from, to);
211
+ }
212
+ catch (e) {
213
+ logger.debug("Error copying file:", e);
214
+ erroredFiles.push(to);
215
+ }
206
216
  }
207
217
  });
208
218
  readdirSync(standaloneNextDir)
@@ -242,7 +252,7 @@ File ${serverPath} does not exist
242
252
  const staticFiles = Object.values(pagesManifest);
243
253
  // Then we need to get all fallback: true dynamic routes html
244
254
  const locales = config.i18n?.locales;
245
- Object.values(prerenderManifest.dynamicRoutes).forEach((route) => {
255
+ Object.values(prerenderManifest?.dynamicRoutes ?? {}).forEach((route) => {
246
256
  if (typeof route.fallback === "string") {
247
257
  if (locales) {
248
258
  locales.forEach((locale) => {
@@ -265,7 +275,7 @@ File ${serverPath} does not exist
265
275
  }
266
276
  logger.debug("copyTracedFiles:", Date.now() - tsStart, "ms");
267
277
  return {
268
- tracedFiles,
278
+ tracedFiles: tracedFiles.filter((f) => !erroredFiles.includes(f)),
269
279
  nodePackages,
270
280
  manifests,
271
281
  };
@@ -72,6 +72,10 @@ export function createCacheAssets(options) {
72
72
  const htmlPages = buildHelper.getHtmlPages(dotNextPath);
73
73
  const isFileSkipped = (relativePath) => relativePath.endsWith(".js") ||
74
74
  relativePath.endsWith(".js.nft.json") ||
75
+ // We skip manifest files as well
76
+ relativePath.endsWith("-manifest.json") ||
77
+ // We skip the segment rsc files as they are treated in a different way
78
+ relativePath.endsWith(".segment.rsc") ||
75
79
  (relativePath.endsWith(".html") && htmlPages.has(relativePath));
76
80
  // Merge cache files into a single file
77
81
  const cacheFilesPath = {};
@@ -116,6 +120,14 @@ export function createCacheAssets(options) {
116
120
  logger.warn(`Skipping invalid cache file: ${cacheFilePath}`);
117
121
  return;
118
122
  }
123
+ // If we have a meta file, and it contains segmentPaths, we need to add them to the cache file
124
+ const segments = Array.isArray(cacheFileMeta?.segmentPaths)
125
+ ? Object.fromEntries(cacheFileMeta.segmentPaths.map((segmentPath) => {
126
+ const absoluteSegmentPath = path.join(files.meta.replace(/\.meta$/, ".segments"), `${segmentPath}.segment.rsc`);
127
+ const segmentContent = fs.readFileSync(absoluteSegmentPath, "utf8");
128
+ return [segmentPath, segmentContent];
129
+ }))
130
+ : {};
119
131
  const cacheFileContent = {
120
132
  type: files.body ? "route" : files.json ? "page" : "app",
121
133
  meta: cacheFileMeta,
@@ -129,6 +141,7 @@ export function createCacheAssets(options) {
129
141
  ? "base64"
130
142
  : "utf8")
131
143
  : undefined,
144
+ segmentData: Object.keys(segments).length > 0 ? segments : undefined,
132
145
  };
133
146
  // Ensure directory exists before writing
134
147
  fs.mkdirSync(path.dirname(cacheFilePath), { recursive: true });
@@ -3,7 +3,7 @@ import { NextConfig, PrerenderManifest } from "../../adapters/config/index";
3
3
  import { emptyReadableStream, toReadableStream } from "../../utils/stream";
4
4
  import { isBinaryContentType } from "../../utils/binary";
5
5
  import { getTagsFromValue, hasBeenRevalidated } from "../../utils/cache";
6
- import { debug } from "../../adapters/logger";
6
+ import { debug, error } from "../../adapters/logger";
7
7
  import { localizePath } from "./i18n";
8
8
  import { generateMessageGroupId } from "./queue";
9
9
  const CACHE_ONE_YEAR = 60 * 60 * 24 * 365;
@@ -18,9 +18,12 @@ const CACHE_ONE_MONTH = 60 * 60 * 24 * 30;
18
18
  * Also see this PR: https://github.com/vercel/next.js/pull/79426
19
19
  */
20
20
  const VARY_HEADER = "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch, Next-Url";
21
+ const NEXT_SEGMENT_PREFETCH_HEADER = "next-router-segment-prefetch";
22
+ const NEXT_PRERENDER_HEADER = "x-nextjs-prerender";
23
+ const NEXT_POSTPONED_HEADER = "x-nextjs-postponed";
21
24
  async function computeCacheControl(path, body, host, revalidate, lastModified) {
22
25
  let finalRevalidate = CACHE_ONE_YEAR;
23
- const existingRoute = Object.entries(PrerenderManifest.routes).find((p) => p[0] === path)?.[1];
26
+ const existingRoute = Object.entries(PrerenderManifest?.routes ?? {}).find((p) => p[0] === path)?.[1];
24
27
  if (revalidate === undefined && existingRoute) {
25
28
  finalRevalidate =
26
29
  existingRoute.initialRevalidateSeconds === false
@@ -81,24 +84,54 @@ async function computeCacheControl(path, body, host, revalidate, lastModified) {
81
84
  etag,
82
85
  };
83
86
  }
87
+ function getBodyForAppRouter(event, cachedValue) {
88
+ if (cachedValue.type !== "app") {
89
+ throw new Error("getBodyForAppRouter called with non-app cache value");
90
+ }
91
+ try {
92
+ const segmentHeader = `${event.headers[NEXT_SEGMENT_PREFETCH_HEADER]}`;
93
+ const isSegmentResponse = Boolean(segmentHeader) &&
94
+ segmentHeader in (cachedValue.segmentData || {});
95
+ const body = isSegmentResponse
96
+ ? cachedValue.segmentData[segmentHeader]
97
+ : cachedValue.rsc;
98
+ return {
99
+ body,
100
+ additionalHeaders: isSegmentResponse
101
+ ? { [NEXT_PRERENDER_HEADER]: "1", [NEXT_POSTPONED_HEADER]: "2" }
102
+ : {},
103
+ };
104
+ }
105
+ catch (e) {
106
+ error("Error while getting body for app router from cache:", e);
107
+ return { body: cachedValue.rsc, additionalHeaders: {} };
108
+ }
109
+ }
84
110
  async function generateResult(event, localizedPath, cachedValue, lastModified) {
85
111
  debug("Returning result from experimental cache");
86
112
  let body = "";
87
113
  let type = "application/octet-stream";
88
114
  let isDataRequest = false;
89
- switch (cachedValue.type) {
90
- case "app":
91
- isDataRequest = Boolean(event.headers.rsc);
92
- body = isDataRequest ? cachedValue.rsc : cachedValue.html;
93
- type = isDataRequest ? "text/x-component" : "text/html; charset=utf-8";
94
- break;
95
- case "page":
96
- isDataRequest = Boolean(event.query.__nextDataReq);
97
- body = isDataRequest
98
- ? JSON.stringify(cachedValue.json)
99
- : cachedValue.html;
100
- type = isDataRequest ? "application/json" : "text/html; charset=utf-8";
101
- break;
115
+ let additionalHeaders = {};
116
+ if (cachedValue.type === "app") {
117
+ isDataRequest = Boolean(event.headers.rsc);
118
+ if (isDataRequest) {
119
+ const { body: appRouterBody, additionalHeaders: appHeaders } = getBodyForAppRouter(event, cachedValue);
120
+ body = appRouterBody;
121
+ additionalHeaders = appHeaders;
122
+ }
123
+ else {
124
+ body = cachedValue.html;
125
+ }
126
+ type = isDataRequest ? "text/x-component" : "text/html; charset=utf-8";
127
+ }
128
+ else if (cachedValue.type === "page") {
129
+ isDataRequest = Boolean(event.query.__nextDataReq);
130
+ body = isDataRequest ? JSON.stringify(cachedValue.json) : cachedValue.html;
131
+ type = isDataRequest ? "application/json" : "text/html; charset=utf-8";
132
+ }
133
+ else {
134
+ throw new Error("generateResult called with unsupported cache value type, only 'app' and 'page' are supported");
102
135
  }
103
136
  const cacheControl = await computeCacheControl(localizedPath, body, event.headers.host, cachedValue.revalidate, lastModified);
104
137
  return {
@@ -116,6 +149,7 @@ async function generateResult(event, localizedPath, cachedValue, lastModified) {
116
149
  "content-type": type,
117
150
  ...cachedValue.meta?.headers,
118
151
  vary: VARY_HEADER,
152
+ ...additionalHeaders,
119
153
  },
120
154
  };
121
155
  }
@@ -168,8 +202,8 @@ export async function cacheInterceptor(event) {
168
202
  // Then we decode the path params
169
203
  localizedPath = decodePathParams(localizedPath);
170
204
  debug("Checking cache for", localizedPath, PrerenderManifest);
171
- const isISR = Object.keys(PrerenderManifest.routes).includes(localizedPath ?? "/") ||
172
- Object.values(PrerenderManifest.dynamicRoutes).some((dr) => new RegExp(dr.routeRegex).test(localizedPath));
205
+ const isISR = Object.keys(PrerenderManifest?.routes ?? {}).includes(localizedPath ?? "/") ||
206
+ Object.values(PrerenderManifest?.dynamicRoutes ?? {}).some((dr) => new RegExp(dr.routeRegex).test(localizedPath));
173
207
  debug("isISR", isISR);
174
208
  if (isISR) {
175
209
  try {
@@ -23,7 +23,7 @@ export declare function handleRewrites<T extends RewriteDefinition>(event: Inter
23
23
  };
24
24
  export declare function handleRedirects(event: InternalEvent, redirects: RedirectDefinition[]): InternalResult | undefined;
25
25
  export declare function fixDataPage(internalEvent: InternalEvent, buildId: string): InternalEvent | InternalResult;
26
- export declare function handleFallbackFalse(internalEvent: InternalEvent, prerenderManifest: PrerenderManifest): {
26
+ export declare function handleFallbackFalse(internalEvent: InternalEvent, prerenderManifest?: PrerenderManifest): {
27
27
  event: InternalEvent;
28
28
  isISR: boolean;
29
29
  };
@@ -294,7 +294,7 @@ export function fixDataPage(internalEvent, buildId) {
294
294
  }
295
295
  export function handleFallbackFalse(internalEvent, prerenderManifest) {
296
296
  const { rawPath } = internalEvent;
297
- const { dynamicRoutes, routes } = prerenderManifest;
297
+ const { dynamicRoutes = {}, routes = {} } = prerenderManifest ?? {};
298
298
  const prerenderedFallbackRoutes = Object.entries(dynamicRoutes).filter(([, { fallback }]) => fallback === false);
299
299
  const routeFallback = prerenderedFallbackRoutes.some(([, { routeRegex }]) => {
300
300
  const routeRegexExp = new RegExp(routeRegex);
@@ -25,7 +25,7 @@ export async function handleMiddleware(internalEvent, initialSearch, middlewareL
25
25
  // The `x-prerender-revalidate` header is set at build time and should be safe to trust
26
26
  if (headers["x-isr"] &&
27
27
  headers["x-prerender-revalidate"] ===
28
- PrerenderManifest.preview.previewModeId)
28
+ PrerenderManifest?.preview?.previewModeId)
29
29
  return internalEvent;
30
30
  // We only need the normalizedPath to check if the middleware should run
31
31
  const normalizedPath = localizePath(internalEvent);
@@ -295,6 +295,13 @@ export async function revalidateIfRequired(host, rawPath, headers, req) {
295
295
  * @__PURE__
296
296
  */
297
297
  export function fixISRHeaders(headers) {
298
+ const sMaxAgeRegex = /s-maxage=(\d+)/;
299
+ const match = headers[CommonHeaders.CACHE_CONTROL]?.match(sMaxAgeRegex);
300
+ const sMaxAge = match ? Number.parseInt(match[1]) : undefined;
301
+ // We only apply the fix if the cache-control header contains s-maxage
302
+ if (!sMaxAge) {
303
+ return;
304
+ }
298
305
  if (headers[CommonHeaders.NEXT_CACHE] === "REVALIDATED") {
299
306
  headers[CommonHeaders.CACHE_CONTROL] =
300
307
  "private, no-cache, no-store, max-age=0, must-revalidate";
@@ -302,18 +309,11 @@ export function fixISRHeaders(headers) {
302
309
  }
303
310
  const _lastModified = globalThis.__openNextAls.getStore()?.lastModified ?? 0;
304
311
  if (headers[CommonHeaders.NEXT_CACHE] === "HIT" && _lastModified > 0) {
305
- // calculate age
306
- const age = Math.round((Date.now() - _lastModified) / 1000);
307
- // extract s-maxage from cache-control
308
- const regex = /s-maxage=(\d+)/;
309
- const cacheControl = headers[CommonHeaders.CACHE_CONTROL];
310
- debug("cache-control", cacheControl, _lastModified, Date.now());
311
- if (typeof cacheControl !== "string")
312
- return;
313
- const match = cacheControl.match(regex);
314
- const sMaxAge = match ? Number.parseInt(match[1]) : undefined;
312
+ debug("cache-control", headers[CommonHeaders.CACHE_CONTROL], _lastModified, Date.now());
315
313
  // 31536000 is the default s-maxage value for SSG pages
316
314
  if (sMaxAge && sMaxAge !== 31536000) {
315
+ // calculate age
316
+ const age = Math.round((Date.now() - _lastModified) / 1000);
317
317
  const remainingTtl = Math.max(sMaxAge - age, 1);
318
318
  headers[CommonHeaders.CACHE_CONTROL] =
319
319
  `s-maxage=${remainingTtl}, stale-while-revalidate=2592000`;
@@ -4,7 +4,7 @@ import type { TransformCallback } from "node:stream";
4
4
  import { Transform } from "node:stream";
5
5
  import type { StreamCreator } from "../types/open-next";
6
6
  export declare class OpenNextNodeResponse extends Transform implements ServerResponse {
7
- private fixHeaders;
7
+ private fixHeadersFn;
8
8
  private onEnd;
9
9
  private streamCreator?;
10
10
  private initialHeaders?;
@@ -13,6 +13,7 @@ export declare class OpenNextNodeResponse extends Transform implements ServerRes
13
13
  headers: OutgoingHttpHeaders;
14
14
  headersSent: boolean;
15
15
  _chunks: Buffer[];
16
+ headersAlreadyFixed: boolean;
16
17
  private _cookies;
17
18
  private responseStream?;
18
19
  private bodyLength;
@@ -35,7 +36,7 @@ export declare class OpenNextNodeResponse extends Transform implements ServerRes
35
36
  socket: Socket | null;
36
37
  setTimeout(_msecs: number, _callback?: (() => void) | undefined): this;
37
38
  addTrailers(_headers: OutgoingHttpHeaders | readonly [string, string][]): void;
38
- constructor(fixHeaders: (headers: OutgoingHttpHeaders) => void, onEnd: (headers: OutgoingHttpHeaders) => Promise<void>, streamCreator?: StreamCreator | undefined, initialHeaders?: OutgoingHttpHeaders | undefined, statusCode?: number);
39
+ constructor(fixHeadersFn: (headers: OutgoingHttpHeaders) => void, onEnd: (headers: OutgoingHttpHeaders) => Promise<void>, streamCreator?: StreamCreator | undefined, initialHeaders?: OutgoingHttpHeaders | undefined, statusCode?: number);
39
40
  get originalResponse(): this;
40
41
  get finished(): boolean;
41
42
  setHeader(name: string, value: string | string[]): this;
@@ -51,8 +52,9 @@ export declare class OpenNextNodeResponse extends Transform implements ServerRes
51
52
  /**
52
53
  * OpenNext specific method
53
54
  */
55
+ fixHeaders(headers: OutgoingHttpHeaders): void;
54
56
  getFixedHeaders(): OutgoingHttpHeaders;
55
- getBody(): Buffer;
57
+ getBody(): Buffer<ArrayBuffer>;
56
58
  private _internalWrite;
57
59
  _transform(chunk: any, encoding: BufferEncoding, callback: TransformCallback): void;
58
60
  _flush(callback: TransformCallback): void;
@@ -5,7 +5,7 @@ const SET_COOKIE_HEADER = "set-cookie";
5
5
  const CANNOT_BE_USED = "This cannot be used in OpenNext";
6
6
  // We only need to implement the methods that are used by next.js
7
7
  export class OpenNextNodeResponse extends Transform {
8
- fixHeaders;
8
+ fixHeadersFn;
9
9
  onEnd;
10
10
  streamCreator;
11
11
  initialHeaders;
@@ -14,6 +14,7 @@ export class OpenNextNodeResponse extends Transform {
14
14
  headers = {};
15
15
  headersSent = false;
16
16
  _chunks = [];
17
+ headersAlreadyFixed = false;
17
18
  _cookies = [];
18
19
  responseStream;
19
20
  bodyLength = 0;
@@ -52,9 +53,9 @@ export class OpenNextNodeResponse extends Transform {
52
53
  addTrailers(_headers) {
53
54
  throw new Error(CANNOT_BE_USED);
54
55
  }
55
- constructor(fixHeaders, onEnd, streamCreator, initialHeaders, statusCode) {
56
+ constructor(fixHeadersFn, onEnd, streamCreator, initialHeaders, statusCode) {
56
57
  super();
57
- this.fixHeaders = fixHeaders;
58
+ this.fixHeadersFn = fixHeadersFn;
58
59
  this.onEnd = onEnd;
59
60
  this.streamCreator = streamCreator;
60
61
  this.initialHeaders = initialHeaders;
@@ -210,6 +211,13 @@ export class OpenNextNodeResponse extends Transform {
210
211
  /**
211
212
  * OpenNext specific method
212
213
  */
214
+ fixHeaders(headers) {
215
+ if (this.headersAlreadyFixed) {
216
+ return;
217
+ }
218
+ this.fixHeadersFn(headers);
219
+ this.headersAlreadyFixed = true;
220
+ }
213
221
  getFixedHeaders() {
214
222
  // Do we want to apply this on writeHead?
215
223
  this.fixHeaders(this.headers);
package/dist/http/util.js CHANGED
@@ -1,4 +1,4 @@
1
- import logger from "../logger";
1
+ import { warn } from "../adapters/logger";
2
2
  export const parseHeaders = (headers) => {
3
3
  const result = {};
4
4
  if (!headers) {
@@ -18,11 +18,11 @@ export const parseHeaders = (headers) => {
18
18
  * and https://github.com/opennextjs/opennextjs-aws/pull/977#issuecomment-3261763114
19
19
  */
20
20
  if (keyLower === "location" && Array.isArray(value)) {
21
- if (value[0] === value[1]) {
21
+ if (value.length === 1 || value[0] === value[1]) {
22
22
  result[keyLower] = value[0];
23
23
  }
24
24
  else {
25
- logger.warn("Multiple different values for Location header found. Using the last one");
25
+ warn("Multiple different values for Location header found. Using the last one");
26
26
  result[keyLower] = value[value.length - 1];
27
27
  }
28
28
  continue;
@@ -8,7 +8,7 @@ const fetchProxy = {
8
8
  const response = await fetch(url, {
9
9
  method,
10
10
  headers,
11
- body,
11
+ body: body,
12
12
  });
13
13
  const responseHeaders = {};
14
14
  response.headers.forEach((value, key) => {
@@ -52,7 +52,12 @@ const handler = async (handler, converter) => async (request, env, ctx, abortSig
52
52
  resolveResponse(response);
53
53
  return new Writable({
54
54
  write(chunk, encoding, callback) {
55
- controller.enqueue(chunk);
55
+ try {
56
+ controller.enqueue(chunk);
57
+ }
58
+ catch (e) {
59
+ return callback(e);
60
+ }
56
61
  callback();
57
62
  },
58
63
  final(callback) {
@@ -144,8 +144,8 @@ ${contents}
144
144
  export const FunctionsConfigManifest = ${JSON.stringify(FunctionsConfigManifest)};
145
145
  export const PagesManifest = ${JSON.stringify(PagesManifest)};
146
146
 
147
-
148
147
  process.env.NEXT_BUILD_ID = BuildId;
148
+ process.env.NEXT_PREVIEW_MODE_ID = PrerenderManifest?.preview?.previewModeId;
149
149
  `;
150
150
  return { contents };
151
151
  });
@@ -6,16 +6,9 @@ export interface ResponseStream extends Writable {
6
6
  setContentType(contentType: string): void;
7
7
  }
8
8
  type Handler = (event: APIGatewayProxyEventV2, responseStream: ResponseStream, context: Context) => Promise<any>;
9
- interface Metadata {
10
- statusCode: number;
11
- headers: Record<string, string>;
12
- }
13
9
  declare global {
14
10
  namespace awslambda {
15
11
  function streamifyResponse(handler: Handler): Handler;
16
- namespace HttpResponseStream {
17
- function from(res: Writable, metadata: Metadata): ResponseStream;
18
- }
19
12
  }
20
13
  }
21
14
  export type AwsLambdaEvent = APIGatewayProxyEventV2 | CloudFrontRequestEvent | APIGatewayProxyEvent | WarmerEvent;
@@ -42,6 +42,7 @@ export type CachedFile = {
42
42
  html: string;
43
43
  rsc: string;
44
44
  meta?: Meta;
45
+ segmentData?: Record<string, string>;
45
46
  } | {
46
47
  type: "route";
47
48
  body: string;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "access": "public"
4
4
  },
5
5
  "name": "@opennextjs/aws",
6
- "version": "3.8.5",
6
+ "version": "3.9.1",
7
7
  "bin": {
8
8
  "open-next": "./dist/index.js"
9
9
  },
@@ -33,7 +33,7 @@
33
33
  "README.md"
34
34
  ],
35
35
  "dependencies": {
36
- "@ast-grep/napi": "^0.35.0",
36
+ "@ast-grep/napi": "^0.40.0",
37
37
  "@aws-sdk/client-cloudfront": "3.398.0",
38
38
  "@aws-sdk/client-dynamodb": "^3.398.0",
39
39
  "@aws-sdk/client-lambda": "^3.398.0",
@@ -41,22 +41,23 @@
41
41
  "@aws-sdk/client-sqs": "^3.398.0",
42
42
  "@node-minify/core": "^8.0.6",
43
43
  "@node-minify/terser": "^8.0.6",
44
- "@tsconfig/node18": "^1.0.1",
45
- "aws4fetch": "^1.0.18",
46
- "chalk": "^5.3.0",
44
+ "@tsconfig/node18": "^1.0.3",
45
+ "aws4fetch": "^1.0.20",
46
+ "chalk": "^5.6.2",
47
47
  "cookie": "^1.0.2",
48
48
  "esbuild": "0.25.4",
49
- "express": "5.0.1",
49
+ "express": "^5.1.0",
50
50
  "path-to-regexp": "^6.3.0",
51
- "urlpattern-polyfill": "^10.0.0",
52
- "yaml": "^2.7.0"
51
+ "urlpattern-polyfill": "^10.1.0",
52
+ "yaml": "^2.8.1"
53
53
  },
54
54
  "devDependencies": {
55
- "@types/aws-lambda": "^8.10.109",
55
+ "@types/aws-lambda": "^8.10.158",
56
56
  "@types/express": "5.0.0",
57
57
  "@types/node": "20.17.6",
58
- "tsc-alias": "^1.8.8",
59
- "typescript": "5.6.3"
58
+ "concurrently": "^9.2.1",
59
+ "tsc-alias": "^1.8.16",
60
+ "typescript": "5.9.3"
60
61
  },
61
62
  "bugs": {
62
63
  "url": "https://github.com/opennextjs/opennextjs-aws/issues"
@@ -68,6 +69,6 @@
68
69
  },
69
70
  "scripts": {
70
71
  "build": "tsc && tsc-alias",
71
- "dev": "tsc -w"
72
+ "dev": "concurrently \"tsc -w\" \"tsc-alias -w\""
72
73
  }
73
74
  }