@opennextjs/aws 3.4.1 → 3.5.0

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 (86) hide show
  1. package/dist/adapters/cache.d.ts +2 -61
  2. package/dist/adapters/cache.js +75 -37
  3. package/dist/adapters/config/index.d.ts +1 -0
  4. package/dist/adapters/config/index.js +3 -1
  5. package/dist/adapters/config/util.d.ts +2 -1
  6. package/dist/adapters/config/util.js +13 -3
  7. package/dist/adapters/dynamo-provider.js +8 -0
  8. package/dist/adapters/edge-adapter.js +4 -10
  9. package/dist/adapters/image-optimization-adapter.d.ts +3 -2
  10. package/dist/adapters/image-optimization-adapter.js +3 -3
  11. package/dist/adapters/middleware.js +12 -8
  12. package/dist/build/compileConfig.js +1 -1
  13. package/dist/build/compileTagCacheProvider.js +1 -0
  14. package/dist/build/constant.d.ts +1 -0
  15. package/dist/build/constant.js +2 -0
  16. package/dist/build/copyTracedFiles.d.ts +10 -1
  17. package/dist/build/copyTracedFiles.js +22 -12
  18. package/dist/build/createAssets.d.ts +3 -1
  19. package/dist/build/createAssets.js +3 -3
  20. package/dist/build/createMiddleware.js +18 -5
  21. package/dist/build/createServerBundle.js +7 -1
  22. package/dist/build/edge/createEdgeBundle.js +14 -12
  23. package/dist/build/generateOutput.js +5 -7
  24. package/dist/build/helper.d.ts +1 -1
  25. package/dist/build/helper.js +9 -2
  26. package/dist/build/middleware/buildNodeMiddleware.d.ts +3 -0
  27. package/dist/build/middleware/buildNodeMiddleware.js +97 -0
  28. package/dist/core/createMainHandler.js +0 -1
  29. package/dist/core/edgeFunctionHandler.js +3 -1
  30. package/dist/core/nodeMiddlewareHandler.d.ts +4 -0
  31. package/dist/core/nodeMiddlewareHandler.js +25 -0
  32. package/dist/core/requestHandler.d.ts +3 -2
  33. package/dist/core/requestHandler.js +58 -25
  34. package/dist/core/routing/cacheInterceptor.js +10 -6
  35. package/dist/core/routing/i18n/index.d.ts +2 -0
  36. package/dist/core/routing/i18n/index.js +1 -1
  37. package/dist/core/routing/matcher.js +17 -14
  38. package/dist/core/routing/middleware.js +14 -29
  39. package/dist/core/routing/queue.d.ts +1 -0
  40. package/dist/core/routing/queue.js +41 -0
  41. package/dist/core/routing/util.d.ts +12 -3
  42. package/dist/core/routing/util.js +24 -50
  43. package/dist/core/routingHandler.d.ts +2 -1
  44. package/dist/core/routingHandler.js +10 -4
  45. package/dist/core/util.d.ts +1 -4
  46. package/dist/core/util.js +7 -15
  47. package/dist/overrides/converters/aws-apigw-v1.js +4 -3
  48. package/dist/overrides/converters/aws-apigw-v2.js +4 -3
  49. package/dist/overrides/converters/aws-cloudfront.js +6 -4
  50. package/dist/overrides/converters/edge.js +1 -10
  51. package/dist/overrides/converters/node.js +10 -8
  52. package/dist/overrides/converters/utils.d.ts +7 -0
  53. package/dist/overrides/converters/utils.js +9 -0
  54. package/dist/overrides/imageLoader/s3-lite.d.ts +3 -0
  55. package/dist/overrides/imageLoader/s3-lite.js +61 -0
  56. package/dist/overrides/incrementalCache/multi-tier-ddb-s3.js +1 -1
  57. package/dist/overrides/proxyExternalRequest/fetch.js +2 -1
  58. package/dist/overrides/tagCache/dummy.js +1 -0
  59. package/dist/overrides/tagCache/dynamodb-lite.js +1 -0
  60. package/dist/overrides/tagCache/dynamodb-nextMode.d.ts +7 -0
  61. package/dist/overrides/tagCache/dynamodb-nextMode.js +119 -0
  62. package/dist/overrides/tagCache/dynamodb.js +1 -0
  63. package/dist/overrides/tagCache/fs-dev.js +1 -0
  64. package/dist/overrides/wrappers/aws-lambda-streaming.js +1 -1
  65. package/dist/overrides/wrappers/aws-lambda.js +3 -1
  66. package/dist/overrides/wrappers/cloudflare-edge.js +3 -2
  67. package/dist/overrides/wrappers/cloudflare-node.js +10 -8
  68. package/dist/overrides/wrappers/dummy.js +2 -2
  69. package/dist/overrides/wrappers/express-dev.js +4 -4
  70. package/dist/overrides/wrappers/node.js +2 -2
  71. package/dist/plugins/edge.d.ts +1 -3
  72. package/dist/plugins/edge.js +4 -11
  73. package/dist/plugins/externalMiddleware.d.ts +2 -0
  74. package/dist/plugins/externalMiddleware.js +10 -0
  75. package/dist/plugins/resolve.js +2 -2
  76. package/dist/types/cache.d.ts +34 -4
  77. package/dist/types/global.d.ts +19 -31
  78. package/dist/types/next-types.d.ts +13 -0
  79. package/dist/types/open-next.d.ts +12 -5
  80. package/dist/types/overrides.d.ts +73 -7
  81. package/dist/utils/cache.d.ts +3 -0
  82. package/dist/utils/cache.js +32 -0
  83. package/dist/utils/promise.d.ts +3 -1
  84. package/dist/utils/promise.js +18 -11
  85. package/dist/utils/regex.js +1 -1
  86. package/package.json +5 -4
