@opennextjs/aws 3.3.0 → 3.4.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 (61) hide show
  1. package/dist/adapters/cache.d.ts +1 -2
  2. package/dist/adapters/cache.js +20 -11
  3. package/dist/adapters/config/index.d.ts +2 -0
  4. package/dist/adapters/config/index.js +4 -3
  5. package/dist/adapters/config/util.d.ts +2 -0
  6. package/dist/adapters/config/util.js +12 -2
  7. package/dist/adapters/middleware.js +13 -2
  8. package/dist/adapters/util.js +5 -1
  9. package/dist/build/createAssets.js +22 -23
  10. package/dist/build/createImageOptimizationBundle.js +2 -1
  11. package/dist/build/createServerBundle.js +4 -4
  12. package/dist/build/edge/createEdgeBundle.d.ts +4 -3
  13. package/dist/build/edge/createEdgeBundle.js +14 -22
  14. package/dist/build/validateConfig.js +2 -1
  15. package/dist/core/createMainHandler.js +2 -1
  16. package/dist/core/requestHandler.js +24 -6
  17. package/dist/core/resolve.d.ts +4 -0
  18. package/dist/core/resolve.js +10 -0
  19. package/dist/core/routing/middleware.js +3 -1
  20. package/dist/core/routing/routeMatcher.d.ts +10 -0
  21. package/dist/core/routing/routeMatcher.js +45 -0
  22. package/dist/core/routing/util.d.ts +4 -3
  23. package/dist/core/routing/util.js +24 -5
  24. package/dist/core/routingHandler.d.ts +3 -0
  25. package/dist/core/routingHandler.js +30 -29
  26. package/dist/overrides/cdnInvalidation/cloudfront.d.ts +9 -0
  27. package/dist/overrides/cdnInvalidation/cloudfront.js +26 -0
  28. package/dist/overrides/cdnInvalidation/dummy.d.ts +9 -0
  29. package/dist/overrides/cdnInvalidation/dummy.js +6 -0
  30. package/dist/overrides/imageLoader/fs-dev.d.ts +10 -0
  31. package/dist/overrides/imageLoader/fs-dev.js +14 -0
  32. package/dist/overrides/incrementalCache/fs-dev.d.ts +3 -0
  33. package/dist/overrides/incrementalCache/fs-dev.js +30 -0
  34. package/dist/overrides/incrementalCache/multi-tier-ddb-s3.d.ts +10 -0
  35. package/dist/overrides/incrementalCache/multi-tier-ddb-s3.js +120 -0
  36. package/dist/overrides/incrementalCache/s3-lite.d.ts +2 -0
  37. package/dist/overrides/incrementalCache/s3-lite.js +1 -1
  38. package/dist/overrides/proxyExternalRequest/node.js +6 -5
  39. package/dist/overrides/queue/direct.d.ts +3 -0
  40. package/dist/overrides/queue/direct.js +18 -0
  41. package/dist/overrides/tagCache/fs-dev.d.ts +3 -0
  42. package/dist/overrides/tagCache/fs-dev.js +32 -0
  43. package/dist/overrides/wrappers/cloudflare-edge.js +11 -4
  44. package/dist/overrides/wrappers/cloudflare-node.js +4 -1
  45. package/dist/overrides/wrappers/dummy.d.ts +1 -1
  46. package/dist/overrides/wrappers/dummy.js +6 -2
  47. package/dist/overrides/wrappers/express-dev.d.ts +7 -0
  48. package/dist/overrides/wrappers/express-dev.js +43 -0
  49. package/dist/plugins/edge.js +10 -4
  50. package/dist/plugins/replacement.d.ts +1 -1
  51. package/dist/plugins/replacement.js +1 -1
  52. package/dist/plugins/resolve.d.ts +1 -0
  53. package/dist/plugins/resolve.js +4 -1
  54. package/dist/types/global.d.ts +7 -1
  55. package/dist/types/open-next.d.ts +19 -6
  56. package/dist/types/overrides.d.ts +13 -4
  57. package/dist/utils/lru.d.ts +8 -0
  58. package/dist/utils/lru.js +29 -0
  59. package/dist/utils/regex.d.ts +18 -0
  60. package/dist/utils/regex.js +16 -0
  61. package/package.json +4 -1
