@opennextjs/aws 3.4.2 → 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 (82) hide show
  1. package/dist/adapters/cache.d.ts +2 -61
  2. package/dist/adapters/cache.js +68 -34
  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 +2 -2
  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 +4 -6
  24. package/dist/build/middleware/buildNodeMiddleware.d.ts +3 -0
  25. package/dist/build/middleware/buildNodeMiddleware.js +97 -0
  26. package/dist/core/createMainHandler.js +0 -1
  27. package/dist/core/edgeFunctionHandler.js +3 -1
  28. package/dist/core/nodeMiddlewareHandler.d.ts +4 -0
  29. package/dist/core/nodeMiddlewareHandler.js +25 -0
  30. package/dist/core/requestHandler.d.ts +3 -2
  31. package/dist/core/requestHandler.js +58 -25
  32. package/dist/core/routing/cacheInterceptor.js +7 -6
  33. package/dist/core/routing/i18n/index.d.ts +2 -0
  34. package/dist/core/routing/i18n/index.js +1 -1
  35. package/dist/core/routing/matcher.js +13 -12
  36. package/dist/core/routing/middleware.js +14 -29
  37. package/dist/core/routing/queue.d.ts +1 -0
  38. package/dist/core/routing/queue.js +41 -0
  39. package/dist/core/routing/util.d.ts +12 -3
  40. package/dist/core/routing/util.js +24 -50
  41. package/dist/core/routingHandler.d.ts +2 -1
  42. package/dist/core/routingHandler.js +10 -4
  43. package/dist/core/util.d.ts +1 -4
  44. package/dist/core/util.js +7 -15
  45. package/dist/overrides/converters/aws-apigw-v1.js +4 -3
  46. package/dist/overrides/converters/aws-apigw-v2.js +4 -3
  47. package/dist/overrides/converters/aws-cloudfront.js +6 -4
  48. package/dist/overrides/converters/edge.js +1 -10
  49. package/dist/overrides/converters/node.js +10 -8
  50. package/dist/overrides/converters/utils.d.ts +7 -0
  51. package/dist/overrides/converters/utils.js +9 -0
  52. package/dist/overrides/imageLoader/s3-lite.d.ts +3 -0
  53. package/dist/overrides/imageLoader/s3-lite.js +61 -0
  54. package/dist/overrides/tagCache/dummy.js +1 -0
  55. package/dist/overrides/tagCache/dynamodb-lite.js +1 -0
  56. package/dist/overrides/tagCache/dynamodb-nextMode.d.ts +7 -0
  57. package/dist/overrides/tagCache/dynamodb-nextMode.js +119 -0
  58. package/dist/overrides/tagCache/dynamodb.js +1 -0
  59. package/dist/overrides/tagCache/fs-dev.js +1 -0
  60. package/dist/overrides/wrappers/aws-lambda-streaming.js +1 -1
  61. package/dist/overrides/wrappers/aws-lambda.js +3 -1
  62. package/dist/overrides/wrappers/cloudflare-edge.js +3 -2
  63. package/dist/overrides/wrappers/cloudflare-node.js +5 -8
  64. package/dist/overrides/wrappers/dummy.js +2 -2
  65. package/dist/overrides/wrappers/express-dev.js +4 -4
  66. package/dist/overrides/wrappers/node.js +2 -2
  67. package/dist/plugins/edge.d.ts +1 -3
  68. package/dist/plugins/edge.js +4 -11
  69. package/dist/plugins/externalMiddleware.d.ts +2 -0
  70. package/dist/plugins/externalMiddleware.js +10 -0
  71. package/dist/plugins/resolve.js +2 -2
  72. package/dist/types/cache.d.ts +34 -4
  73. package/dist/types/global.d.ts +19 -31
  74. package/dist/types/next-types.d.ts +13 -0
  75. package/dist/types/open-next.d.ts +12 -5
  76. package/dist/types/overrides.d.ts +72 -6
  77. package/dist/utils/cache.d.ts +3 -0
  78. package/dist/utils/cache.js +32 -0
  79. package/dist/utils/promise.d.ts +3 -1
  80. package/dist/utils/promise.js +18 -11
  81. package/dist/utils/regex.js +1 -1
  82. package/package.json +5 -4