@@ -1,63 +1,4 @@
1
- interface CachedFetchValue {
2
- kind: "FETCH";
3
- data: {
4
- headers: {
5
- [k: string]: string;
6
- };
7
- body: string;
8
- url: string;
9
- status?: number;
10
- tags?: string[];
11
- };
12
- revalidate: number;
13
- }
14
- interface CachedRedirectValue {
15
- kind: "REDIRECT";
16
- props: Object;
17
- }
18
- interface CachedRouteValue {
19
- kind: "ROUTE" | "APP_ROUTE";
20
- body: Buffer;
21
- status: number;
22
- headers: Record<string, undefined | string | string[]>;
23
- }
24
- interface CachedImageValue {
25
- kind: "IMAGE";
26
- etag: string;
27
- buffer: Buffer;
28
- extension: string;
29
- isMiss?: boolean;
30
- isStale?: boolean;
31
- }
32
- interface IncrementalCachedPageValue {
33
- kind: "PAGE" | "PAGES";
34
- html: string;
35
- pageData: Object;
36
- status?: number;
37
- headers?: Record<string, undefined | string>;
38
- }
39
- interface IncrementalCachedAppPageValue {
40
- kind: "APP_PAGE";
41
- html: string;
42
- rscData: Buffer;
43
- headers?: Record<string, undefined | string | string[]>;
44
- postponed?: string;
45
- status?: number;
46
- }
47
- type IncrementalCacheValue = CachedRedirectValue | IncrementalCachedPageValue | IncrementalCachedAppPageValue | CachedImageValue | CachedFetchValue | CachedRouteValue;
48
- type IncrementalCacheContext = {
49
- revalidate?: number | false | undefined;
50
- fetchCache?: boolean | undefined;
51
- fetchUrl?: string | undefined;
52
- fetchIdx?: number | undefined;
53
- tags?: string[] | undefined;
54
- };
55
- interface CacheHandlerValue {
56
- lastModified?: number;
57
- age?: number;
58
- cacheState?: string;
59
- value: IncrementalCacheValue | null;
60
- }
1
+ import type { CacheHandlerValue, IncrementalCacheContext, IncrementalCacheValue } from "../types/cache";
61
2
  export default class Cache {
62
3
  get(key: string, options?: boolean | {
63
4
  fetchCache?: boolean;
@@ -70,5 +11,5 @@ export default class Cache {
70
11
  getIncrementalCache(key: string): Promise<CacheHandlerValue | null>;
71
12
  set(key: string, data?: IncrementalCacheValue, ctx?: IncrementalCacheContext): Promise<void>;
72
13
  revalidateTag(tags: string | string[]): Promise<void>;
14
+ private updateTagsOnSet;
73
15
  }
74
- export {};
@@ -1,3 +1,4 @@
1
+ import { getTagsFromValue, hasBeenRevalidated } from "../utils/cache";
1
2
  import { isBinaryContentType } from "../utils/binary";
2
3
  import { debug, error, warn } from "./logger";
3
4
  function isFetchCache(options) {
@@ -28,13 +29,13 @@ export default class Cache {
28
29
  async getFetchCache(key, softTags, tags) {
29
30
  debug("get fetch cache", { key, softTags, tags });
30
31
  try {
31
- const { value, lastModified } = await globalThis.incrementalCache.get(key, true);
32
- const _lastModified = await globalThis.tagCache.getLastModified(key, lastModified);
33
- if (_lastModified === -1) {
34
- // If some tags are stale we need to force revalidation
32
+ const cachedEntry = await globalThis.incrementalCache.get(key, true);
33
+ if (cachedEntry?.value === undefined)
35
34
  return null;
36
- }
37
- if (value === undefined)
35
+ const _tags = [...(tags ?? []), ...(softTags ?? [])];
36
+ const _lastModified = cachedEntry.lastModified ?? Date.now();
37
+ const _hasBeenRevalidated = await hasBeenRevalidated(key, _tags, cachedEntry);
38
+ if (_hasBeenRevalidated)
38
39
  return null;
39
40
  // For cases where we don't have tags, we need to ensure that the soft tags are not being revalidated
40
41
  // We only need to check for the path as it should already contain all the tags
@@ -44,8 +45,8 @@ export default class Cache {
44
45
  !tag.endsWith("layout") &&
45
46
  !tag.endsWith("page"));
46
47
  if (path) {
47
- const pathLastModified = await globalThis.tagCache.getLastModified(path.replace("_N_T_/", ""), lastModified);
48
- if (pathLastModified === -1) {
48
+ const hasPathBeenUpdated = await hasBeenRevalidated(path.replace("_N_T_/", ""), [], cachedEntry);
49
+ if (hasPathBeenUpdated) {
49
50
  // In case the path has been revalidated, we don't want to use the fetch cache
50
51
  return null;
51
52
  }
@@ -53,7 +54,7 @@ export default class Cache {
53
54
  }
54
55
  return {
55
56
  lastModified: _lastModified,
56
- value: value,
57
+ value: cachedEntry.value,
57
58
  };
58
59
  }
59
60
  catch (e) {
@@ -64,15 +65,21 @@ export default class Cache {
64
65
  }
65
66
  async getIncrementalCache(key) {
66
67
  try {
67
- const { value: cacheData, lastModified } = await globalThis.incrementalCache.get(key, false);
68
- const meta = cacheData?.meta;
69
- const _lastModified = await globalThis.tagCache.getLastModified(key, lastModified);
70
- if (_lastModified === -1) {
71
- // If some tags are stale we need to force revalidation
68
+ const cachedEntry = await globalThis.incrementalCache.get(key, false);
69
+ if (!cachedEntry?.value) {
70
+ return null;
71
+ }
72
+ const cacheData = cachedEntry.value;
73
+ const meta = cacheData.meta;
74
+ const tags = getTagsFromValue(cacheData);
75
+ const _lastModified = cachedEntry.lastModified ?? Date.now();
76
+ const _hasBeenRevalidated = await hasBeenRevalidated(key, tags, cachedEntry);
77
+ if (_hasBeenRevalidated)
72
78
  return null;
79
+ const store = globalThis.__openNextAls.getStore();
80
+ if (store) {
81
+ store.lastModified = _lastModified;
73
82
  }
74
- const requestId = globalThis.__openNextAls.getStore()?.requestId ?? "";
75
- globalThis.lastModified[requestId] = _lastModified;
76
83
  if (cacheData?.type === "route") {
77
84
  return {
78
85
  lastModified: _lastModified,
@@ -210,27 +217,7 @@ export default class Cache {
210
217
  break;
211
218
  }
212
219
  }
213
- // Write derivedTags to dynamodb
214
- // If we use an in house version of getDerivedTags in build we should use it here instead of next's one
215
- const derivedTags = data?.kind === "FETCH"
216
- ? (ctx?.tags ?? data?.data?.tags ?? []) // before version 14 next.js used data?.data?.tags so we keep it for backward compatibility
217
- : data?.kind === "PAGE"
218
- ? (data.headers?.["x-next-cache-tags"]?.split(",") ?? [])
219
- : [];
220
- debug("derivedTags", derivedTags);
221
- // Get all tags stored in dynamodb for the given key
222
- // If any of the derived tags are not stored in dynamodb for the given key, write them
223
- const storedTags = await globalThis.tagCache.getByPath(key);
224
- const tagsToWrite = derivedTags.filter((tag) => !storedTags.includes(tag));
225
- if (tagsToWrite.length > 0) {
226
- await globalThis.tagCache.writeTags(tagsToWrite.map((tag) => ({
227
- path: key,
228
- tag: tag,
229
- // In case the tags are not there we just need to create them
230
- // but we don't want them to return from `getLastModified` as they are not stale
231
- revalidatedAt: 1,
232
- })));
233
- }
220
+ await this.updateTagsOnSet(key, data, ctx);
234
221
  debug("Finished setting cache");
235
222
  }
236
223
  catch (e) {
@@ -248,6 +235,26 @@ export default class Cache {
248
235
  }
249
236
  try {
250
237
  const _tags = Array.isArray(tags) ? tags : [tags];
238
+ if (globalThis.tagCache.mode === "nextMode") {
239
+ const paths = (await globalThis.tagCache.getPathsByTags?.(_tags)) ?? [];
240
+ await globalThis.tagCache.writeTags(_tags);
241
+ if (paths.length > 0) {
242
+ // TODO: we should introduce a new method in cdnInvalidationHandler to invalidate paths by tags for cdn that supports it
243
+ // It also means that we'll need to provide the tags used in every request to the wrapper or converter.
244
+ await globalThis.cdnInvalidationHandler.invalidatePaths(paths.map((path) => ({
245
+ initialPath: path,
246
+ rawPath: path,
247
+ resolvedRoutes: [
248
+ {
249
+ route: path,
250
+ // TODO: ideally here we should check if it's an app router page or route
251
+ type: "app",
252
+ },
253
+ ],
254
+ })));
255
+ }
256
+ return;
257
+ }
251
258
  for (const tag of _tags) {
252
259
  debug("revalidateTag", tag);
253
260
  // Find all keys with the given tag
@@ -301,4 +308,35 @@ export default class Cache {
301
308
  error("Failed to revalidate tag", e);
302
309
  }
303
310
  }
311
+ // TODO: We should delete/update tags in this method
312
+ // This will require an update to the tag cache interface
313
+ async updateTagsOnSet(key, data, ctx) {
314
+ if (globalThis.openNextConfig.dangerous?.disableTagCache ||
315
+ globalThis.tagCache.mode === "nextMode" ||
316
+ // Here it means it's a delete
317
+ !data) {
318
+ return;
319
+ }
320
+ // Write derivedTags to the tag cache
321
+ // If we use an in house version of getDerivedTags in build we should use it here instead of next's one
322
+ const derivedTags = data?.kind === "FETCH"
323
+ ? (ctx?.tags ?? data?.data?.tags ?? []) // before version 14 next.js used data?.data?.tags so we keep it for backward compatibility
324
+ : data?.kind === "PAGE"
325
+ ? (data.headers?.["x-next-cache-tags"]?.split(",") ?? [])
326
+ : [];
327
+ debug("derivedTags", derivedTags);
328
+ // Get all tags stored in dynamodb for the given key
329
+ // If any of the derived tags are not stored in dynamodb for the given key, write them
330
+ const storedTags = await globalThis.tagCache.getByPath(key);
331
+ const tagsToWrite = derivedTags.filter((tag) => !storedTags.includes(tag));
332
+ if (tagsToWrite.length > 0) {
333
+ await globalThis.tagCache.writeTags(tagsToWrite.map((tag) => ({
334
+ path: key,
335
+ tag: tag,
336
+ // In case the tags are not there we just need to create them
337
+ // but we don't want them to return from `getLastModified` as they are not stale
338
+ revalidatedAt: 1,
339
+ })));
340
+ }
341
+ }
304
342
  }
@@ -27,3 +27,4 @@ export declare const AppPathsManifestKeys: string[];
27
27
  export declare const MiddlewareManifest: import("../../types/next-types").MiddlewareManifest;
28
28
  export declare const AppPathsManifest: Record<string, string>;
29
29
  export declare const AppPathRoutesManifest: Record<string, string>;
30
+ export declare const FunctionsConfigManifest: import("../../types/next-types").FunctionsConfigManifest;
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { debug } from "../logger";
3
- import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "./util.js";
3
+ import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadFunctionsConfigManifest, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "./util.js";
4
4
  export const NEXT_DIR = path.join(__dirname, ".next");
5
5
  export const OPEN_NEXT_DIR = path.join(__dirname, ".open-next");
6
6
  debug({ NEXT_DIR, OPEN_NEXT_DIR });
@@ -20,3 +20,5 @@ export const MiddlewareManifest =
20
20
  export const AppPathsManifest = /* @__PURE__ */ loadAppPathsManifest(NEXT_DIR);
21
21
  export const AppPathRoutesManifest =
22
22
  /* @__PURE__ */ loadAppPathRoutesManifest(NEXT_DIR);
23
+ export const FunctionsConfigManifest =
24
+ /* @__PURE__ */ loadFunctionsConfigManifest(NEXT_DIR);
@@ -1,4 +1,4 @@
1
- import type { MiddlewareManifest, NextConfig, PrerenderManifest } from "../../types/next-types";
1
+ import type { FunctionsConfigManifest, MiddlewareManifest, NextConfig, PrerenderManifest } from "../../types/next-types";
2
2
  import type { PublicFiles } from "../../build";
3
3
  export declare function loadConfig(nextDir: string): NextConfig;
4
4
  export declare function loadBuildId(nextDir: string): string;
@@ -28,3 +28,4 @@ export declare function loadAppPathsManifest(nextDir: string): Record<string, st
28
28
  export declare function loadAppPathRoutesManifest(nextDir: string): Record<string, string>;
29
29
  export declare function loadAppPathsManifestKeys(nextDir: string): string[];
30
30
  export declare function loadMiddlewareManifest(nextDir: string): MiddlewareManifest;
31
+ export declare function loadFunctionsConfigManifest(nextDir: string): FunctionsConfigManifest;
@@ -11,7 +11,7 @@ export function loadBuildId(nextDir) {
11
11
  return fs.readFileSync(filePath, "utf-8").trim();
12
12
  }
13
13
  export function loadHtmlPages(nextDir) {
14
- const filePath = path.join(nextDir, "server", "pages-manifest.json");
14
+ const filePath = path.join(nextDir, "server/pages-manifest.json");
15
15
  const json = fs.readFileSync(filePath, "utf-8");
16
16
  return Object.entries(JSON.parse(json))
17
17
  .filter(([_, value]) => value.endsWith(".html"))
@@ -61,7 +61,7 @@ export function loadPrerenderManifest(nextDir) {
61
61
  return JSON.parse(json);
62
62
  }
63
63
  export function loadAppPathsManifest(nextDir) {
64
- const appPathsManifestPath = path.join(nextDir, "server", "app-paths-manifest.json");
64
+ const appPathsManifestPath = path.join(nextDir, "server/app-paths-manifest.json");
65
65
  const appPathsManifestJson = fs.existsSync(appPathsManifestPath)
66
66
  ? fs.readFileSync(appPathsManifestPath, "utf-8")
67
67
  : "{}";
@@ -88,7 +88,17 @@ export function loadAppPathsManifestKeys(nextDir) {
88
88
  });
89
89
  }
90
90
  export function loadMiddlewareManifest(nextDir) {
91
- const filePath = path.join(nextDir, "server", "middleware-manifest.json");
91
+ const filePath = path.join(nextDir, "server/middleware-manifest.json");
92
92
  const json = fs.readFileSync(filePath, "utf-8");
93
93
  return JSON.parse(json);
94
94
  }
95
+ export function loadFunctionsConfigManifest(nextDir) {
96
+ const filePath = path.join(nextDir, "server/functions-config-manifest.json");
97
+ try {
98
+ const json = fs.readFileSync(filePath, "utf-8");
99
+ return JSON.parse(json);
100
+ }
101
+ catch (e) {
102
+ return { functions: {}, version: 1 };
103
+ }
104
+ }
@@ -16,6 +16,14 @@ async function defaultHandler(event) {
16
16
  }
17
17
  }
18
18
  async function insert(requestType) {
19
+ // If it is in nextMode, we don't need to do anything
20
+ if (tagCache.mode === "nextMode") {
21
+ return {
22
+ type: "initializationFunction",
23
+ requestType,
24
+ resourceId: PHYSICAL_RESOURCE_ID,
25
+ };
26
+ }
19
27
  const file = readFileSync("dynamodb-cache.json", "utf8");
20
28
  const data = JSON.parse(file);
21
29
  const parsedData = data.map((item) => ({
@@ -3,18 +3,12 @@ import { emptyReadableStream } from "../utils/stream";
3
3
  // We import it like that so that the edge plugin can replace it
4
4
  import { NextConfig } from "../adapters/config";
5
5
  import { createGenericHandler } from "../core/createGenericHandler";
6
- import { convertBodyToReadableStream, convertToQueryString, } from "../core/routing/util";
6
+ import { convertBodyToReadableStream } from "../core/routing/util";
7
7
  globalThis.__openNextAls = new AsyncLocalStorage();
8
- const defaultHandler = async (internalEvent) => {
8
+ const defaultHandler = async (internalEvent, options) => {
9
9
  globalThis.isEdgeRuntime = true;
10
10
  // We run everything in the async local storage context so that it is available in edge runtime functions
11
- return runWithOpenNextRequestContext({ isISRRevalidation: false }, async () => {
12
- const host = internalEvent.headers.host
13
- ? `https://${internalEvent.headers.host}`
14
- : "http://localhost:3000";
15
- const initialUrl = new URL(internalEvent.rawPath, host);
16
- initialUrl.search = convertToQueryString(internalEvent.query);
17
- const url = initialUrl.toString();
11
+ return runWithOpenNextRequestContext({ isISRRevalidation: false, waitUntil: options?.waitUntil }, async () => {
18
12
  // @ts-expect-error - This is bundled
19
13
  const handler = await import("./middleware.mjs");
20
14
  const response = await handler.default({
@@ -25,7 +19,7 @@ const defaultHandler = async (internalEvent) => {
25
19
  i18n: NextConfig.i18n,
26
20
  trailingSlash: NextConfig.trailingSlash,
27
21
  },
28
- url,
22
+ url: internalEvent.url,
29
23
  body: convertBodyToReadableStream(internalEvent.method, internalEvent.body),
30
24
  });
31
25
  const responseHeaders = {};
@@ -1,3 +1,4 @@
1
- import type { InternalEvent, InternalResult, StreamCreator } from "../types/open-next.js";
1
+ import type { InternalEvent, InternalResult } from "../types/open-next.js";
2
+ import type { OpenNextHandlerOptions } from "../types/overrides.js";
2
3
  export declare const handler: (...args: any[]) => any;
3
- export declare function defaultHandler(event: InternalEvent, streamCreator?: StreamCreator): Promise<InternalResult>;
4
+ export declare function defaultHandler(event: InternalEvent, options?: OpenNextHandlerOptions): Promise<InternalResult>;
@@ -36,7 +36,7 @@ export const handler = await createGenericHandler({
36
36
  handler: defaultHandler,
37
37
  type: "imageOptimization",
38
38
  });
39
- export async function defaultHandler(event, streamCreator) {
39
+ export async function defaultHandler(event, options) {
40
40
  // Images are handled via header and query param information.
41
41
  debug("handler event", event);
42
42
  const { headers, query: queryString } = event;
@@ -65,10 +65,10 @@ export async function defaultHandler(event, streamCreator) {
65
65
  };
66
66
  }
67
67
  const result = await optimizeImage(headers, imageParams, nextConfig, downloadHandler);
68
- return buildSuccessResponse(result, streamCreator, etag);
68
+ return buildSuccessResponse(result, options?.streamCreator, etag);
69
69
  }
70
70
  catch (e) {
71
- return buildFailureResponse(e, streamCreator);
71
+ return buildFailureResponse(e, options?.streamCreator);
72
72
  }
73
73
  }
74
74
  //////////////////////
@@ -2,10 +2,11 @@ import { runWithOpenNextRequestContext } from "../utils/promise";
2
2
  import { debug, error } from "../adapters/logger";
3
3
  import { createGenericHandler } from "../core/createGenericHandler";
4
4
  import { resolveIncrementalCache, resolveOriginResolver, resolveProxyRequest, resolveQueue, resolveTagCache, } from "../core/resolve";
5
- import routingHandler, { INTERNAL_HEADER_INITIAL_PATH, INTERNAL_HEADER_RESOLVED_ROUTES, } from "../core/routingHandler";
5
+ import { constructNextUrl } from "../core/routing/util";
6
+ import routingHandler, { INTERNAL_HEADER_INITIAL_URL, INTERNAL_HEADER_RESOLVED_ROUTES, } from "../core/routingHandler";
6
7
  globalThis.internalFetch = fetch;
7
8
  globalThis.__openNextAls = new AsyncLocalStorage();
8
- const defaultHandler = async (internalEvent) => {
9
+ const defaultHandler = async (internalEvent, options) => {
9
10
  const originResolver = await resolveOriginResolver(globalThis.openNextConfig.middleware?.originResolver);
10
11
  const externalRequestProxy = await resolveProxyRequest(globalThis.openNextConfig.middleware?.override?.proxyExternalRequest);
11
12
  //#override includeCacheInMiddleware
@@ -14,7 +15,10 @@ const defaultHandler = async (internalEvent) => {
14
15
  globalThis.incrementalCache = await resolveIncrementalCache(globalThis.openNextConfig.middleware?.override?.incrementalCache);
15
16
  //#endOverride
16
17
  // We run everything in the async local storage context so that it is available in the external middleware
17
- return runWithOpenNextRequestContext({ isISRRevalidation: internalEvent.headers["x-isr"] === "1" }, async () => {
18
+ return runWithOpenNextRequestContext({
19
+ isISRRevalidation: internalEvent.headers["x-isr"] === "1",
20
+ waitUntil: options?.waitUntil,
21
+ }, async () => {
18
22
  const result = await routingHandler(internalEvent);
19
23
  if ("internalEvent" in result) {
20
24
  debug("Middleware intercepted event", internalEvent);
@@ -26,14 +30,14 @@ const defaultHandler = async (internalEvent) => {
26
30
  ...result.internalEvent,
27
31
  headers: {
28
32
  ...result.internalEvent.headers,
29
- [INTERNAL_HEADER_INITIAL_PATH]: internalEvent.rawPath,
30
- [INTERNAL_HEADER_RESOLVED_ROUTES]: JSON.stringify(result.resolvedRoutes) ?? "[]",
33
+ [INTERNAL_HEADER_INITIAL_URL]: internalEvent.url,
34
+ [INTERNAL_HEADER_RESOLVED_ROUTES]: JSON.stringify(result.resolvedRoutes),
31
35
  },
32
36
  },
33
37
  isExternalRewrite: result.isExternalRewrite,
34
38
  origin,
35
39
  isISR: result.isISR,
36
- initialPath: result.initialPath,
40
+ initialURL: result.initialURL,
37
41
  resolvedRoutes: result.resolvedRoutes,
38
42
  };
39
43
  }
@@ -47,14 +51,14 @@ const defaultHandler = async (internalEvent) => {
47
51
  internalEvent: {
48
52
  ...result.internalEvent,
49
53
  rawPath: "/500",
50
- url: "/500",
54
+ url: constructNextUrl(result.internalEvent.url, "/500"),
51
55
  method: "GET",
52
56
  },
53
57
  // On error we need to rewrite to the 500 page which is an internal rewrite
54
58
  isExternalRewrite: false,
55
59
  origin: false,
56
60
  isISR: result.isISR,
57
- initialPath: result.internalEvent.rawPath,
61
+ initialURL: result.internalEvent.url,
58
62
  resolvedRoutes: [{ route: "/500", type: "page" }],
59
63
  };
60
64
  }
@@ -29,7 +29,7 @@ export async function compileOpenNextConfig(baseDir, openNextConfigPath, nodeExt
29
29
  validateConfig(config);
30
30
  // We need to check if the config uses the edge runtime at any point
31
31
  // If it does, we need to compile it with the edge runtime
32
- const usesEdgeRuntime = config.middleware?.external ||
32
+ const usesEdgeRuntime = (config.middleware?.external && config.middleware.runtime !== "node") ||
33
33
  Object.values(config.functions || {}).some((fn) => fn.runtime === "edge");
34
34
  if (!usesEdgeRuntime) {
35
35
  logger.debug("No edge runtime found in the open-next.config.ts. Using default config.");
@@ -18,6 +18,7 @@ export async function compileTagCacheProvider(options) {
18
18
  overrides: {
19
19
  converter: overrides?.converter ?? "dummy",
20
20
  wrapper: overrides?.wrapper,
21
+ tagCache: options.config.initializationFunction?.tagCache,
21
22
  },
22
23
  }),
23
24
  ],
@@ -0,0 +1 @@
1
+ export declare const MIDDLEWARE_TRACE_FILE = "server/middleware.js.nft.json";
@@ -0,0 +1,2 @@
1
+ //TODO: Move all other manifest path here as well
2
+ export const MIDDLEWARE_TRACE_FILE = "server/middleware.js.nft.json";
@@ -1 +1,10 @@
1
- export declare function copyTracedFiles(buildOutputPath: string, packagePath: string, outputDir: string, routes: string[], bundledNextServer: boolean): Promise<void>;
1
+ interface CopyTracedFilesOptions {
2
+ buildOutputPath: string;
3
+ packagePath: string;
4
+ outputDir: string;
5
+ routes: string[];
6
+ bundledNextServer: boolean;
7
+ skipServerFiles?: boolean;
8
+ }
9
+ export declare function copyTracedFiles({ buildOutputPath, packagePath, outputDir, routes, bundledNextServer, skipServerFiles, }: CopyTracedFilesOptions): Promise<void>;
10
+ export {};
@@ -1,7 +1,9 @@
1
1
  import url from "node:url";
2
2
  import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, statSync, symlinkSync, writeFileSync, } from "node:fs";
3
3
  import path from "node:path";
4
+ import { loadConfig, loadPrerenderManifest } from "../adapters/config/util.js";
4
5
  import logger from "../logger.js";
6
+ import { MIDDLEWARE_TRACE_FILE } from "./constant.js";
5
7
  const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
6
8
  function copyPatchFile(outputDir) {
7
9
  const patchFile = path.join(__dirname, "patch", "patchedAsyncStorage.js");
@@ -9,11 +11,12 @@ function copyPatchFile(outputDir) {
9
11
  copyFileSync(patchFile, outputPatchFile);
10
12
  }
11
13
  // eslint-disable-next-line sonarjs/cognitive-complexity
12
- export async function copyTracedFiles(buildOutputPath, packagePath, outputDir, routes, bundledNextServer) {
14
+ export async function copyTracedFiles({ buildOutputPath, packagePath, outputDir, routes, bundledNextServer, skipServerFiles, }) {
13
15
  const tsStart = Date.now();
14
16
  const dotNextDir = path.join(buildOutputPath, ".next");
15
17
  const standaloneDir = path.join(dotNextDir, "standalone");
16
18
  const standaloneNextDir = path.join(standaloneDir, packagePath, ".next");
19
+ const standaloneServerDir = path.join(standaloneNextDir, "server");
17
20
  const outputNextDir = path.join(outputDir, packagePath, ".next");
18
21
  const extractFiles = (files, from = standaloneNextDir) => files.map((f) => path.resolve(from, f));
19
22
  // On next 14+, we might not have to include those files
@@ -23,16 +26,18 @@ export async function copyTracedFiles(buildOutputPath, packagePath, outputDir, r
23
26
  : "next-server.js.nft.json"), "utf8"));
24
27
  const filesToCopy = new Map();
25
28
  // Files necessary by the server
26
- extractFiles(requiredServerFiles.files).forEach((f) => {
27
- filesToCopy.set(f, f.replace(standaloneDir, outputDir));
28
- });
29
+ if (!skipServerFiles) {
30
+ extractFiles(requiredServerFiles.files).forEach((f) => {
31
+ filesToCopy.set(f, f.replace(standaloneDir, outputDir));
32
+ });
33
+ }
29
34
  // create directory for pages
30
- if (existsSync(path.join(standaloneDir, ".next/server/pages"))) {
35
+ if (existsSync(path.join(standaloneServerDir, "pages"))) {
31
36
  mkdirSync(path.join(outputNextDir, "server/pages"), {
32
37
  recursive: true,
33
38
  });
34
39
  }
35
- if (existsSync(path.join(standaloneDir, ".next/server/app"))) {
40
+ if (existsSync(path.join(standaloneServerDir, "app"))) {
36
41
  mkdirSync(path.join(outputNextDir, "server/app"), {
37
42
  recursive: true,
38
43
  });
@@ -83,6 +88,11 @@ File ${fullFilePath} does not exist
83
88
  }
84
89
  }
85
90
  };
91
+ if (existsSync(path.join(dotNextDir, MIDDLEWARE_TRACE_FILE))) {
92
+ // We still need to copy the nft.json file so that computeCopyFilesForPage doesn't throw
93
+ copyFileSync(path.join(dotNextDir, MIDDLEWARE_TRACE_FILE), path.join(standaloneNextDir, MIDDLEWARE_TRACE_FILE));
94
+ computeCopyFilesForPage("middleware");
95
+ }
86
96
  const hasPageDir = routes.some((route) => route.startsWith("pages/"));
87
97
  const hasAppDir = routes.some((route) => route.startsWith("app/"));
88
98
  // We need to copy all the base files like _app, _document, _error, etc
@@ -145,10 +155,10 @@ File ${fullFilePath} does not exist
145
155
  .forEach((file) => copyFileSync(path.join(standaloneNextDir, file), path.join(outputNextDir, file)));
146
156
  // We then need to copy all the files at the root of server
147
157
  mkdirSync(path.join(outputNextDir, "server"), { recursive: true });
148
- readdirSync(path.join(standaloneNextDir, "server"))
149
- .filter((fileOrDir) => !statSync(path.join(standaloneNextDir, "server", fileOrDir)).isDirectory())
158
+ readdirSync(standaloneServerDir)
159
+ .filter((fileOrDir) => !statSync(path.join(standaloneServerDir, fileOrDir)).isDirectory())
150
160
  .filter((file) => file !== "server.js")
151
- .forEach((file) => copyFileSync(path.join(standaloneNextDir, "server", file), path.join(path.join(outputNextDir, "server"), file)));
161
+ .forEach((file) => copyFileSync(path.join(standaloneServerDir, file), path.join(path.join(outputNextDir, "server"), file)));
152
162
  // Copy patch file
153
163
  copyPatchFile(path.join(outputDir, packagePath));
154
164
  // TODO: Recompute all the files.
@@ -172,10 +182,10 @@ File ${fullFilePath} does not exist
172
182
  // Maybe even move this to s3 directly
173
183
  if (hasPageDir) {
174
184
  // First we get truly static files - i.e. pages without getStaticProps
175
- const staticFiles = Object.values(JSON.parse(readFileSync(path.join(standaloneNextDir, "server/pages-manifest.json"), "utf8")));
185
+ const staticFiles = Object.values(JSON.parse(readFileSync(path.join(standaloneServerDir, "pages-manifest.json"), "utf8")));
176
186
  // Then we need to get all fallback: true dynamic routes html
177
- const prerenderManifest = JSON.parse(readFileSync(path.join(standaloneNextDir, "prerender-manifest.json"), "utf8"));
178
- const config = JSON.parse(readFileSync(path.join(standaloneNextDir, "required-server-files.json"), "utf8")).config;
187
+ const prerenderManifest = loadPrerenderManifest(standaloneNextDir);
188
+ const config = loadConfig(standaloneNextDir);
179
189
  const locales = config.i18n?.locales;
180
190
  Object.values(prerenderManifest.dynamicRoutes).forEach((route) => {
181
191
  if (typeof route.fallback === "string") {
@@ -1,11 +1,13 @@
1
+ import type { TagCacheMetaFile } from "../types/cache.js";
1
2
  import * as buildHelper from "./helper.js";
2
3
  export declare function createStaticAssets(options: buildHelper.BuildOptions): void;
3
4
  /**
4
5
  * Create the cache assets.
5
6
  *
6
7
  * @param options Build options.
7
- * @returns Whether tag cache is used.
8
+ * @returns Whether the tag cache is used, and the meta files collected.
8
9
  */
9
10
  export declare function createCacheAssets(options: buildHelper.BuildOptions): {
10
11
  useTagCache: boolean;
12
+ metaFiles: TagCacheMetaFile[];
11
13
  };
@@ -39,13 +39,13 @@ export function createStaticAssets(options) {
39
39
  * Create the cache assets.
40
40
  *
41
41
  * @param options Build options.
42
- * @returns Whether tag cache is used.
42
+ * @returns Whether the tag cache is used, and the meta files collected.
43
43
  */
44
44
  export function createCacheAssets(options) {
45
45
  logger.info("Bundling cache assets...");
46
46
  const { appBuildOutputPath, outputDir } = options;
47
47
  const packagePath = buildHelper.getPackagePath(options);
48
- const buildId = buildHelper.getBuildId(appBuildOutputPath);
48
+ const buildId = buildHelper.getBuildId(options);
49
49
  let useTagCache = false;
50
50
  // Copy pages to cache folder
51
51
  const dotNextPath = path.join(appBuildOutputPath, ".next/standalone", packagePath);
@@ -165,5 +165,5 @@ export function createCacheAssets(options) {
165
165
  }
166
166
  // We need to remove files later because we need the metafiles for dynamodb tags cache
167
167
  buildHelper.removeFiles(outputPath, ({ relativePath }) => !relativePath.endsWith(".cache"));
168
- return { useTagCache };
168
+ return { useTagCache, metaFiles };
169
169
  }