@@ -0,0 +1,43 @@
1
+ import express from "express";
2
+ const wrapper = async (handler, converter) => {
3
+ const app = express();
4
+ // To serve static assets
5
+ app.use(express.static("../../assets"));
6
+ const imageHandlerPath = "../../image-optimization-function/index.mjs";
7
+ const imageHandler = await import(imageHandlerPath).then((m) => m.handler);
8
+ app.all("/_next/image", async (req, res) => {
9
+ const internalEvent = await converter.convertFrom(req);
10
+ const _res = {
11
+ writeHeaders: (prelude) => {
12
+ res.writeHead(prelude.statusCode, prelude.headers);
13
+ return res;
14
+ },
15
+ };
16
+ await imageHandler(internalEvent, _res);
17
+ });
18
+ app.all("*paths", async (req, res) => {
19
+ const internalEvent = await converter.convertFrom(req);
20
+ const _res = {
21
+ writeHeaders: (prelude) => {
22
+ res.writeHead(prelude.statusCode, prelude.headers);
23
+ return res;
24
+ },
25
+ onFinish: () => { },
26
+ };
27
+ await handler(internalEvent, _res);
28
+ });
29
+ const server = app.listen(Number.parseInt(process.env.PORT ?? "3000", 10), () => {
30
+ console.log(`Server running on port ${process.env.PORT ?? 3000}`);
31
+ });
32
+ app.on("error", (err) => {
33
+ console.error("error", err);
34
+ });
35
+ return () => {
36
+ server.close();
37
+ };
38
+ };
39
+ export default {
40
+ wrapper,
41
+ name: "expresss-dev",
42
+ supportStreaming: true,
43
+ };
@@ -1,8 +1,9 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import chalk from "chalk";
4
- import { loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "../adapters/config/util.js";
4
+ import { loadAppPathRoutesManifest, loadAppPathsManifest, loadAppPathsManifestKeys, loadBuildId, loadConfig, loadConfigHeaders, loadHtmlPages, loadMiddlewareManifest, loadPrerenderManifest, loadRoutesManifest, } from "../adapters/config/util.js";
5
5
  import logger from "../logger.js";
6
+ import { getCrossPlatformPathRegex } from "../utils/regex.js";
6
7
  /**
7
8
  * @param opts.nextDir - The path to the .next directory
8
9
  * @param opts.edgeFunctionHandlerPath - The path to the edgeFunctionHandler.js file that we'll use to bundle the routing
@@ -28,7 +29,7 @@ export function openNextEdgePlugins({ nextDir, edgeFunctionHandlerPath, middlewa
28
29
  logger.debug(chalk.blue("OpenNext Edge plugin"));
29
30
  if (edgeFunctionHandlerPath) {
30
31
  // If we bundle the routing, we need to resolve the middleware
31
- build.onResolve({ filter: /\.\/middleware.mjs/g }, () => {
32
+ build.onResolve({ filter: getCrossPlatformPathRegex("./middleware.mjs") }, () => {
32
33
  return {
33
34
  path: edgeFunctionHandlerPath,
34
35
  };
@@ -55,7 +56,7 @@ export function openNextEdgePlugins({ nextDir, edgeFunctionHandlerPath, middlewa
55
56
  loader: "js",
56
57
  }));
57
58
  // We inject the entry files into the edgeFunctionHandler
58
- build.onLoad({ filter: /\/edgeFunctionHandler.js/g }, async (args) => {
59
+ build.onLoad({ filter: getCrossPlatformPathRegex("/edgeFunctionHandler.js") }, async (args) => {
59
60
  let contents = readFileSync(args.path, "utf-8");
60
61
  contents = `
61
62
  globalThis._ENTRIES = {};
@@ -117,7 +118,7 @@ ${contents}
117
118
  contents,
118
119
  };
119
120
  });
120
- build.onLoad({ filter: /adapters\/config\/index/g }, async () => {
121
+ build.onLoad({ filter: getCrossPlatformPathRegex("adapters/config/index") }, async () => {
121
122
  const NextConfig = loadConfig(nextDir);
122
123
  const BuildId = loadBuildId(nextDir);
123
124
  const HtmlPages = loadHtmlPages(nextDir);
@@ -126,6 +127,8 @@ ${contents}
126
127
  const PrerenderManifest = loadPrerenderManifest(nextDir);
127
128
  const AppPathsManifestKeys = loadAppPathsManifestKeys(nextDir);
128
129
  const MiddlewareManifest = loadMiddlewareManifest(nextDir);
130
+ const AppPathsManifest = loadAppPathsManifest(nextDir);
131
+ const AppPathRoutesManifest = loadAppPathRoutesManifest(nextDir);
129
132
  const contents = `
130
133
  import path from "node:path";
131
134
 
@@ -146,6 +149,9 @@ ${contents}
146
149
  export const PrerenderManifest = ${JSON.stringify(PrerenderManifest)};
147
150
  export const AppPathsManifestKeys = ${JSON.stringify(AppPathsManifestKeys)};
148
151
  export const MiddlewareManifest = ${JSON.stringify(MiddlewareManifest)};
152
+ export const AppPathsManifest = ${JSON.stringify(AppPathsManifest)};
153
+ export const AppPathRoutesManifest = ${JSON.stringify(AppPathRoutesManifest)};
154
+
149
155
 
150
156
  process.env.NEXT_BUILD_ID = BuildId;
151
157
  `;
@@ -8,7 +8,7 @@ export interface IPluginSettings {
8
8
  /**
9
9
  *
10
10
  * openNextPlugin({
11
- * target: /plugins\/default\.js/g,
11
+ * target: /plugins(\/|\\)default\.js/g,
12
12
  * replacements: [require.resolve("./plugins/default.js")],
13
13
  * deletes: ["id1"],
14
14
  * })
@@ -6,7 +6,7 @@ const importPattern = /\/\/#import([\s\S]*?)\n\/\/#endImport/gm;
6
6
  /**
7
7
  *
8
8
  * openNextPlugin({
9
- * target: /plugins\/default\.js/g,
9
+ * target: /plugins(\/|\\)default\.js/g,
10
10
  * replacements: [require.resolve("./plugins/default.js")],
11
11
  * deletes: ["id1"],
12
12
  * })
@@ -12,6 +12,7 @@ export interface IPluginSettings {
12
12
  originResolver?: LazyLoadedOverride<OriginResolver> | IncludedOriginResolver;
13
13
  warmer?: LazyLoadedOverride<Warmer> | IncludedWarmer;
14
14
  proxyExternalRequest?: OverrideOptions["proxyExternalRequest"];
15
+ cdnInvalidation?: OverrideOptions["cdnInvalidation"];
15
16
  };
16
17
  fnName?: string;
17
18
  }
@@ -1,6 +1,7 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import chalk from "chalk";
3
3
  import logger from "../logger.js";
4
+ import { getCrossPlatformPathRegex } from "../utils/regex.js";
4
5
  function getOverrideOrDummy(override) {
5
6
  if (typeof override === "string") {
6
7
  return override;
@@ -19,6 +20,7 @@ const nameToFolder = {
19
20
  originResolver: "originResolver",
20
21
  warmer: "warmer",
21
22
  proxyExternalRequest: "proxyExternalRequest",
23
+ cdnInvalidation: "cdnInvalidation",
22
24
  };
23
25
  const defaultOverrides = {
24
26
  wrapper: "aws-lambda",
@@ -30,6 +32,7 @@ const defaultOverrides = {
30
32
  originResolver: "pattern-env",
31
33
  warmer: "aws-lambda",
32
34
  proxyExternalRequest: "node",
35
+ cdnInvalidation: "dummy",
33
36
  };
34
37
  /**
35
38
  * @param opts.overrides - The name of the overrides to use
@@ -40,7 +43,7 @@ export function openNextResolvePlugin({ overrides, fnName, }) {
40
43
  name: "opennext-resolve",
41
44
  setup(build) {
42
45
  logger.debug(chalk.blue("OpenNext Resolve plugin"), fnName ? `for ${fnName}` : "");
43
- build.onLoad({ filter: /core(\/|\\)resolve\.js/g }, async (args) => {
46
+ build.onLoad({ filter: getCrossPlatformPathRegex("core/resolve.js") }, async (args) => {
44
47
  let contents = readFileSync(args.path, "utf-8");
45
48
  const overridesEntries = Object.entries(overrides ?? {});
46
49
  for (let [overrideName, overrideValue] of overridesEntries) {
@@ -1,6 +1,6 @@
1
1
  import type { AsyncLocalStorage } from "node:async_hooks";
2
2
  import type { OutgoingHttpHeaders } from "node:http";
3
- import type { IncrementalCache, ProxyExternalRequest, Queue, TagCache } from "./overrides";
3
+ import type { CDNInvalidationHandler, IncrementalCache, ProxyExternalRequest, Queue, TagCache } from "./overrides";
4
4
  import type { DetachedPromiseRunner } from "../utils/promise";
5
5
  import type { OpenNextConfig } from "./open-next";
6
6
  export interface RequestData {
@@ -184,5 +184,11 @@ declare global {
184
184
  * Defined in `createMainHandler`.
185
185
  */
186
186
  var proxyExternalRequest: ProxyExternalRequest;
187
+ /**
188
+ * The function that will be called when the CDN needs invalidating (either from `revalidateTag` or from `res.revalidate`)
189
+ * Available in main functions
190
+ * Defined in `createMainHandler`
191
+ */
192
+ var cdnInvalidationHandler: CDNInvalidationHandler;
187
193
  }
