@opennextjs/aws 3.6.0 → 3.6.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.
@@ -51,7 +51,11 @@ export async function openNextHandler(internalEvent, options) {
51
51
  continue;
52
52
  }
53
53
  const key = rawKey.slice(MIDDLEWARE_HEADER_PREFIX_LEN);
54
- overwrittenResponseHeaders[key] = value;
54
+ // We skip this header here since it is used by Next internally and we don't want it on the response headers.
55
+ // This header needs to be present in the request headers for processRequest, so cookies().get() from Next will work on initial render.
56
+ if (key !== "x-middleware-set-cookie") {
57
+ overwrittenResponseHeaders[key] = value;
58
+ }
55
59
  headers[key] = value;
56
60
  delete headers[rawKey];
57
61
  }
@@ -151,44 +155,68 @@ async function processRequest(req, res, routingResult) {
151
155
  // Next.js doesn't parse body if the property exists
152
156
  // https://github.com/dougmoscrop/serverless-http/issues/227
153
157
  delete req.body;
158
+ // Here we try to apply as much request metadata as possible
159
+ // We apply every metadata from `resolve-routes` https://github.com/vercel/next.js/blob/916f105b97211de50f8580f0b39c9e7c60de4886/packages/next/src/server/lib/router-utils/resolve-routes.ts
160
+ // and `router-server` https://github.com/vercel/next.js/blob/916f105b97211de50f8580f0b39c9e7c60de4886/packages/next/src/server/lib/router-server.ts
161
+ const initialURL = new URL(routingResult.initialURL);
162
+ let invokeStatus;
163
+ if (routingResult.internalEvent.rawPath === "/500") {
164
+ invokeStatus = 500;
165
+ }
166
+ else if (routingResult.internalEvent.rawPath === "/404") {
167
+ invokeStatus = 404;
168
+ }
169
+ const requestMetadata = {
170
+ isNextDataReq: routingResult.internalEvent.query.__nextDataReq === "1",
171
+ initURL: routingResult.initialURL,
172
+ initQuery: convertToQuery(initialURL.search),
173
+ initProtocol: initialURL.protocol,
174
+ defaultLocale: NextConfig.i18n?.defaultLocale,
175
+ locale: routingResult.locale,
176
+ middlewareInvoke: false,
177
+ // By setting invokePath and invokeQuery we can bypass some of the routing logic in Next.js
178
+ invokePath: routingResult.internalEvent.rawPath,
179
+ invokeQuery: routingResult.internalEvent.query,
180
+ // invokeStatus is only used for error pages
181
+ invokeStatus,
182
+ };
154
183
  try {
155
184
  //#override applyNextjsPrebundledReact
156
185
  setNextjsPrebundledReact(routingResult.internalEvent.rawPath);
157
186
  //#endOverride
158
- // Here we try to apply as much request metadata as possible
159
- // We apply every metadata from `resolve-routes` https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/resolve-routes.ts
160
- // and `router-server` https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-server.ts
161
- const initialURL = new URL(routingResult.initialURL);
162
- let invokeStatus;
163
- if (routingResult.internalEvent.rawPath === "/500") {
164
- invokeStatus = 500;
165
- }
166
- else if (routingResult.internalEvent.rawPath === "/404") {
167
- invokeStatus = 404;
168
- }
169
- const requestMetadata = {
170
- isNextDataReq: routingResult.internalEvent.query.__nextDataReq === "1",
171
- initURL: routingResult.initialURL,
172
- initQuery: convertToQuery(initialURL.search),
173
- initProtocol: initialURL.protocol,
174
- defaultLocale: NextConfig.i18n?.defaultLocale,
175
- locale: routingResult.locale,
176
- middlewareInvoke: false,
177
- // By setting invokePath and invokeQuery we can bypass some of the routing logic in Next.js
178
- invokePath: routingResult.internalEvent.rawPath,
179
- invokeQuery: routingResult.internalEvent.query,
180
- // invokeStatus is only used for error pages
181
- invokeStatus,
182
- };
183
187
  // Next Server
184
188
  await requestHandler(requestMetadata)(req, res);
185
189
  }
186
190
  catch (e) {
187
191
  // This might fail when using bundled next, importing won't do the trick either
188
192
  if (e.constructor.name === "NoFallbackError") {
189
- // Do we need to handle _not-found
190
- // Ideally this should never get triggered and be intercepted by the routing handler
191
- await tryRenderError("404", res, routingResult.internalEvent);
193
+ await handleNoFallbackError(req, res, routingResult, requestMetadata);
194
+ }
195
+ else {
196
+ error("NextJS request failed.", e);
197
+ await tryRenderError("500", res, routingResult.internalEvent);
198
+ }
199
+ }
200
+ }
201
+ async function handleNoFallbackError(req, res, routingResult, metadata, index = 1) {
202
+ if (index >= 5) {
203
+ await tryRenderError("500", res, routingResult.internalEvent);
204
+ return;
205
+ }
206
+ if (index >= routingResult.resolvedRoutes.length) {
207
+ await tryRenderError("404", res, routingResult.internalEvent);
208
+ return;
209
+ }
210
+ try {
211
+ await requestHandler({
212
+ ...routingResult,
213
+ invokeOutput: routingResult.resolvedRoutes[index].route,
214
+ ...metadata,
215
+ })(req, res);
216
+ }
217
+ catch (e) {
218
+ if (e.constructor.name === "NoFallbackError") {
219
+ await handleNoFallbackError(req, res, routingResult, metadata, index + 1);
192
220
  }
193
221
  else {
194
222
  error("NextJS request failed.", e);
@@ -114,17 +114,13 @@ export async function cacheInterceptor(event) {
114
114
  }
115
115
  // We also need to remove trailing slash
116
116
  localizedPath = localizedPath.replace(/\/$/, "");
117
- // If empty path, it means we want index
118
- if (localizedPath === "") {
119
- localizedPath = "index";
120
- }
121
117
  debug("Checking cache for", localizedPath, PrerenderManifest);
122
- const isISR = Object.keys(PrerenderManifest.routes).includes(localizedPath) ||
118
+ const isISR = Object.keys(PrerenderManifest.routes).includes(localizedPath ?? "/") ||
123
119
  Object.values(PrerenderManifest.dynamicRoutes).some((dr) => new RegExp(dr.routeRegex).test(localizedPath));
124
120
  debug("isISR", isISR);
125
121
  if (isISR) {
126
122
  try {
127
- const cachedData = await globalThis.incrementalCache.get(localizedPath);
123
+ const cachedData = await globalThis.incrementalCache.get(localizedPath ?? "/index");
128
124
  debug("cached data in interceptor", cachedData);
129
125
  if (!cachedData?.value) {
130
126
  return event;
@@ -3,6 +3,7 @@ import { compile, match } from "path-to-regexp";
3
3
  import { emptyReadableStream, toReadableStream } from "../../utils/stream";
4
4
  import { debug } from "../../adapters/logger";
5
5
  import { handleLocaleRedirect, localizePath } from "./i18n";
6
+ import { dynamicRouteMatcher, staticRouteMatcher } from "./routeMatcher";
6
7
  import { constructNextUrl, convertFromQueryString, convertToQueryString, escapeRegex, getUrlParts, isExternal, unescapeRegex, } from "./util";
7
8
  const routeHasMatcher = (headers, cookies, query) => (redirect) => {
8
9
  switch (redirect.type) {
@@ -265,9 +266,8 @@ export function fixDataPage(internalEvent, buildId) {
265
266
  export function handleFallbackFalse(internalEvent, prerenderManifest) {
266
267
  const { rawPath } = internalEvent;
267
268
  const { dynamicRoutes, routes } = prerenderManifest;
268
- const routeFallback = Object.entries(dynamicRoutes)
269
- .filter(([, { fallback }]) => fallback === false)
270
- .some(([, { routeRegex }]) => {
269
+ const prerenderedFallbackRoutes = Object.entries(dynamicRoutes).filter(([, { fallback }]) => fallback === false);
270
+ const routeFallback = prerenderedFallbackRoutes.some(([, { routeRegex }]) => {
271
271
  const routeRegexExp = new RegExp(routeRegex);
272
272
  return routeRegexExp.test(rawPath);
273
273
  });
@@ -275,11 +275,21 @@ export function handleFallbackFalse(internalEvent, prerenderManifest) {
275
275
  const routesAlreadyHaveLocale = locales?.includes(rawPath.split("/")[1]) ||
276
276
  // If we don't use locales, we don't need to add the default locale
277
277
  locales === undefined;
278
- const localizedPath = routesAlreadyHaveLocale
278
+ let localizedPath = routesAlreadyHaveLocale
279
279
  ? rawPath
280
280
  : `/${NextConfig.i18n?.defaultLocale}${rawPath}`;
281
+ // We need to remove the trailing slash if it exists
282
+ if (NextConfig.trailingSlash && localizedPath.endsWith("/")) {
283
+ localizedPath = localizedPath.slice(0, -1);
284
+ }
285
+ const matchedStaticRoute = staticRouteMatcher(localizedPath);
286
+ const prerenderedFallbackRoutesName = prerenderedFallbackRoutes.map(([name]) => name);
287
+ const matchedDynamicRoute = dynamicRouteMatcher(localizedPath).filter(({ route }) => !prerenderedFallbackRoutesName.includes(route));
281
288
  const isPregenerated = Object.keys(routes).includes(localizedPath);
282
- if (routeFallback && !isPregenerated) {
289
+ if (routeFallback &&
290
+ !isPregenerated &&
291
+ matchedStaticRoute.length === 0 &&
292
+ matchedDynamicRoute.length === 0) {
283
293
  return {
284
294
  event: {
285
295
  ...internalEvent,
@@ -75,7 +75,6 @@ export async function handleMiddleware(internalEvent, initialSearch, middlewareL
75
75
  // These are internal headers used by Next.js, we don't want to expose them to the client
76
76
  const filteredHeaders = [
77
77
  "x-middleware-override-headers",
78
- "x-middleware-set-cookie",
79
78
  "x-middleware-next",
80
79
  "x-middleware-rewrite",
81
80
  // We need to drop `content-encoding` because it will be decoded
@@ -1,10 +1,4 @@
1
- import type { RouteType } from "../../types/open-next";
1
+ import type { ResolvedRoute } from "../../types/open-next";
2
2
  export declare const apiPrefix: string;
3
- export declare const staticRouteMatcher: (path: string) => {
4
- route: string;
5
- type: RouteType;
6
- }[];
7
- export declare const dynamicRouteMatcher: (path: string) => {
8
- route: string;
9
- type: RouteType;
10
- }[];
3
+ export declare const staticRouteMatcher: (path: string) => ResolvedRoute[];
4
+ export declare const dynamicRouteMatcher: (path: string) => ResolvedRoute[];
@@ -55,7 +55,23 @@ const converter = {
55
55
  }
56
56
  const headers = new Headers();
57
57
  for (const [key, value] of Object.entries(result.headers)) {
58
- headers.set(key, Array.isArray(value) ? value.join(",") : value);
58
+ if (key === "set-cookie" && typeof value === "string") {
59
+ // If the value is a string, we need to parse it into an array
60
+ // This is the case for middleware direct result
61
+ const cookies = parseCookies(value);
62
+ for (const cookie of cookies) {
63
+ headers.append(key, cookie);
64
+ }
65
+ continue;
66
+ }
67
+ if (Array.isArray(value)) {
68
+ for (const v of value) {
69
+ headers.append(key, v);
70
+ }
71
+ }
72
+ else {
73
+ headers.set(key, value);
74
+ }
59
75
  }
60
76
  return new Response(result.body, {
61
77
  status: result.statusCode,
@@ -1,9 +1,11 @@
1
1
  import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { getMonorepoRelativePath } from "../../utils/normalize-path";
2
4
  export default {
3
5
  name: "fs-dev",
4
6
  load: async (url) => {
5
- const basePath = "../../assets";
6
- const body = fs.createReadStream(`${basePath}/${url}`);
7
+ const imagePath = path.join(getMonorepoRelativePath(), "assets", url);
8
+ const body = fs.createReadStream(imagePath);
7
9
  const contentType = url.endsWith(".png") ? "image/png" : "image/jpeg";
8
10
  return {
9
11
  body,
@@ -1,7 +1,8 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import { getMonorepoRelativePath } from "../../utils/normalize-path";
3
4
  const buildId = process.env.NEXT_BUILD_ID;
4
- const basePath = path.resolve(process.cwd(), `../../cache/${buildId}`);
5
+ const basePath = path.join(getMonorepoRelativePath(), `cache/${buildId}`);
5
6
  const getCacheKey = (key) => {
6
7
  return path.join(basePath, `${key}.cache`);
7
8
  };
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
- // TODO: fix this for monorepo
3
- const tagFile = "../../dynamodb-provider/dynamodb-cache.json";
2
+ import path from "node:path";
3
+ import { getMonorepoRelativePath } from "../../utils/normalize-path";
4
+ const tagFile = path.join(getMonorepoRelativePath(), "dynamodb-provider/dynamodb-cache.json");
4
5
  const tagContent = fs.readFileSync(tagFile, "utf-8");
5
6
  let tags = JSON.parse(tagContent);
6
7
  const tagCache = {
@@ -1,9 +1,11 @@
1
+ import path from "node:path";
1
2
  import express from "express";
3
+ import { getMonorepoRelativePath } from "../../utils/normalize-path";
2
4
  const wrapper = async (handler, converter) => {
3
5
  const app = express();
4
6
  // To serve static assets
5
- app.use(express.static("../../assets"));
6
- const imageHandlerPath = "../../image-optimization-function/index.mjs";
7
+ app.use(express.static(path.join(getMonorepoRelativePath(), "assets")));
8
+ const imageHandlerPath = path.join(getMonorepoRelativePath(), "image-optimization-function/index.mjs");
7
9
  const imageHandler = await import(imageHandlerPath).then((m) => m.handler);
8
10
  app.all("/_next/image", async (req, res) => {
9
11
  const internalEvent = await converter.convertFrom(req);
@@ -185,5 +185,10 @@ declare global {
185
185
  * Only available in main functions.
186
186
  */
187
187
  var __next_route_preloader: (stage: "waitUntil" | "start" | "warmerEvent" | "onDemand") => Promise<void>;
188
+ /**
189
+ * This is the relative package path of the monorepo. It will be an empty string "" in normal repos.
190
+ * ex. `packages/web`
191
+ */
192
+ var monorepoPackagePath: string;
188
193
  }
189
194
  export {};
@@ -1 +1,2 @@
1
1
  export declare function normalizePath(path: string): string;
2
+ export declare function getMonorepoRelativePath(relativePath?: string): string;
@@ -1,3 +1,11 @@
1
+ import path from "node:path";
1
2
  export function normalizePath(path) {
2
3
  return path.replace(/\\/g, "/");
3
4
  }
5
+ export function getMonorepoRelativePath(relativePath = "../..") {
6
+ return path.join(globalThis.monorepoPackagePath
7
+ .split("/")
8
+ .filter(Boolean)
9
+ .map(() => "..")
10
+ .join("/"), relativePath);
11
+ }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "access": "public"
4
4
  },
5
5
  "name": "@opennextjs/aws",
6
- "version": "3.6.0",
6
+ "version": "3.6.1",
7
7
  "bin": {
8
8
  "open-next": "./dist/index.js"
9
9
  },