@opennextjs/aws 3.9.15 → 3.10.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 (38) hide show
  1. package/dist/adapters/cache.d.ts +3 -1
  2. package/dist/adapters/cache.js +84 -18
  3. package/dist/adapters/composable-cache.d.ts +12 -0
  4. package/dist/adapters/composable-cache.js +82 -2
  5. package/dist/adapters/dynamo-provider.js +2 -0
  6. package/dist/adapters/revalidate.js +2 -1
  7. package/dist/build/compileCache.js +2 -3
  8. package/dist/build/copyTracedFiles.d.ts +1 -0
  9. package/dist/build/copyTracedFiles.js +20 -0
  10. package/dist/build/createAssets.js +4 -1
  11. package/dist/build/helper.d.ts +7 -12
  12. package/dist/build/helper.js +19 -57
  13. package/dist/build/patch/patches/patchNextServer.d.ts +1 -0
  14. package/dist/build/patch/patches/patchNextServer.js +19 -0
  15. package/dist/build/patch/patches/patchOriginalNextConfig.js +4 -15
  16. package/dist/core/routing/cacheInterceptor.js +2 -1
  17. package/dist/core/routing/i18n/index.js +4 -2
  18. package/dist/overrides/proxyExternalRequest/fetch.js +10 -1
  19. package/dist/overrides/tagCache/dummy.js +3 -0
  20. package/dist/overrides/tagCache/dynamodb-lite.js +94 -8
  21. package/dist/overrides/tagCache/dynamodb-nextMode.d.ts +2 -1
  22. package/dist/overrides/tagCache/dynamodb-nextMode.js +112 -27
  23. package/dist/overrides/tagCache/dynamodb.js +104 -22
  24. package/dist/overrides/tagCache/fs-dev-nextMode.d.ts +2 -1
  25. package/dist/overrides/tagCache/fs-dev-nextMode.js +37 -6
  26. package/dist/overrides/tagCache/fs-dev.js +40 -8
  27. package/dist/types/cache.d.ts +21 -0
  28. package/dist/types/global.d.ts +5 -2
  29. package/dist/types/next-types.d.ts +1 -0
  30. package/dist/types/overrides.d.ts +42 -1
  31. package/dist/utils/cache.d.ts +22 -2
  32. package/dist/utils/cache.js +44 -4
  33. package/dist/utils/promise.js +2 -0
  34. package/dist/utils/requestCache.d.ts +20 -0
  35. package/dist/utils/requestCache.js +27 -0
  36. package/dist/utils/semver.d.ts +10 -0
  37. package/dist/utils/semver.js +55 -0
  38. package/package.json +6 -6
@@ -9,9 +9,9 @@ export default {
9
9
  }
10
10
  let lastRevalidated = 0;