188
194
  export {};
@@ -1,7 +1,7 @@
1
1
  import type { ReadableStream } from "node:stream/web";
2
2
  import type { Writable } from "node:stream";
3
3
  import type { WarmerEvent, WarmerResponse } from "../adapters/warmer-function";
4
- import type { Converter, ImageLoader, IncrementalCache, OriginResolver, ProxyExternalRequest, Queue, TagCache, Warmer, Wrapper } from "./overrides";
4
+ import type { CDNInvalidationHandler, Converter, ImageLoader, IncrementalCache, OriginResolver, ProxyExternalRequest, Queue, TagCache, Warmer, Wrapper } from "./overrides";
5
5
  export type BaseEventOrResult<T extends string = string> = {
6
6
  type: T;
7
7
  };
@@ -66,23 +66,31 @@ export interface Origin {
66
66
  port?: number;
67
67
  customHeaders?: Record<string, string>;
68
68
  }
69
- export type IncludedWrapper = "aws-lambda" | "aws-lambda-streaming" | "node" | "cloudflare" | "cloudflare-edge" | "cloudflare-node" | "dummy";
69
+ export type IncludedWrapper = "aws-lambda" | "aws-lambda-streaming" | "node" | "cloudflare" | "cloudflare-edge" | "cloudflare-node" | "express-dev" | "dummy";
70
70
  export type IncludedConverter = "aws-apigw-v2" | "aws-apigw-v1" | "aws-cloudfront" | "edge" | "node" | "sqs-revalidate" | "dummy";