@@ -0,0 +1,7 @@
1
+ declare const _default: {
2
+ name: string;
3
+ mode: "nextMode";
4
+ hasBeenRevalidated: (tags: string[], lastModified?: number) => Promise<boolean>;
5
+ writeTags: (tags: string[]) => Promise<void>;
6
+ };
7
+ export default _default;
@@ -0,0 +1,119 @@
1
+ import { AwsClient } from "aws4fetch";
2
+ import { RecoverableError } from "../../utils/error";
3
+ import { customFetchClient } from "../../utils/fetch";
4
+ import path from "node:path";
5
+ import { debug, error } from "../../adapters/logger";
6
+ import { chunk, parseNumberFromEnv } from "../../adapters/util";
7
+ import { MAX_DYNAMO_BATCH_WRITE_ITEM_COUNT, getDynamoBatchWriteCommandConcurrency, } from "./constants";
8
+ let awsClient = null;
9
+ const getAwsClient = () => {
10
+ const { CACHE_BUCKET_REGION } = process.env;
11
+ if (awsClient) {
12
+ return awsClient;
13
+ }
14
+ awsClient = new AwsClient({
15
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
16
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
17
+ sessionToken: process.env.AWS_SESSION_TOKEN,
18
+ region: CACHE_BUCKET_REGION,
19
+ retries: parseNumberFromEnv(process.env.AWS_SDK_S3_MAX_ATTEMPTS),
20
+ });
21
+ return awsClient;
22
+ };
23
+ const awsFetch = (body, type = "query") => {
24
+ const { CACHE_BUCKET_REGION } = process.env;
25
+ const client = getAwsClient();
26
+ return customFetchClient(client)(`https://dynamodb.${CACHE_BUCKET_REGION}.amazonaws.com`, {
27
+ method: "POST",
28
+ headers: {
29
+ "Content-Type": "application/x-amz-json-1.0",
30
+ "X-Amz-Target": `DynamoDB_20120810.${type === "query" ? "BatchGetItem" : "BatchWriteItem"}`,
31
+ },
32
+ body,
33
+ });
34
+ };
35
+ function buildDynamoKey(key) {
36
+ const { NEXT_BUILD_ID } = process.env;
37
+ // FIXME: We should probably use something else than path.join here
38
+ // this could transform some fetch cache key into a valid path
39
+ return path.posix.join(NEXT_BUILD_ID ?? "", "_tag", key);
40
+ }
41
+ // We use the same key for both path and tag
42
+ // That's mostly for compatibility reason so that it's easier to use this with existing infra
43
+ // FIXME: Allow a simpler object without an unnecessary path key
44
+ function buildDynamoObject(tag, revalidatedAt) {
45
+ return {
46
+ path: { S: buildDynamoKey(tag) },
47
+ tag: { S: buildDynamoKey(tag) },
48
+ revalidatedAt: { N: `${revalidatedAt ?? Date.now()}` },
49
+ };
50
+ }
51
+ // This implementation does not support automatic invalidation of paths by the cdn
52
+ export default {
53
+ name: "ddb-nextMode",
54
+ mode: "nextMode",
55
+ hasBeenRevalidated: async (tags, lastModified) => {
56
+ if (globalThis.openNextConfig.dangerous?.disableTagCache) {
57
+ return false;
58
+ }
59
+ if (tags.length > 100) {
60
+ throw new RecoverableError("Cannot query more than 100 tags at once. You should not be using this tagCache implementation for this amount of tags");
61
+ }
62
+ const { CACHE_DYNAMO_TABLE } = process.env;
63
+ // It's unlikely that we will have more than 100 items to query
64
+ // If that's the case, you should not use this tagCache implementation
65
+ const response = await awsFetch(JSON.stringify({
66
+ RequestItems: {
67
+ [CACHE_DYNAMO_TABLE ?? ""]: {
68
+ Keys: tags.map((tag) => ({
69
+ path: { S: buildDynamoKey(tag) },
70
+ tag: { S: buildDynamoKey(tag) },
71
+ })),
72
+ },
73
+ },
74
+ }), "query");
75
+ if (response.status !== 200) {
76
+ throw new RecoverableError(`Failed to query dynamo item: ${response.status}`);
77
+ }
78
+ // Now we need to check for every item if lastModified is greater than the revalidatedAt
79
+ const { Responses } = await response.json();
80
+ if (!Responses) {
81
+ return false;
82
+ }
83
+ const revalidatedTags = Responses[CACHE_DYNAMO_TABLE ?? ""].filter((item) => Number.parseInt(item.revalidatedAt.N) > (lastModified ?? 0));
84
+ debug("retrieved tags", revalidatedTags);
85
+ return revalidatedTags.length > 0;
86
+ },
87
+ writeTags: async (tags) => {
88
+ try {
89
+ const { CACHE_DYNAMO_TABLE } = process.env;
90
+ if (globalThis.openNextConfig.dangerous?.disableTagCache) {
91
+ return;
92
+ }
93
+ const dataChunks = chunk(tags, MAX_DYNAMO_BATCH_WRITE_ITEM_COUNT).map((Items) => ({
94
+ RequestItems: {
95
+ [CACHE_DYNAMO_TABLE ?? ""]: Items.map((tag) => ({
96
+ PutRequest: {
97
+ Item: {
98
+ ...buildDynamoObject(tag),
99
+ },
100
+ },
101
+ })),
102
+ },
103
+ }));
104
+ const toInsert = chunk(dataChunks, getDynamoBatchWriteCommandConcurrency());
105
+ for (const paramsChunk of toInsert) {
106
+ await Promise.all(paramsChunk.map(async (params) => {
107
+ const response = await awsFetch(JSON.stringify(params), "batchWrite");
108
+ if (response.status !== 200) {
109
+ throw new RecoverableError(`Failed to batch write dynamo item: ${response.status}`);
110
+ }
111
+ return response;
112
+ }));
113
+ }
114
+ }
115
+ catch (e) {
116
+ error("Failed to batch write dynamo item", e);
117
+ }
118
+ },
119
+ };
@@ -25,6 +25,7 @@ function buildDynamoObject(path, tags, revalidatedAt) {
25
25
  };