11
11
  tags.forEach((tag) => {
12
- const tagTime = tagsMap.get(tag);
13
- if (tagTime && tagTime > lastRevalidated) {
14
- lastRevalidated = tagTime;
12
+ const tagData = tagsMap.get(tag);
13
+ if (tagData && tagData.revalidatedAt > lastRevalidated) {
14
+ lastRevalidated = tagData.revalidatedAt;
15
15
  }
16
16
  });
17
17
  debug("getLastRevalidated result:", lastRevalidated);
@@ -21,13 +21,37 @@ export default {
21
21
  if (globalThis.openNextConfig.dangerous?.disableTagCache) {
22
22
  return false;
23
23
  }
24
+ const now = Date.now();
24
25
  const hasRevalidatedTag = tags.some((tag) => {
25
- const tagRevalidatedAt = tagsMap.get(tag);
26
- return tagRevalidatedAt ? tagRevalidatedAt > (lastModified ?? 0) : false;
26
+ const tagData = tagsMap.get(tag);
27
+ if (!tagData) {
28
+ return false;
29
+ }
30
+ // Check if tag has expired
31
+ if (typeof tagData.expire === "number") {
32
+ const isExpired = tagData.expire <= now && tagData.expire > (lastModified ?? 0);
33
+ return isExpired;
34
+ }
35
+ // Check if tag has been revalidated
36
+ return tagData.revalidatedAt > (lastModified ?? 0);
27
37
  });
28
38
  debug("hasBeenRevalidated result:", hasRevalidatedTag);
29
39
  return hasRevalidatedTag;
30
40
  },
41
+ isStale: async (tags, lastModified) => {
42
+ if (globalThis.openNextConfig.dangerous?.disableTagCache) {
43
+ return false;
44
+ }
45
+ const hasStaleTag = tags.some((tag) => {
46
+ const tagData = tagsMap.get(tag);
47
+ if (!tagData || typeof tagData.stale !== "number") {
48
+ return false;
49
+ }
50
+ return tagData.stale > (lastModified ?? 0);
51
+ });
52
+ debug("isStale result:", hasStaleTag);
53
+ return hasStaleTag;
54
+ },
31
55
  writeTags: async (tags) => {
32
56
  if (globalThis.openNextConfig.dangerous?.disableTagCache ||
33
57
  tags.length === 0) {
@@ -35,7 +59,14 @@ export default {
35
59
  }
36
60
  debug("writeTags", { tags: tags });
37
61
  tags.forEach((tag) => {
38
- tagsMap.set(tag, Date.now());
62
+ const tagStr = typeof tag === "string" ? tag : tag.tag;
63
+ const stale = typeof tag === "string" ? undefined : tag.stale;
64
+ const expire = typeof tag === "string" ? undefined : tag.expire;
65
+ tagsMap.set(tagStr, {
66
+ revalidatedAt: Date.now(),
67
+ stale,
68
+ expire,
69
+ });
39
70
  });
40
71
  debug("writeTags completed, written", tags.length, "tags");
41
72
  },
@@ -22,18 +22,50 @@ const tagCache = {
22
22
  .map((tagEntry) => tagEntry.path.S.replace(`${NEXT_BUILD_ID}/`, ""));
23
23
  },
24
24
  getLastModified: async (path, lastModified) => {
25
- const revalidatedTags = tags.filter((tagPathMapping) => tagPathMapping.path.S === buildKey(path) &&
26
- Number.parseInt(tagPathMapping.revalidatedAt.N) > (lastModified ?? 0));
27
- return revalidatedTags.length > 0 ? -1 : (lastModified ?? Date.now());
25
+ // Check if any tag has expired
26
+ const now = Date.now();
27
+ const hasExpiredTag = tags.some((tagPathMapping) => {
28
+ if (tagPathMapping.path.S === buildKey(path) &&
29
+ tagPathMapping.expire?.N) {
30
+ const expiry = Number.parseInt(tagPathMapping.expire.N);
31
+ return expiry <= now && expiry > (lastModified ?? 0);
32
+ }
33
+ return false;
34
+ });
35
+ const nonExpiredRevalidatedTags = tags.filter((tagPathMapping) => tagPathMapping.path.S === buildKey(path) &&
36
+ Number.parseInt(tagPathMapping.revalidatedAt.N) > (lastModified ?? 0) &&
37
+ (!tagPathMapping.expire?.N ||
38
+ Number.parseInt(tagPathMapping.expire.N) > now));
39
+ return nonExpiredRevalidatedTags.length > 0 || hasExpiredTag
40
+ ? -1
41
+ : (lastModified ?? Date.now());
42
+ },
43
+ isStale: async (path, lastModified) => {
44
+ return tags.some((entry) => {
45
+ if (entry.path.S !== buildKey(path))
46
+ return false;
47
+ if (!entry.stale?.N)
48
+ return false;
49
+ return Number.parseInt(entry.stale.N) > (lastModified ?? 0);
50
+ });
28
51
  },
29
52
  writeTags: async (newTags) => {
30
53
  const newTagsSet = new Set(newTags.map(({ tag, path }) => `${buildKey(tag)}-${buildKey(path)}`));
31
54
  const unchangedTags = tags.filter(({ tag, path }) => !newTagsSet.has(`${tag.S}-${path.S}`));
32
- tags = unchangedTags.concat(newTags.map((item) => ({
33
- tag: { S: buildKey(item.tag) },
34
- path: { S: buildKey(item.path) },
35
- revalidatedAt: { N: `${item.revalidatedAt ?? Date.now()}` },
36
- })));
55
+ tags = unchangedTags.concat(newTags.map((item) => {
56
+ const tagEntry = {
57
+ tag: { S: buildKey(item.tag) },
58
+ path: { S: buildKey(item.path) },
59
+ revalidatedAt: { N: `${item.revalidatedAt ?? Date.now()}` },
60
+ ...(item.stale !== undefined
61
+ ? { stale: { N: `${item.stale}` } }
62
+ : undefined),
63
+ ...(item.expire !== undefined
64
+ ? { expire: { N: `${item.expire}` } }
65
+ : undefined),
66
+ };
67
+ return tagEntry;
68
+ }));
37
69
  },
38
70
  };
39
71
  export default tagCache;
@@ -44,6 +44,7 @@ interface IncrementalCachedAppPageValue {
44
44
  headers?: Record<string, undefined | string | string[]>;
45
45
  postponed?: string;
46
46
  status?: number;
47
+ segmentData?: Map<string, Buffer>;
47
48
  }
48
49
  export type IncrementalCacheValue = CachedRedirectValue | IncrementalCachedPageValue | IncrementalCachedAppPageValue | CachedImageValue | CachedFetchValue | CachedRouteValue;
49
50
  export interface CacheHandlerContext {
@@ -82,6 +83,18 @@ export type TagCacheMetaFile = {
82
83
  revalidatedAt: {
83
84
  N: string;
84
85
  };
86
+ /**
87
+ * The time at which the tag should be considered stale, in milliseconds since epoch. Optional, if not set the tag will never be stale.
88
+ */
89
+ stale?: {
90
+ N: string;
91
+ };
92
+ /**
93
+ * The time at which the tag should expire, in milliseconds since epoch. Optional, if not set the tag will never expire.
94
+ */
95
+ expire?: {
96
+ N: string;
97
+ };
85
98
  };
86
99
  interface SetIncrementalFetchCacheContext {
87
100
  fetchCache: true;
@@ -138,5 +151,13 @@ export interface ComposableCacheHandler {
138
151
  * This function is only there for older versions and do nothing
139
152
  */
140
153
  receiveExpiredTags(...tags: string[]): Promise<void>;
154
+ /**
155
+ * Added in Next.js 16. Updates tags with optional stale/expire durations.
156
+ * When durations is provided, marks tags as stale immediately and sets expiry;
157
+ * when omitted, immediately expires tags (same as expireTags).
158
+ */
159
+ updateTags(tags: string[], durations?: {
160
+ expire?: number;
161
+ }): Promise<void>;
141
162
  }
142
163
  export {};
@@ -2,6 +2,7 @@ import type { AsyncLocalStorage } from "node:async_hooks";
2
2
  import type { OutgoingHttpHeaders } from "node:http";
3
3
  import type { AssetResolver, CDNInvalidationHandler, IncrementalCache, ProxyExternalRequest, Queue, TagCache } from "./overrides";
4
4
  import type { DetachedPromiseRunner } from "../utils/promise";
5
+ import type { RequestCache } from "../utils/requestCache";
5
6
  import type { OpenNextConfig, WaitUntil } from "./open-next";
6
7
  export interface RequestData {
7
8
  geo?: {
@@ -55,6 +56,8 @@ interface OpenNextRequestContext {
55
56
  waitUntil?: WaitUntil;
56
57
  /** We use this to deduplicate write of the tags*/
57
58
  writtenTags: Set<string>;
59
+ /** Per-request in-memory cache. Overrides can use this to store data scoped to the current request. */
60
+ requestCache: RequestCache;
58
61
  }
59
62
  declare global {
60
63
  /**
@@ -88,11 +91,11 @@ declare global {
88
91
  */
89
92
  var disableIncrementalCache: boolean;
90
93
  /**
91
- * A boolean that indicates if Next is V15 or higher.
94
+ * The Next.js version of the application.
92
95
  * Only available in the cache adapter.
93
96
  * Defined in the esbuild banner for the cache adapter.
94
97
  */
95
- var isNextAfter15: boolean;
98
+ var nextVersion: string;
96
99
  /**
97
100
  * A boolean that indicates if the runtime is Edge.
98
101
  * Only available in `edge` runtime functions (i.e. external middleware or function with edge runtime).
@@ -73,6 +73,7 @@ export interface NextConfig {
73
73
  serverActions?: boolean;
74
74
  appDir?: boolean;
75
75
  optimizeCss?: boolean;
76
+ prefetchInlining?: boolean;
76
77
  };
77
78
  images: ImageConfig;
78
79
  poweredByHeader?: boolean;
@@ -107,17 +107,47 @@ Cons :
107
107
  - One page request (i.e. GET request) could require to check a lot of tags (And some of them multiple time when used with the fetch cache)
108
108
  - Almost impossible to do automatic cdn revalidation by itself
109
109
  */
110
+ export type NextModeTagCacheWriteInput = string | {
111
+ tag: string;
112
+ /**
113
+ * Timestamp in milliseconds since epoch after which the tag should be considered stale.
114
+ */
115
+ stale?: number;
116
+ /**
117
+ * Timestamp in milliseconds since epoch after which the tag should be considered expired.
118
+ */
119
+ expire?: number;
120
+ };
110
121
  export type NextModeTagCache = BaseTagCache & {
111
122
  mode: "nextMode";
112
123
  getLastRevalidated(tags: string[]): Promise<number>;
113
124
  hasBeenRevalidated(tags: string[], lastModified?: number): Promise<boolean>;
114
- writeTags(tags: string[]): Promise<void>;
125
+ writeTags(tags: NextModeTagCacheWriteInput[]): Promise<void>;
115
126
  getPathsByTags?: (tags: string[]) => Promise<string[]>;
127
+ /**
128
+ * Optional method to check if any tag has become stale (but not yet expired).
129
+ * There are three possible states for a cache entry:
130
+ * - **Fresh**: no tag has been revalidated since `lastModified` → returns `false`.
131
+ * - **Stale**: at least one tag was revalidated after `lastModified` but has not yet expired →
132
+ * returns `true`. The cache entry is still served but `revalidate` is set to `1` to trigger
133
+ * background revalidation.
134
+ * - **Expired**: at least one tag has fully expired → handled by `hasBeenRevalidated` returning
135
+ * `true`. This method is only called when `hasBeenRevalidated` returned `false`.
136
+ */
137
+ isStale?(tags: string[], lastModified?: number): Promise<boolean>;
116
138
  };
117
139
  export interface OriginalTagCacheWriteInput {
118
140
  tag: string;
119
141
  path: string;
120
142
  revalidatedAt?: number;
143
+ /**
144
+ * Timestamp in milliseconds since epoch after which the tag/path combination should be considered stale.
145
+ */
146
+ stale?: number;
147
+ /**
148
+ * Timestamp in milliseconds since epoch after which the tag/path combination should be considered expired.
149
+ */
150
+ expire?: number;
121
151
  }
122
152
  /**
123
153
  * On get :
@@ -144,6 +174,17 @@ export type OriginalTagCache = BaseTagCache & {
144
174
  getByPath(path: string): Promise<string[]>;
145
175
  getLastModified(path: string, lastModified?: number): Promise<number>;
146
176
  writeTags(tags: OriginalTagCacheWriteInput[]): Promise<void>;
177
+ /**
178
+ * Optional method to check if any tag entry for the given path has become stale (but not yet expired).
179
+ * There are three possible states for a cache entry:
180
+ * - **Fresh**: no tag/path combination has been revalidated since `lastModified` → returns `false`.
181
+ * - **Stale**: at least one tag/path combination was revalidated after `lastModified` but has not
182
+ * yet expired → returns `true`. The cache entry is still served but `revalidate` is set to `1`
183
+ * to trigger background revalidation.
184
+ * - **Expired**: at least one tag/path combination has fully expired → handled by `getLastModified`
185
+ * returning `-1`. This method is only called when the entry is not fully expired.
186
+ */
187
+ isStale?(path: string, lastModified?: number): Promise<boolean>;
147
188
  };
148
189
  export type TagCache = NextModeTagCache | OriginalTagCache;
149
190
  export type WrapperHandler<E extends BaseEventOrResult = InternalEvent, R extends BaseEventOrResult = InternalResult> = (handler: OpenNextHandler<E, R>, converter: Converter<E, R>) => Promise<(...args: any[]) => any>;
@@ -1,4 +1,24 @@
1
- import type { CacheValue, OriginalTagCacheWriteInput, WithLastModified } from "../types/overrides";
1
+ import type { CacheValue, NextModeTagCacheWriteInput, OriginalTagCacheWriteInput, WithLastModified } from "../types/overrides";
2
+ /**
3
+ *
4
+ * @param key The key for that specific cache entry
5
+ * @param tags Array of tags associated with that cache entry
6
+ * @param lastModified Time of the last update to the cache entry
7
+ * @returns A boolean indicating whether the cache entry has become stale -
8
+ * A cache entry is considered stale if at least one of its associated tags has been revalidated since the `lastModified` time, but none of them has expired yet.
9
+ * In this case, the cache entry is still valid and can be served, but it should trigger a background revalidation to update the cache.
10
+ */
11
+ export declare function isStale(key: string, tags: string[], lastModified?: number): Promise<boolean>;
12
+ /**
13
+ * @param key The key for that specific cache entry
14
+ * @param tags Array of tags associated with that cache entry
15
+ * @param cacheEntry The cache entry with its last modified time and value
16
+ * @returns A boolean indicating whether the cache entry has been revalidated -
17
+ * A cache entry is considered revalidated if at least one of its associated tags has been revalidated
18
+ * after the entry's `lastModified` time, meaning the cached data is stale and must be re-fetched.
19
+ * For Next 16+ you need {@link isStale}, to know if a revalidated entry is stale (valid but needs background revalidation) or expired (needs to be re-fetched immediately).
20
+ * Without it, we consider all revalidated entries as expired, which means that they will be re-fetched immediately without a chance to be served stale.
21
+ */
2
22
  export declare function hasBeenRevalidated(key: string, tags: string[], cacheEntry: WithLastModified<CacheValue<any>>): Promise<boolean>;
3
23
  export declare function getTagsFromValue(value?: CacheValue<"cache">): string[];
4
- export declare function writeTags(tags: (string | OriginalTagCacheWriteInput)[]): Promise<void>;
24
+ export declare function writeTags(tags: (string | OriginalTagCacheWriteInput | NextModeTagCacheWriteInput)[]): Promise<void>;
@@ -1,4 +1,39 @@
1
1
  import { debug } from "../adapters/logger";
2
+ import { compareSemver } from "./semver";
3
+ /**
4
+ *
5
+ * @param key The key for that specific cache entry
6
+ * @param tags Array of tags associated with that cache entry
7
+ * @param lastModified Time of the last update to the cache entry
8
+ * @returns A boolean indicating whether the cache entry has become stale -
9
+ * A cache entry is considered stale if at least one of its associated tags has been revalidated since the `lastModified` time, but none of them has expired yet.
10
+ * In this case, the cache entry is still valid and can be served, but it should trigger a background revalidation to update the cache.
11
+ */
12
+ export async function isStale(key, tags, lastModified) {
13
+ // SWR for revalidateTag has been implemented starting from Next.js 16
14
+ if (!compareSemver(globalThis.nextVersion, ">=", "16.0.0")) {
15
+ return false;
16
+ }
17
+ if (globalThis.openNextConfig.dangerous?.disableTagCache) {
18
+ return false;
19
+ }
20
+ if (globalThis.tagCache.mode === "nextMode") {
21
+ return tags.length === 0
22
+ ? false
23
+ : ((await globalThis.tagCache.isStale?.(tags, lastModified)) ?? false);
24
+ }
25
+ return (await globalThis.tagCache.isStale?.(key, lastModified)) ?? false;
26
+ }
27
+ /**
28
+ * @param key The key for that specific cache entry
29
+ * @param tags Array of tags associated with that cache entry
30
+ * @param cacheEntry The cache entry with its last modified time and value
31
+ * @returns A boolean indicating whether the cache entry has been revalidated -
32
+ * A cache entry is considered revalidated if at least one of its associated tags has been revalidated
33
+ * after the entry's `lastModified` time, meaning the cached data is stale and must be re-fetched.
34
+ * For Next 16+ you need {@link isStale}, to know if a revalidated entry is stale (valid but needs background revalidation) or expired (needs to be re-fetched immediately).
35
+ * Without it, we consider all revalidated entries as expired, which means that they will be re-fetched immediately without a chance to be served stale.
36
+ */
2
37
  export async function hasBeenRevalidated(key, tags, cacheEntry) {
3
38
  if (globalThis.openNextConfig.dangerous?.disableTagCache) {
4
39
  return false;
@@ -39,10 +74,15 @@ function getTagKey(tag) {
39
74
  if (typeof tag === "string") {
40
75
  return tag;
41
76
  }
42
- return JSON.stringify({
43
- tag: tag.tag,
44
- path: tag.path,
45
- });
77
+ // For OriginalTagCacheWriteInput, include path in the key
78
+ if ("path" in tag) {
79
+ return JSON.stringify({
80
+ tag: tag.tag,
81
+ path: tag.path,
82
+ });
83
+ }
84
+ // For NextModeTagCacheWriteInput, just use the tag
85
+ return tag.tag;
46
86
  }
47
87
  export async function writeTags(tags) {
48
88
  const store = globalThis.__openNextAls.getStore();
@@ -1,4 +1,5 @@
1
1
  import { debug, error } from "../adapters/logger";
2
+ import { RequestCache } from "./requestCache";
2
3
  /**
3
4
  * A `Promise.withResolvers` implementation that exposes the `resolve` and
4
5
  * `reject` functions on a `Promise`.
@@ -84,6 +85,7 @@ export function runWithOpenNextRequestContext({ isISRRevalidation, waitUntil, re
84
85
  isISRRevalidation,
85
86
  waitUntil,
86
87
  writtenTags: new Set(),
88
+ requestCache: new RequestCache(),
87
89
  }, async () => {
88
90
  provideNextAfterProvider();
89
91
  let result;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A per-request cache that provides named Map instances.
3
+ * Overrides can use this to store and share data within the scope of a single request
4
+ * without polluting global state.
5
+ *
6
+ * Retrieve it from the ALS context:
7
+ * ```ts
8
+ * const store = globalThis.__openNextAls.getStore();
9
+ * const myMap = store?.requestCache.getOrCreate<MyKey, MyValue>("my-override");
10
+ * ```
11
+ */
12
+ export declare class RequestCache {
13
+ private _caches;
14
+ /**
15
+ * Returns the Map registered under `key`.
16
+ * If no Map exists yet for that key, a new empty Map is created, stored, and returned.
17
+ * Repeated calls with the same key always return the **same** Map instance.
18
+ */
19
+ getOrCreate<K = unknown, V = unknown>(key: string): Map<K, V>;
20
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * A per-request cache that provides named Map instances.
3
+ * Overrides can use this to store and share data within the scope of a single request
4
+ * without polluting global state.
5
+ *
6
+ * Retrieve it from the ALS context:
7
+ * ```ts
8
+ * const store = globalThis.__openNextAls.getStore();
9
+ * const myMap = store?.requestCache.getOrCreate<MyKey, MyValue>("my-override");
10
+ * ```
11
+ */
12
+ export class RequestCache {
13
+ _caches = new Map();
14
+ /**
15
+ * Returns the Map registered under `key`.
16
+ * If no Map exists yet for that key, a new empty Map is created, stored, and returned.
17
+ * Repeated calls with the same key always return the **same** Map instance.
18
+ */
19
+ getOrCreate(key) {
20
+ let cache = this._caches.get(key);
21
+ if (!cache) {
22
+ cache = new Map();
23
+ this._caches.set(key, cache);
24
+ }
25
+ return cache;
26
+ }
27
+ }
@@ -0,0 +1,10 @@
1
+ export type SemverOp = "=" | ">=" | "<=" | ">" | "<";
2
+ /**
3
+ * Compare two semver versions.
4
+ *
5
+ * @param v1 - First version. Can be "latest", otherwise it should be a valid semver version in the format of `major.minor.patch`. Usually is the next version from the package.json without canary suffix. If minor or patch are missing, they are considered 0.
6
+ * @param v2 - Second version. Should not be "latest", it should be a valid semver version in the format of `major.minor.patch`. If minor or patch are missing, they are considered 0.
7
+ * @example
8
+ * compareSemver("2.0.0", ">=", "1.0.0") === true
9
+ */
10
+ export declare function compareSemver(v1: string, operator: SemverOp, v2: string): boolean;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Compare two semver versions.
3
+ *
4
+ * @param v1 - First version. Can be "latest", otherwise it should be a valid semver version in the format of `major.minor.patch`. Usually is the next version from the package.json without canary suffix. If minor or patch are missing, they are considered 0.
5
+ * @param v2 - Second version. Should not be "latest", it should be a valid semver version in the format of `major.minor.patch`. If minor or patch are missing, they are considered 0.
6
+ * @example
7
+ * compareSemver("2.0.0", ">=", "1.0.0") === true
8
+ */
9
+ export function compareSemver(v1, operator, v2) {
10
+ // - = 0 when versions are equal
11
+ // - > 0 if v1 > v2
12
+ // - < 0 if v2 > v1
13
+ let versionDiff = 0;
14
+ if (v1 === "latest") {
15
+ versionDiff = 1;
16
+ }
17
+ else {
18
+ if (/^[^\d]/.test(v1)) {
19
+ // biome-ignore lint/style/noParameterAssign:
20
+ v1 = v1.substring(1);
21
+ }
22
+ if (/^[^\d]/.test(v2)) {
23
+ // biome-ignore lint/style/noParameterAssign:
24
+ v2 = v2.substring(1);
25
+ }
26
+ const [major1, minor1 = 0, patch1 = 0] = v1.split(".").map(Number);
27
+ const [major2, minor2 = 0, patch2 = 0] = v2.split(".").map(Number);
28
+ if (Number.isNaN(major1) || Number.isNaN(major2)) {
29
+ throw new Error("The major version is required.");
30
+ }
31
+ if (major1 !== major2) {
32
+ versionDiff = major1 - major2;
33
+ }
34
+ else if (minor1 !== minor2) {
35
+ versionDiff = minor1 - minor2;
36
+ }
37
+ else if (patch1 !== patch2) {
38
+ versionDiff = patch1 - patch2;
39
+ }
40
+ }
41
+ switch (operator) {
42
+ case "=":
43
+ return versionDiff === 0;
44
+ case ">=":
45
+ return versionDiff >= 0;
46
+ case "<=":
47
+ return versionDiff <= 0;
48
+ case ">":
49
+ return versionDiff > 0;
50
+ case "<":
51
+ return versionDiff < 0;
52
+ default:
53
+ throw new Error(`Unsupported operator: ${operator}`);
54
+ }
55
+ }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "access": "public"
4
4
  },
5
5
  "name": "@opennextjs/aws",
6
- "version": "3.9.15",
6
+ "version": "3.10.0",
7
7
  "bin": {
8
8
  "open-next": "./dist/index.js"
9
9
  },
@@ -34,11 +34,11 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "@ast-grep/napi": "^0.40.5",
37
- "@aws-sdk/client-cloudfront": "3.398.0",
38
- "@aws-sdk/client-dynamodb": "^3.398.0",
39
- "@aws-sdk/client-lambda": "^3.398.0",
40
- "@aws-sdk/client-s3": "^3.398.0",
41
- "@aws-sdk/client-sqs": "^3.398.0",
37
+ "@aws-sdk/client-cloudfront": "3.984.0",
38
+ "@aws-sdk/client-dynamodb": "3.984.0",
39
+ "@aws-sdk/client-lambda": "3.984.0",
40
+ "@aws-sdk/client-s3": "3.984.0",
41
+ "@aws-sdk/client-sqs": "3.984.0",
42
42
  "@node-minify/core": "^8.0.6",
43
43
  "@node-minify/terser": "^8.0.6",
44
44
  "@tsconfig/node18": "^1.0.3",