71
+ export type RouteType = "route" | "page" | "app";
72
+ export interface ResolvedRoute {
73
+ route: string;
74
+ type: RouteType;
75
+ }
71
76
  export interface RoutingResult {
72
77
  internalEvent: InternalEvent;
73
78
  isExternalRewrite: boolean;
74
79
  origin: Origin | false;
75
80
  isISR: boolean;
81
+ initialPath: string;
82
+ resolvedRoutes: ResolvedRoute[];
76
83
  }
77
84
  export interface MiddlewareResult extends RoutingResult, BaseEventOrResult<"middleware"> {
78
85
  }
79
- export type IncludedQueue = "sqs" | "sqs-lite" | "dummy";
80
- export type IncludedIncrementalCache = "s3" | "s3-lite" | "dummy";
81
- export type IncludedTagCache = "dynamodb" | "dynamodb-lite" | "dummy";
82
- export type IncludedImageLoader = "s3" | "host" | "dummy";
86
+ export type IncludedQueue = "sqs" | "sqs-lite" | "direct" | "dummy";
87
+ 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";
83
90
  export type IncludedOriginResolver = "pattern-env" | "dummy";
84
91
  export type IncludedWarmer = "aws-lambda" | "dummy";
85
92
  export type IncludedProxyExternalRequest = "node" | "fetch" | "dummy";