26
26
  }
27
27
  const tagCache = {
28
+ mode: "original",
28
29
  async getByPath(path) {
29
30
  try {
30
31
  if (globalThis.openNextConfig.dangerous?.disableTagCache) {
@@ -4,6 +4,7 @@ const tagContent = fs.readFileSync(tagFile, "utf-8");
4
4
  let tags = JSON.parse(tagContent);
5
5
  const tagCache = {
6
6
  name: "fs-dev",
7
+ mode: "original",
7
8
  getByPath: async (path) => {
8
9
  return tags
9
10
  .filter((tagPathMapping) => tagPathMapping.path.S === path)
@@ -63,7 +63,7 @@ const handler = async (handler, converter) => awslambda.streamifyResponse(async
63
63
  return compressedStream ?? responseStream;
64
64
  },
65
65
  };
66
- const response = await handler(internalEvent, streamCreator);
66
+ const response = await handler(internalEvent, { streamCreator });
67
67
  const isUsingEdge = globalThis.isEdgeRuntime ?? false;
68
68
  if (isUsingEdge) {
69
69
  debug("Headers has not been set, we must be in the edge runtime");
@@ -26,7 +26,9 @@ const handler = async (handler, converter) => async (event) => {
26
26
  });
27
27
  },
28
28
  };
29
- const response = await handler(internalEvent, fakeStream);
29
+ const response = await handler(internalEvent, {
30
+ streamCreator: fakeStream,
31
+ });
30
32
  return converter.convertTo(response, event);
31
33
  };
32
34
  export default {
@@ -9,7 +9,6 @@ const cfPropNameMapping = {
9
9
  };
10
10
  const handler = async (handler, converter) => async (request, env, ctx) => {
11
11
  globalThis.process = process;
12
- globalThis.openNextWaitUntil = ctx.waitUntil.bind(ctx);
13
12
  // Set the environment variables
14
13
  // Cloudflare suggests to not override the process.env object but instead apply the values to it
15
14
  for (const [key, value] of Object.entries(env)) {
@@ -33,7 +32,9 @@ const handler = async (handler, converter) => async (request, env, ctx) => {
33
32
  : propValue;
34
33
  }
35
34
  }
36
- const response = await handler(internalEvent);
35
+ const response = await handler(internalEvent, {
36
+ waitUntil: ctx.waitUntil.bind(ctx),
37
+ });
37
38
  const result = await converter.convertTo(response);
38
39
  return result;
39
40
  };
@@ -3,7 +3,6 @@ import { Writable } from "node:stream";
3
3
  const NULL_BODY_STATUSES = new Set([101, 204, 205, 304]);
4
4
  const handler = async (handler, converter) => async (request, env, ctx) => {
5
5
  globalThis.process = process;
6
- globalThis.openNextWaitUntil = ctx.waitUntil.bind(ctx);
7
6
  // Set the environment variables
8
7
  // Cloudflare suggests to not override the process.env object but instead apply the values to it
9
8
  for (const [key, value] of Object.entries(env)) {
@@ -12,12 +11,7 @@ const handler = async (handler, converter) => async (request, env, ctx) => {
12
11
  }
13
12
  }
14
13
  const internalEvent = await converter.convertFrom(request);
15
- // TODO:
16
- // The edge converter populate event.url with the url including the origin.
17
- // This is required for middleware to keep track of the protocol (i.e. http with wrangler dev).
18
- // However the server expects that the origin is not included.
19
- const url = new URL(internalEvent.url);
20
- internalEvent.url = url.href.slice(url.origin.length);
14
+ const url = new URL(request.url);
21
15
  const { promise: promiseResponse, resolve: resolveResponse } = Promise.withResolvers();
22
16
  const streamCreator = {
23
17
  writeHeaders(prelude) {
@@ -45,7 +39,10 @@ const handler = async (handler, converter) => async (request, env, ctx) => {
45
39
  return Writable.fromWeb(writable);
46
40
  },
47
41
  };
48
- ctx.waitUntil(handler(internalEvent, streamCreator));
42
+ ctx.waitUntil(handler(internalEvent, {
43
+ streamCreator,
44
+ waitUntil: ctx.waitUntil.bind(ctx),
45
+ }));
49
46
  return promiseResponse;
50
47
  };
51
48
  export default {
@@ -1,6 +1,6 @@
1
1
  const dummyWrapper = async (handler, converter) => {
2
- return async (event, responseStream) => {
3
- return await handler(event, responseStream);
2
+ return async (event, options) => {
3
+ return await handler(event, options);
4
4
  };
5
5
  };
6
6
  export default {
@@ -7,24 +7,24 @@ const wrapper = async (handler, converter) => {
7
7
  const imageHandler = await import(imageHandlerPath).then((m) => m.handler);
8
8
  app.all("/_next/image", async (req, res) => {
9
9
  const internalEvent = await converter.convertFrom(req);
10
- const _res = {
10
+ const streamCreator = {
11
11
  writeHeaders: (prelude) => {
12
12
  res.writeHead(prelude.statusCode, prelude.headers);
13
13
  return res;
14
14
  },
15
15
  };
16
- await imageHandler(internalEvent, _res);
16
+ await imageHandler(internalEvent, { streamCreator });
17
17
  });
18
18
  app.all("*paths", async (req, res) => {
19
19
  const internalEvent = await converter.convertFrom(req);
20
- const _res = {
20
+ const streamCreator = {
21
21
  writeHeaders: (prelude) => {
22
22
  res.writeHead(prelude.statusCode, prelude.headers);
23
23
  return res;
24
24
  },
25
25
  onFinish: () => { },
26
26
  };
27
- await handler(internalEvent, _res);
27
+ await handler(internalEvent, { streamCreator });
28
28
  });
29
29
  const server = app.listen(Number.parseInt(process.env.PORT ?? "3000", 10), () => {
30
30
  console.log(`Server running on port ${process.env.PORT ?? 3000}`);
@@ -3,7 +3,7 @@ import { debug, error } from "../../adapters/logger";
3
3
  const wrapper = async (handler, converter) => {
4
4
  const server = createServer(async (req, res) => {
5
5
  const internalEvent = await converter.convertFrom(req);
6
- const _res = {
6
+ const streamCreator = {
7
7
  writeHeaders: (prelude) => {
8
8
  res.setHeader("Set-Cookie", prelude.cookies);
9
9
  res.writeHead(prelude.statusCode, prelude.headers);
@@ -19,7 +19,7 @@ const wrapper = async (handler, converter) => {
19
19
  res.end("OK");
20
20
  }
21
21
  else {
22
- await handler(internalEvent, _res);
22
+ await handler(internalEvent, { streamCreator });
23
23
  }
24
24
  });
25
25
  await new Promise((resolve) => {
@@ -2,15 +2,13 @@ import type { Plugin } from "esbuild";
2
2
  import type { MiddlewareInfo } from "../types/next-types.js";
3
3
  export interface IPluginSettings {
4
4
  nextDir: string;
5
- edgeFunctionHandlerPath?: string;
6
5
  middlewareInfo?: MiddlewareInfo;
7
6
  isInCloudfare?: boolean;
8
7
  }
9
8
  /**
10
9
  * @param opts.nextDir - The path to the .next directory
11
- * @param opts.edgeFunctionHandlerPath - The path to the edgeFunctionHandler.js file that we'll use to bundle the routing
12
10
  * @param opts.middlewareInfo - Information about the middleware
13
11
  * @param opts.isInCloudfare - Whether the code runs on the cloudflare runtime
14
12
  * @returns
15
13
  */
16
- export declare function openNextEdgePlugins({ nextDir, edgeFunctionHandlerPath, middlewareInfo, isInCloudfare, }: IPluginSettings): Plugin;
14
+ export declare function openNextEdgePlugins({ nextDir, middlewareInfo, isInCloudfare, }: IPluginSettings): Plugin;
@@ -1,17 +1,16 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import chalk from "chalk";
4
- import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "../adapters/config/util.js";
4
+ import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadFunctionsConfigManifest, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "../adapters/config/util.js";
5
5
  import logger from "../logger.js";
6
6
  import { getCrossPlatformPathRegex } from "../utils/regex.js";
7
7
  /**
8
8
  * @param opts.nextDir - The path to the .next directory
9
- * @param opts.edgeFunctionHandlerPath - The path to the edgeFunctionHandler.js file that we'll use to bundle the routing
10
9
  * @param opts.middlewareInfo - Information about the middleware
11
10
  * @param opts.isInCloudfare - Whether the code runs on the cloudflare runtime
12
11
  * @returns
13
12
  */
14
- export function openNextEdgePlugins({ nextDir, edgeFunctionHandlerPath, middlewareInfo, isInCloudfare, }) {
13
+ export function openNextEdgePlugins({ nextDir, middlewareInfo, isInCloudfare, }) {
15
14
  const entryFiles = middlewareInfo?.files.map((file) => path.join(nextDir, file)) ?? [];
16
15
  const routes = middlewareInfo
17
16
  ? [
@@ -27,14 +26,6 @@ export function openNextEdgePlugins({ nextDir, edgeFunctionHandlerPath, middlewa
27
26
  name: "opennext-edge",
28
27
  setup(build) {
29
28
  logger.debug(chalk.blue("OpenNext Edge plugin"));
30
- if (edgeFunctionHandlerPath) {
31
- // If we bundle the routing, we need to resolve the middleware
32
- build.onResolve({ filter: getCrossPlatformPathRegex("./middleware.mjs") }, () => {
33
- return {
34
- path: edgeFunctionHandlerPath,
35
- };
36
- });
37
- }
38
29
  build.onResolve({ filter: /\.(mjs|wasm)$/g }, () => {
39
30
  return {
40
31
  external: true,
@@ -129,6 +120,7 @@ ${contents}
129
120
  const MiddlewareManifest = loadMiddlewareManifest(nextDir);
130
121
  const AppPathsManifest = loadAppPathsManifest(nextDir);
131
122
  const AppPathRoutesManifest = loadAppPathRoutesManifest(nextDir);
123
+ const FunctionsConfigManifest = loadFunctionsConfigManifest(nextDir);
132
124
  const contents = `
133
125
  import path from "node:path";
134
126
 
@@ -151,6 +143,7 @@ ${contents}
151
143
  export const MiddlewareManifest = ${JSON.stringify(MiddlewareManifest)};
152
144
  export const AppPathsManifest = ${JSON.stringify(AppPathsManifest)};
153
145
  export const AppPathRoutesManifest = ${JSON.stringify(AppPathRoutesManifest)};
146
+ export const FunctionsConfigManifest = ${JSON.stringify(FunctionsConfigManifest)};
154
147
 
155
148
 
156
149
  process.env.NEXT_BUILD_ID = BuildId;
@@ -0,0 +1,2 @@
1
+ import type { Plugin } from "esbuild";
2
+ export declare function openNextExternalMiddlewarePlugin(functionPath: string): Plugin;
@@ -0,0 +1,10 @@
1
+ import { getCrossPlatformPathRegex } from "../utils/regex.js";
2
+ export function openNextExternalMiddlewarePlugin(functionPath) {
3
+ return {
4
+ name: "open-next-external-node-middleware",
5
+ setup(build) {
6
+ // If we bundle the routing, we need to resolve the middleware
7
+ build.onResolve({ filter: getCrossPlatformPathRegex("./middleware.mjs") }, () => ({ path: functionPath }));
8
+ },
9
+ };
10
+ }
@@ -1,4 +1,4 @@
1
- import { readFileSync } from "node:fs";
1
+ import { readFile } from "node:fs/promises";
2
2
  import chalk from "chalk";
3
3
  import logger from "../logger.js";
4
4
  import { getCrossPlatformPathRegex } from "../utils/regex.js";
@@ -44,7 +44,7 @@ export function openNextResolvePlugin({ overrides, fnName, }) {
44
44
  setup(build) {
45
45
  logger.debug(chalk.blue("OpenNext Resolve plugin"), fnName ? `for ${fnName}` : "");
46
46
  build.onLoad({ filter: getCrossPlatformPathRegex("core/resolve.js") }, async (args) => {
47
- let contents = readFileSync(args.path, "utf-8");
47
+ let contents = await readFile(args.path, "utf-8");
48
48
  const overridesEntries = Object.entries(overrides ?? {});
49
49
  for (let [overrideName, overrideValue] of overridesEntries) {
50
50
  if (!overrideValue) {
@@ -16,7 +16,7 @@ interface CachedRedirectValue {
16
16
  props: Object;
17
17
  }
18
18
  interface CachedRouteValue {
19
- kind: "ROUTE";
19
+ kind: "ROUTE" | "APP_ROUTE";
20
20
  body: Buffer;
21
21
  status: number;
22
22
  headers: Record<string, undefined | string | string[]>;
@@ -30,13 +30,21 @@ interface CachedImageValue {
30
30
  isStale?: boolean;
31
31
  }
32
32
  interface IncrementalCachedPageValue {
33
- kind: "PAGE";
33
+ kind: "PAGE" | "PAGES";
34
34
  html: string;
35
35
  pageData: Object;
36
36
  status?: number;
37
37
  headers?: Record<string, undefined | string>;
38
38
  }
39
- type IncrementalCacheValue = CachedRedirectValue | IncrementalCachedPageValue | CachedImageValue | CachedFetchValue | CachedRouteValue;
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
+ export type IncrementalCacheValue = CachedRedirectValue | IncrementalCachedPageValue | IncrementalCachedAppPageValue | CachedImageValue | CachedFetchValue | CachedRouteValue;
40
48
  export interface CacheHandlerContext {
41
49
  fs?: never;
42
50
  dev?: boolean;
@@ -54,9 +62,31 @@ export interface CacheHandlerValue {
54
62
  value: IncrementalCacheValue | null;
55
63
  }
56
64
  export type Extension = "cache" | "fetch";
65
+ type MetaHeaders = {
66
+ "x-next-cache-tags"?: string;
67
+ [k: string]: string | string[] | undefined;
68
+ };
57
69
  export interface Meta {
58
70
  status?: number;
59
- headers?: Record<string, undefined | string | string[]>;
71
+ headers?: MetaHeaders;
60
72
  postponed?: string;
61
73
  }
74
+ export type TagCacheMetaFile = {
75
+ tag: {
76
+ S: string;
77
+ };
78
+ path: {
79
+ S: string;
80
+ };
81
+ revalidatedAt: {
82
+ N: string;
83
+ };
84
+ };
85
+ export type IncrementalCacheContext = {
86
+ revalidate?: number | false | undefined;
87
+ fetchCache?: boolean | undefined;
88
+ fetchUrl?: string | undefined;
89
+ fetchIdx?: number | undefined;
90
+ tags?: string[] | undefined;
91
+ };
62
92
  export {};
@@ -2,7 +2,7 @@ import type { AsyncLocalStorage } from "node:async_hooks";
2
2
  import type { OutgoingHttpHeaders } from "node:http";
3
3
  import type { CDNInvalidationHandler, IncrementalCache, ProxyExternalRequest, Queue, TagCache } from "./overrides";
4
4
  import type { DetachedPromiseRunner } from "../utils/promise";
5
- import type { OpenNextConfig } from "./open-next";
5
+ import type { OpenNextConfig, WaitUntil } from "./open-next";
6
6
  export interface RequestData {
7
7
  geo?: {
8
8
  city?: string;
@@ -51,20 +51,28 @@ interface OpenNextRequestContext {
51
51
  pendingPromiseRunner: DetachedPromiseRunner;
52
52
  isISRRevalidation?: boolean;
53
53
  mergeHeadersPriority?: "middleware" | "handler";
54
+ lastModified?: number;
55
+ waitUntil?: WaitUntil;
54
56
  }
55
57
  declare global {
56
58
  /**
57
59
  * The cache adapter for incremental static regeneration.
58
- * Only available in main functions or in the external middleware with `enableCacheInterception` set to `true`.
59
- * Defined in `createMainHandler` or in `adapter/middleware.ts`.
60
+ * Only available in main functions and in the external middleware when `enableCacheInterception` is `true`.
61
+ * Defined in `createMainHandler` and in `adapters/middleware.ts`.
60
62
  */
61
63
  var incrementalCache: IncrementalCache;
62
64
  /**
63
65
  * The cache adapter for the tag cache.
64
- * Only available in main functions, the initializationFunction or in the external middleware with `enableCacheInterception` set to `true`.
65
- * Defined in `createMainHandler` or in `adapter/middleware.ts`.
66
+ * Only available in main functions and in the external middleware when `enableCacheInterception` is `true`.
67
+ * Defined in `createMainHandler` and in `adapters/middleware.ts`.
66
68
  */
67
69
  var tagCache: TagCache;
70
+ /**
71
+ * The queue that is used to handle ISR revalidation requests.
72
+ * Only available in main functions and in the external middleware when `enableCacheInterception` is `true`.
73
+ * Defined in `createMainHandler` and in `adapters/middleware.ts`.
74
+ */
75
+ var queue: Queue;
68
76
  /**
69
77
  * A boolean that indicates if the DynamoDB cache is disabled.
70
78
  * @deprecated This will be removed, use `globalThis.openNextConfig.dangerous?.disableTagCache` instead.
@@ -77,13 +85,6 @@ declare global {
77
85
  * Defined in esbuild banner for the cache adapter.
78
86
  */
79
87
  var disableIncrementalCache: boolean;
80
- /**
81
- * An object that contains the last modified time of the pages.
82
- * Only available in main functions.
83
- * TODO: Integrate this directly in the AsyncLocalStorage context
84
- * Defined in `createMainHandler`.
85
- */
86
- var lastModified: Record<string, number>;
87
88
  /**
88
89
  * A boolean that indicates if Next is V15 or higher.
89
90
  * Only available in the cache adapter.
@@ -93,7 +94,7 @@ declare global {
93
94
  /**
94
95
  * A boolean that indicates if the runtime is Edge.
95
96
  * Only available in `edge` runtime functions (i.e. external middleware or function with edge runtime).
96
- * Defined in the `edge-adapter.ts`.
97
+ * Defined in `adapters/edge-adapter.ts`.
97
98
  */
98
99
  var isEdgeRuntime: true;
99
100
  /**
@@ -105,40 +106,33 @@ declare global {
105
106
  /**
106
107
  * The fetch function that should be used to make requests during the execution of the function.
107
108
  * Used to bypass Next intercepting and caching the fetch calls. Only available in main functions.
108
- * Defined in the `server-adapter.ts` and in `adapters/middleware.ts`.
109
+ * Defined in `adapters/server-adapter.ts` and in `adapters/middleware.ts`.
109
110
  */
110
111
  var internalFetch: typeof fetch;
111
112
  /**
112
113
  * The Open Next configuration object.
113
114
  * Available in all functions.
114
- * Defined in the `createMainHandler` or in the `createGenericHandler`.
115
+ * Defined in `createMainHandler` and in the `createGenericHandler`.
115
116
  */
116
117
  var openNextConfig: Partial<OpenNextConfig>;
117
118
  /**
118
119
  * The name of the function that is currently being executed.
119
120
  * Only available in main functions.
120
- * Defined in the `createMainHandler`.
121
+ * Defined in `createMainHandler`.
121
122
  */
122
123
  var fnName: string | undefined;
123
124
  /**
124
125
  * The unique identifier of the server.
125
126
  * Only available in main functions.
126
- * Defined in the `createMainHandler`.
127
+ * Defined in `createMainHandler`.
127
128
  */
128
129
  var serverId: string;
129
130
  /**
130
131
  * The AsyncLocalStorage instance that is used to store the request context.
131
132
  * Only available in main, middleware and edge functions.
132
- * TODO: should be available everywhere in the future.
133
- * Defined in `requestHandler.ts`, `middleware.ts` and `edge-adapter.ts`.
133
+ * Defined in `requestHandler.ts`, `adapters/middleware.ts` and `adapters/edge-adapter.ts`.
134
134
  */
135
135
  var __openNextAls: AsyncLocalStorage<OpenNextRequestContext>;
136
- /**
137
- * The function that is used to run background tasks even after the response has been sent.
138
- * This one is defined by the wrapper function as most of them don't need or support this feature.
139
- * If not present, all the awaiting promises will be resolved before sending the response.
140
- */
141
- var openNextWaitUntil: ((promise: Promise<void>) => void) | undefined;
142
136
  /**
143
137
  * The entries object that contains the functions that are available in the function.
144
138
  * Only available in edge runtime functions.
@@ -173,12 +167,6 @@ declare global {
173
167
  * Defined in the esbuild banner.
174
168
  */
175
169
  var openNextVersion: string;
176
- /**
177
- * The queue that is used to handle ISR revalidation requests.
178
- * Only available in main functions and in the external middleware with `enableCacheInterception` set to `true`.
179
- * Defined in `createMainHandler` or in `adapter/middleware.ts`.
180
- */
181
- var queue: Queue;
182
170
  /**
183
171
  * The function that is used when resolving external rewrite requests.
184
172
  * Only available in main functions
@@ -152,4 +152,17 @@ export type Options = {
152
152
  isExternalRewrite?: boolean;
153
153
  };
154
154
  export type PluginHandler = (req: IncomingMessage, res: OpenNextNodeResponse, options: Options) => Promise<OpenNextNodeResponse | undefined>;
155
+ export interface FunctionsConfigManifest {
156
+ version: number;
157
+ functions: Record<string, {
158
+ maxDuration?: number | undefined;
159
+ runtime?: "nodejs";
160
+ matchers?: Array<{
161
+ regexp: string;
162
+ originalSource: string;
163
+ has?: Rewrite["has"];
164
+ missing?: Rewrite["has"];
165
+ }>;
166
+ }>;
167
+ }
155
168
  export {};
@@ -30,6 +30,7 @@ export interface StreamCreator {
30
30
  onWrite?: () => void;
31
31
  onFinish?: (length: number) => void;
32
32
  }
33
+ export type WaitUntil = (promise: Promise<void>) => void;
33
34
  export interface DangerousOptions {
34
35
  /**
35
36
  * The tag cache is used for revalidateTags and revalidatePath.
@@ -59,7 +60,7 @@ export interface DangerousOptions {
59
60
  export type BaseOverride = {
60
61
  name: string;
61
62
  };
62
- export type LazyLoadedOverride<T extends BaseOverride> = () => Promise<T>;
63
+ export type LazyLoadedOverride<T extends BaseOverride> = () => T | Promise<T>;
63
64
  export interface Origin {
64
65
  host: string;
65
66
  protocol: "http" | "https";
@@ -78,15 +79,16 @@ export interface RoutingResult {
78
79
  isExternalRewrite: boolean;
79
80
  origin: Origin | false;
80
81
  isISR: boolean;
81
- initialPath: string;
82
+ initialURL: string;
83
+ locale?: string;
82
84
  resolvedRoutes: ResolvedRoute[];
83
85
  }
84
86
  export interface MiddlewareResult extends RoutingResult, BaseEventOrResult<"middleware"> {
85
87
  }
86
88
  export type IncludedQueue = "sqs" | "sqs-lite" | "direct" | "dummy";
87
89
  export type IncludedIncrementalCache = "s3" | "s3-lite" | "multi-tier-ddb-s3" | "fs-dev" | "dummy";
88
- export type IncludedTagCache = "dynamodb" | "dynamodb-lite" | "fs-dev" | "dummy";
89
- export type IncludedImageLoader = "s3" | "host" | "fs-dev" | "dummy";
90
+ export type IncludedTagCache = "dynamodb" | "dynamodb-lite" | "dynamodb-nextMode" | "fs-dev" | "dummy";
91
+ export type IncludedImageLoader = "s3" | "s3-lite" | "host" | "fs-dev" | "dummy";
90
92
  export type IncludedOriginResolver = "pattern-env" | "dummy";
91
93
  export type IncludedWarmer = "aws-lambda" | "dummy";
92
94
  export type IncludedProxyExternalRequest = "node" | "fetch" | "dummy";
@@ -241,6 +243,11 @@ export interface OpenNextConfig {
241
243
  */
242
244
  middleware?: DefaultFunctionOptions & {
243
245
  external: true;
246
+ /**
247
+ * The runtime used by next for the middleware.
248
+ * @default "edge"
249
+ */
250
+ runtime?: "node" | "edge";
244
251
  /**
245
252
  * The override options for the middleware.
246
253
  * By default the lite override are used (.i.e. s3-lite, dynamodb-lite, sqs-lite)
@@ -295,7 +302,7 @@ export interface OpenNextConfig {
295
302
  * Supports only node runtime
296
303
  */
297
304
  initializationFunction?: DefaultFunctionOptions & {
298
- tagCache?: "dynamodb" | LazyLoadedOverride<TagCache>;
305
+ tagCache?: IncludedTagCache | LazyLoadedOverride<TagCache>;
299
306
  };
300
307
  /**
301
308
  * Dangerous options. This break some functionnality but can be useful in some cases.