93
+ export type IncludedCDNInvalidationHandler = "cloudfront" | "dummy";
86
94
  export interface DefaultOverrideOptions<E extends BaseEventOrResult = InternalEvent, R extends BaseEventOrResult = InternalResult> {
87
95
  /**
88
96
  * This is the main entrypoint of your app.
@@ -122,6 +130,11 @@ export interface OverrideOptions extends DefaultOverrideOptions {
122
130
  * @default "node"
123
131
  */
124
132
  proxyExternalRequest?: IncludedProxyExternalRequest | LazyLoadedOverride<ProxyExternalRequest>;
133
+ /**
134
+ * Add possibility to override the default cdn invalidation for On Demand Revalidation
135
+ * @default "dummy"
136
+ */
137
+ cdnInvalidation?: IncludedCDNInvalidationHandler | LazyLoadedOverride<CDNInvalidationHandler>;
125
138
  }
126
139
  export interface InstallOptions {
127
140
  /**
@@ -1,6 +1,6 @@
1
1
  import type { Readable } from "node:stream";
2
2
  import type { Meta } from "./cache";
3
- import type { BaseEventOrResult, BaseOverride, InternalEvent, InternalResult, Origin, StreamCreator } from "./open-next";
3
+ import type { BaseEventOrResult, BaseOverride, InternalEvent, InternalResult, Origin, ResolvedRoute, StreamCreator } from "./open-next";
4
4
  export interface QueueMessage {
5
5
  MessageDeduplicationId: string;
6
6
  MessageBody: {
@@ -13,7 +13,7 @@ export interface Queue {
13
13
  send(message: QueueMessage): Promise<void>;
14
14
  name: string;
15
15
  }
16
- export type S3CachedFile = {
16
+ export type CachedFile = {
17
17
  type: "redirect";
18
18
  props?: Object;
19
19
  meta?: Meta;
@@ -32,12 +32,12 @@ export type S3CachedFile = {
32
32
  body: string;
33
33
  meta?: Meta;
34
34
  };
35
- export type S3FetchCache = Object;
35
+ export type FetchCache = Object;
36
36
  export type WithLastModified<T> = {
37
37
  lastModified?: number;
38
38
  value?: T;
39
39
  };
40
- export type CacheValue<IsFetch extends boolean> = (IsFetch extends true ? S3FetchCache : S3CachedFile) & {
40
+ export type CacheValue<IsFetch extends boolean> = (IsFetch extends true ? FetchCache : CachedFile) & {
41
41
  revalidate?: number | false;
42
42
  };
43
43
  export type IncrementalCache = {
@@ -84,3 +84,12 @@ export type OriginResolver = BaseOverride & {
84
84
  export type ProxyExternalRequest = BaseOverride & {
85
85
  proxy: (event: InternalEvent) => Promise<InternalResult>;
86
86
  };
87
+ type CDNPath = {
88
+ initialPath: string;
89
+ rawPath: string;
90
+ resolvedRoutes: ResolvedRoute[];
91
+ };
92
+ export type CDNInvalidationHandler = BaseOverride & {
93
+ invalidatePaths: (paths: CDNPath[]) => Promise<void>;
94
+ };
95
+ export {};
@@ -0,0 +1,8 @@
1
+ export declare class LRUCache<T> {
2
+ private maxSize;
3
+ private cache;
4
+ constructor(maxSize: number);
5
+ get(key: string): T | undefined;
6
+ set(key: string, value: any): void;
7
+ delete(key: string): void;
8
+ }
@@ -0,0 +1,29 @@
1
+ export class LRUCache {
2
+ maxSize;
3
+ cache = new Map();
4
+ constructor(maxSize) {
5
+ this.maxSize = maxSize;
6
+ }
7
+ get(key) {
8
+ const result = this.cache.get(key);
9
+ // We could have used .has to allow for nullish value to be stored but we don't need that right now
10
+ if (result) {
11
+ // By removing and setting the key again we ensure it's the most recently used
12
+ this.cache.delete(key);
13
+ this.cache.set(key, result);
14
+ }
15
+ return result;
16
+ }
17
+ set(key, value) {
18
+ if (this.cache.size >= this.maxSize) {
19
+ const firstKey = this.cache.keys().next().value;
20
+ if (firstKey !== undefined) {
21
+ this.cache.delete(firstKey);
22
+ }
23
+ }
24
+ this.cache.set(key, value);
25
+ }
26
+ delete(key) {
27
+ this.cache.delete(key);
28
+ }
29
+ }
@@ -0,0 +1,18 @@
1
+ type Options = {
2
+ escape?: boolean;
3
+ flags?: string;
4
+ };
5
+ /**
6
+ * Constructs a regular expression for a path that supports separators for multiple platforms
7
+ * - Uses posix separators (`/`) as the input that should be made cross-platform.
8
+ * - Special characters are escaped by default but can be controlled through opts.escape.
9
+ * - Posix separators are always escaped.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * getCrossPlatformPathRegex("./middleware.mjs")
14
+ * getCrossPlatformPathRegex(String.raw`\./middleware\.(mjs|cjs)`, { escape: false })
15
+ * ```
16
+ */
17
+ export declare function getCrossPlatformPathRegex(regex: string, { escape: shouldEscape, flags }?: Options): RegExp;
18
+ export {};
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Constructs a regular expression for a path that supports separators for multiple platforms
3
+ * - Uses posix separators (`/`) as the input that should be made cross-platform.
4
+ * - Special characters are escaped by default but can be controlled through opts.escape.
5
+ * - Posix separators are always escaped.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * getCrossPlatformPathRegex("./middleware.mjs")
10
+ * getCrossPlatformPathRegex(String.raw`\./middleware\.(mjs|cjs)`, { escape: false })
11
+ * ```
12
+ */
13
+ export function getCrossPlatformPathRegex(regex, { escape: shouldEscape = true, flags = "g" } = {}) {
14
+ const newExpr = (shouldEscape ? regex.replace(/([[\]().*+?^$|{}\\])/g, "\\$1") : regex).replaceAll("/", String.raw `(?:\/|\\)`);
15
+ return new RegExp(newExpr, flags);
16
+ }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "access": "public"
4
4
  },
5
5
  "name": "@opennextjs/aws",
6
- "version": "3.3.0",
6
+ "version": "3.4.0",
7
7
  "bin": {
8
8
  "open-next": "./dist/index.js"
9
9
  },
@@ -30,6 +30,7 @@
30
30
  "README.md"
31
31
  ],
32
32
  "dependencies": {
33
+ "@aws-sdk/client-cloudfront": "3.398.0",
33
34
  "@aws-sdk/client-dynamodb": "^3.398.0",
34
35
  "@aws-sdk/client-lambda": "^3.398.0",
35
36
  "@aws-sdk/client-s3": "^3.398.0",
@@ -41,12 +42,14 @@
41
42
  "aws4fetch": "^1.0.18",
42
43
  "chalk": "^5.3.0",
43
44
  "esbuild": "0.19.2",
45
+ "express": "5.0.1",
44
46
  "path-to-regexp": "^6.3.0",
45
47
  "promise.series": "^0.2.0",
46
48
  "urlpattern-polyfill": "^10.0.0"
47
49
  },
48
50
  "devDependencies": {
49
51
  "@types/aws-lambda": "^8.10.109",
52
+ "@types/express": "5.0.0",
50
53
  "@types/node": "20.17.6",
51
54
  "tsc-alias": "^1.8.8",
52
55
  "typescript": "5.6.3"