@opennextjs/aws 3.9.16 → 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.
- package/dist/adapters/cache.d.ts +3 -1
- package/dist/adapters/cache.js +75 -17
- package/dist/adapters/composable-cache.d.ts +12 -0
- package/dist/adapters/composable-cache.js +82 -2
- package/dist/adapters/dynamo-provider.js +2 -0
- package/dist/adapters/revalidate.js +2 -1
- package/dist/build/compileCache.js +2 -3
- package/dist/build/copyTracedFiles.d.ts +1 -0
- package/dist/build/copyTracedFiles.js +20 -0
- package/dist/build/createAssets.js +4 -1
- package/dist/build/helper.d.ts +2 -10
- package/dist/build/helper.js +1 -55
- package/dist/build/patch/patches/patchNextServer.d.ts +1 -0
- package/dist/build/patch/patches/patchNextServer.js +19 -0
- package/dist/core/routing/cacheInterceptor.js +2 -1
- package/dist/core/routing/i18n/index.js +4 -2
- package/dist/overrides/proxyExternalRequest/fetch.js +10 -1
- package/dist/overrides/tagCache/dummy.js +3 -0
- package/dist/overrides/tagCache/dynamodb-lite.js +94 -8
- package/dist/overrides/tagCache/dynamodb-nextMode.d.ts +2 -1
- package/dist/overrides/tagCache/dynamodb-nextMode.js +112 -27
- package/dist/overrides/tagCache/dynamodb.js +104 -22
- package/dist/overrides/tagCache/fs-dev-nextMode.d.ts +2 -1
- package/dist/overrides/tagCache/fs-dev-nextMode.js +37 -6
- package/dist/overrides/tagCache/fs-dev.js +40 -8
- package/dist/types/cache.d.ts +20 -0
- package/dist/types/global.d.ts +5 -2
- package/dist/types/next-types.d.ts +1 -0
- package/dist/types/overrides.d.ts +42 -1
- package/dist/utils/cache.d.ts +22 -2
- package/dist/utils/cache.js +44 -4
- package/dist/utils/promise.js +2 -0
- package/dist/utils/requestCache.d.ts +20 -0
- package/dist/utils/requestCache.js +27 -0
- package/dist/utils/semver.d.ts +10 -0
- package/dist/utils/semver.js +55 -0
- package/package.json +1 -1
package/dist/adapters/cache.d.ts
CHANGED
|
@@ -11,7 +11,9 @@ export default class Cache {
|
|
|
11
11
|
getFetchCache(key: string, softTags?: string[], tags?: string[]): Promise<CacheHandlerValue | null>;
|
|
12
12
|
getIncrementalCache(key: string): Promise<CacheHandlerValue | null>;
|
|
13
13
|
set(key: string, data?: IncrementalCacheValue, ctx?: IncrementalCacheContext): Promise<void>;
|
|
14
|
-
revalidateTag(tags: string | string[]
|
|
14
|
+
revalidateTag(tags: string | string[], durations?: {
|
|
15
|
+
expire?: number;
|
|
16
|
+
}): Promise<void>;
|
|
15
17
|
private updateTagsOnSet;
|
|
16
18
|
private extractRevalidateForSet;
|
|
17
19
|
}
|
package/dist/adapters/cache.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { getTagsFromValue, hasBeenRevalidated, writeTags } from "../utils/cache";
|
|
1
|
+
import { getTagsFromValue, hasBeenRevalidated, isStale, writeTags, } from "../utils/cache";
|
|
2
2
|
import { isBinaryContentType } from "../utils/binary";
|
|
3
|
+
import { compareSemver } from "../utils/semver";
|
|
3
4
|
import { debug, error, warn } from "./logger";
|
|
4
5
|
export const SOFT_TAG_PREFIX = "_N_T_/";
|
|
5
6
|
function isFetchCache(options) {
|
|
@@ -57,8 +58,11 @@ export default class Cache {
|
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
}
|
|
61
|
+
const _isStale = cachedEntry.shouldBypassTagCache
|
|
62
|
+
? false
|
|
63
|
+
: await isStale(key, _tags, _lastModified);
|
|
60
64
|
return {
|
|
61
|
-
lastModified: _lastModified,
|
|
65
|
+
lastModified: _isStale ? 1 : _lastModified,
|
|
62
66
|
value: cachedEntry.value,
|
|
63
67
|
};
|
|
64
68
|
}
|
|
@@ -77,21 +81,27 @@ export default class Cache {
|
|
|
77
81
|
const cacheData = cachedEntry.value;
|
|
78
82
|
const meta = cacheData.meta;
|
|
79
83
|
const tags = getTagsFromValue(cacheData);
|
|
80
|
-
|
|
84
|
+
let _lastModified = cachedEntry.lastModified ?? Date.now();
|
|
81
85
|
const _hasBeenRevalidated = cachedEntry.shouldBypassTagCache
|
|
82
86
|
? false
|
|
83
87
|
: await hasBeenRevalidated(key, tags, cachedEntry);
|
|
84
88
|
if (_hasBeenRevalidated)
|
|
85
89
|
return null;
|
|
90
|
+
const _isStale = cachedEntry.shouldBypassTagCache
|
|
91
|
+
? false
|
|
92
|
+
: await isStale(key, tags, _lastModified);
|
|
86
93
|
const store = globalThis.__openNextAls.getStore();
|
|
87
94
|
if (store) {
|
|
88
|
-
store.lastModified = _lastModified;
|
|
95
|
+
store.lastModified = _isStale ? 1 : _lastModified;
|
|
96
|
+
_lastModified = store.lastModified;
|
|
89
97
|
}
|
|
90
98
|
if (cacheData?.type === "route") {
|
|
91
99
|
return {
|
|
92
100
|
lastModified: _lastModified,
|
|
93
101
|
value: {
|
|
94
|
-
kind: globalThis.
|
|
102
|
+
kind: compareSemver(globalThis.nextVersion, ">=", "15.0.0")
|
|
103
|
+
? "APP_ROUTE"
|
|
104
|
+
: "ROUTE",
|
|
95
105
|
body: Buffer.from(cacheData.body ?? Buffer.alloc(0), isBinaryContentType(String(meta?.headers?.["content-type"]))
|
|
96
106
|
? "base64"
|
|
97
107
|
: "utf8"),
|
|
@@ -101,7 +111,8 @@ export default class Cache {
|
|
|
101
111
|
};
|
|
102
112
|
}
|
|
103
113
|
if (cacheData?.type === "page" || cacheData?.type === "app") {
|
|
104
|
-
if (globalThis.
|
|
114
|
+
if (compareSemver(globalThis.nextVersion, ">=", "15.0.0") &&
|
|
115
|
+
cacheData?.type === "app") {
|
|
105
116
|
const segmentData = new Map();
|
|
106
117
|
if (cacheData.segmentData) {
|
|
107
118
|
for (const [segmentPath, segmentContent] of Object.entries(cacheData.segmentData ?? {})) {
|
|
@@ -124,7 +135,9 @@ export default class Cache {
|
|
|
124
135
|
return {
|
|
125
136
|
lastModified: _lastModified,
|
|
126
137
|
value: {
|
|
127
|
-
kind: globalThis.
|
|
138
|
+
kind: compareSemver(globalThis.nextVersion, ">=", "15.0.0")
|
|
139
|
+
? "PAGES"
|
|
140
|
+
: "PAGE",
|
|
128
141
|
html: cacheData.html,
|
|
129
142
|
pageData: cacheData.type === "page" ? cacheData.json : cacheData.rsc,
|
|
130
143
|
status: meta?.status,
|
|
@@ -256,7 +269,7 @@ export default class Cache {
|
|
|
256
269
|
detachedPromise?.resolve();
|
|
257
270
|
}
|
|
258
271
|
}
|
|
259
|
-
async revalidateTag(tags) {
|
|
272
|
+
async revalidateTag(tags, durations) {
|
|
260
273
|
const config = globalThis.openNextConfig.dangerous;
|
|
261
274
|
if (config?.disableTagCache || config?.disableIncrementalCache) {
|
|
262
275
|
return;
|
|
@@ -268,7 +281,25 @@ export default class Cache {
|
|
|
268
281
|
try {
|
|
269
282
|
if (globalThis.tagCache.mode === "nextMode") {
|
|
270
283
|
const paths = (await globalThis.tagCache.getPathsByTags?.(_tags)) ?? [];
|
|
271
|
-
|
|
284
|
+
const now = Date.now();
|
|
285
|
+
const tagsToWrite = _tags.map((tag) => {
|
|
286
|
+
if (durations) {
|
|
287
|
+
// Use provided durations
|
|
288
|
+
return {
|
|
289
|
+
tag,
|
|
290
|
+
stale: now,
|
|
291
|
+
expire: durations.expire !== undefined
|
|
292
|
+
? now + durations.expire * 1000
|
|
293
|
+
: undefined,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
// Immediate expiration, default behavior before next 16, now only with {expire: 0}
|
|
297
|
+
return {
|
|
298
|
+
tag,
|
|
299
|
+
expire: now,
|
|
300
|
+
};
|
|
301
|
+
});
|
|
302
|
+
await writeTags(tagsToWrite);
|
|
272
303
|
if (paths.length > 0) {
|
|
273
304
|
// TODO: we should introduce a new method in cdnInvalidationHandler to invalidate paths by tags for cdn that supports it
|
|
274
305
|
// It also means that we'll need to provide the tags used in every request to the wrapper or converter.
|
|
@@ -291,10 +322,25 @@ export default class Cache {
|
|
|
291
322
|
// Find all keys with the given tag
|
|
292
323
|
const paths = await globalThis.tagCache.getByTag(tag);
|
|
293
324
|
debug("Items", paths);
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
tag
|
|
297
|
-
|
|
325
|
+
const now = Date.now();
|
|
326
|
+
const toInsert = paths.map((path) => {
|
|
327
|
+
const baseEntry = { path, tag };
|
|
328
|
+
if (durations) {
|
|
329
|
+
// Use provided durations
|
|
330
|
+
return {
|
|
331
|
+
...baseEntry,
|
|
332
|
+
stale: now,
|
|
333
|
+
expire: durations.expire !== undefined
|
|
334
|
+
? now + durations.expire * 1000
|
|
335
|
+
: undefined,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
// Default behavior: immediate expiration
|
|
339
|
+
return {
|
|
340
|
+
...baseEntry,
|
|
341
|
+
expire: now,
|
|
342
|
+
};
|
|
343
|
+
});
|
|
298
344
|
// If the tag is a soft tag, we should also revalidate the hard tags
|
|
299
345
|
if (tag.startsWith(SOFT_TAG_PREFIX)) {
|
|
300
346
|
for (const path of paths) {
|
|
@@ -305,10 +351,22 @@ export default class Cache {
|
|
|
305
351
|
for (const hardTag of hardTags) {
|
|
306
352
|
const _paths = await globalThis.tagCache.getByTag(hardTag);
|
|
307
353
|
debug({ hardTag, _paths });
|
|
308
|
-
toInsert.push(..._paths.map((path) =>
|
|
309
|
-
path,
|
|
310
|
-
|
|
311
|
-
|
|
354
|
+
toInsert.push(..._paths.map((path) => {
|
|
355
|
+
const baseEntry = { path, tag: hardTag };
|
|
356
|
+
if (durations) {
|
|
357
|
+
return {
|
|
358
|
+
...baseEntry,
|
|
359
|
+
stale: now,
|
|
360
|
+
expire: durations.expire !== undefined
|
|
361
|
+
? now + durations.expire * 1000
|
|
362
|
+
: undefined,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
...baseEntry,
|
|
367
|
+
expire: now,
|
|
368
|
+
};
|
|
369
|
+
}));
|
|
312
370
|
}
|
|
313
371
|
}
|
|
314
372
|
}
|
|
@@ -21,5 +21,17 @@ declare const _default: {
|
|
|
21
21
|
*/
|
|
22
22
|
expireTags(...tags: string[]): Promise<void>;
|
|
23
23
|
receiveExpiredTags(...tags: string[]): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Added in Next.js 16. Updates tags with optional stale/expire durations.
|
|
26
|
+
* Mirrors the logic in `Cache.revalidateTag` but without CDN invalidation
|
|
27
|
+
* since composable cache keys are not URL paths.
|
|
28
|
+
*
|
|
29
|
+
* When `durations` is provided, marks tags as stale immediately and optionally
|
|
30
|
+
* sets an expiry timestamp. When omitted, immediately expires tags (no grace period).
|
|
31
|
+
* durations.expire is in seconds, but we convert it to milliseconds for storage and comparison.
|
|
32
|
+
*/
|
|
33
|
+
updateTags(tags: string[], durations?: {
|
|
34
|
+
expire?: number;
|
|
35
|
+
}): Promise<void>;
|
|
24
36
|
};
|
|
25
37
|
export default _default;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { writeTags } from "../utils/cache";
|
|
1
|
+
import { isStale, writeTags } from "../utils/cache";
|
|
2
2
|
import { fromReadableStream, toReadableStream } from "../utils/stream";
|
|
3
3
|
import { debug } from "./logger";
|
|
4
4
|
const pendingWritePromiseMap = new Map();
|
|
@@ -21,14 +21,22 @@ export default {
|
|
|
21
21
|
return undefined;
|
|
22
22
|
}
|
|
23
23
|
debug("composable cache result", result);
|
|
24
|
-
|
|
24
|
+
let revalidate = result.value.revalidate;
|
|
25
25
|
if (globalThis.tagCache.mode === "nextMode" &&
|
|
26
26
|
result.value.tags.length > 0) {
|
|
27
|
+
// We need to check if the tags associated with this entry has been revalidated
|
|
27
28
|
const hasBeenRevalidated = result.shouldBypassTagCache
|
|
28
29
|
? false
|
|
29
30
|
: await globalThis.tagCache.hasBeenRevalidated(result.value.tags, result.lastModified);
|
|
30
31
|
if (hasBeenRevalidated)
|
|
31
32
|
return undefined;
|
|
33
|
+
// Check if tags are stale – entry is valid but needs background revalidation
|
|
34
|
+
const isCacheStale = result.shouldBypassTagCache
|
|
35
|
+
? false
|
|
36
|
+
: await isStale(cacheKey, result.value.tags, result.lastModified);
|
|
37
|
+
if (isCacheStale) {
|
|
38
|
+
revalidate = -1;
|
|
39
|
+
}
|
|
32
40
|
}
|
|
33
41
|
else if (globalThis.tagCache.mode === "original" ||
|
|
34
42
|
globalThis.tagCache.mode === undefined) {
|
|
@@ -37,9 +45,17 @@ export default {
|
|
|
37
45
|
: (await globalThis.tagCache.getLastModified(cacheKey, result.lastModified)) === -1;
|
|
38
46
|
if (hasBeenRevalidated)
|
|
39
47
|
return undefined;
|
|
48
|
+
// Check if tags are stale – entry is valid but needs background revalidation
|
|
49
|
+
const isCacheStale = result.shouldBypassTagCache
|
|
50
|
+
? false
|
|
51
|
+
: await isStale(cacheKey, result.value.tags, result.lastModified);
|
|
52
|
+
if (isCacheStale) {
|
|
53
|
+
revalidate = -1;
|
|
54
|
+
}
|
|
40
55
|
}
|
|
41
56
|
return {
|
|
42
57
|
...result.value,
|
|
58
|
+
revalidate,
|
|
43
59
|
value: toReadableStream(result.value.value),
|
|
44
60
|
};
|
|
45
61
|
}
|
|
@@ -118,4 +134,68 @@ export default {
|
|
|
118
134
|
// This function does absolutely nothing
|
|
119
135
|
return;
|
|
120
136
|
},
|
|
137
|
+
/**
|
|
138
|
+
* Added in Next.js 16. Updates tags with optional stale/expire durations.
|
|
139
|
+
* Mirrors the logic in `Cache.revalidateTag` but without CDN invalidation
|
|
140
|
+
* since composable cache keys are not URL paths.
|
|
141
|
+
*
|
|
142
|
+
* When `durations` is provided, marks tags as stale immediately and optionally
|
|
143
|
+
* sets an expiry timestamp. When omitted, immediately expires tags (no grace period).
|
|
144
|
+
* durations.expire is in seconds, but we convert it to milliseconds for storage and comparison.
|
|
145
|
+
*/
|
|
146
|
+
async updateTags(tags, durations) {
|
|
147
|
+
const config = globalThis.openNextConfig.dangerous;
|
|
148
|
+
if (config?.disableTagCache || config?.disableIncrementalCache) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (tags.length === 0) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const now = Date.now();
|
|
156
|
+
if (globalThis.tagCache.mode === "nextMode") {
|
|
157
|
+
const tagsToWrite = tags.map((tag) => {
|
|
158
|
+
if (durations) {
|
|
159
|
+
return {
|
|
160
|
+
tag,
|
|
161
|
+
stale: now,
|
|
162
|
+
expire: durations.expire !== undefined
|
|
163
|
+
? now + durations.expire * 1000
|
|
164
|
+
: undefined,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// Default: immediate expiry, no grace period
|
|
168
|
+
return { tag, expire: now };
|
|
169
|
+
});
|
|
170
|
+
await writeTags(tagsToWrite);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// Original mode: resolve tag → path mappings first
|
|
174
|
+
const originalTagCache = globalThis.tagCache;
|
|
175
|
+
const pathsPerTag = await Promise.all(tags.map(async (tag) => {
|
|
176
|
+
const paths = await originalTagCache.getByTag(tag);
|
|
177
|
+
return paths.map((path) => {
|
|
178
|
+
if (durations) {
|
|
179
|
+
return {
|
|
180
|
+
path,
|
|
181
|
+
tag,
|
|
182
|
+
stale: now,
|
|
183
|
+
expire: durations.expire !== undefined
|
|
184
|
+
? now + durations.expire * 1000
|
|
185
|
+
: undefined,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
return { path, tag, expire: now };
|
|
189
|
+
});
|
|
190
|
+
}));
|
|
191
|
+
const toWrite = pathsPerTag.flat();
|
|
192
|
+
if (toWrite.length > 0) {
|
|
193
|
+
await writeTags(toWrite);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
debug("Failed to update tags", e);
|
|
199
|
+
}
|
|
200
|
+
},
|
|
121
201
|
};
|
|
@@ -30,6 +30,8 @@ async function insert(requestType) {
|
|
|
30
30
|
tag: item.tag.S,
|
|
31
31
|
path: item.path.S,
|
|
32
32
|
revalidatedAt: Number.parseInt(item.revalidatedAt.N),
|
|
33
|
+
...(item.stale && { stale: Number.parseInt(item.stale.N) }),
|
|
34
|
+
...(item.expire && { expire: Number.parseInt(item.expire.N) }),
|
|
33
35
|
}));
|
|
34
36
|
await tagCache.writeTags(parsedData);
|
|
35
37
|
return {
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { createGenericHandler } from "../core/createGenericHandler.js";
|
|
5
5
|
import { debug, error } from "./logger.js";
|
|
6
6
|
const prerenderManifest = loadPrerenderManifest();
|
|
7
|
+
const ALLOWED_STATUS_CODES = [200, 307, 308, 404];
|
|
7
8
|
const defaultHandler = async (event) => {
|
|
8
9
|
const failedRecords = [];
|
|
9
10
|
for (const record of event.records) {
|
|
@@ -32,7 +33,7 @@ const defaultHandler = async (event) => {
|
|
|
32
33
|
headers: res.headers,
|
|
33
34
|
statusCode: res.statusCode,
|
|
34
35
|
});
|
|
35
|
-
if (res.statusCode
|
|
36
|
+
if (!ALLOWED_STATUS_CODES.includes(res.statusCode ?? 0) ||
|
|
36
37
|
res.headers["x-nextjs-cache"] !== "REVALIDATED") {
|
|
37
38
|
failedRecords.push(record);
|
|
38
39
|
}
|
|
@@ -11,7 +11,6 @@ export function compileCache(options, format = "cjs") {
|
|
|
11
11
|
const { config } = options;
|
|
12
12
|
const ext = format === "cjs" ? "cjs" : "mjs";
|
|
13
13
|
const compiledCacheFile = path.join(options.buildDir, `cache.${ext}`);
|
|
14
|
-
const isAfter15 = buildHelper.compareSemver(options.nextVersion, ">=", "15.0.0");
|
|
15
14
|
// Normal cache
|
|
16
15
|
buildHelper.esbuildSync({
|
|
17
16
|
external: ["next", "styled-jsx", "react", "@aws-sdk/*"],
|
|
@@ -23,7 +22,7 @@ export function compileCache(options, format = "cjs") {
|
|
|
23
22
|
js: [
|
|
24
23
|
`globalThis.disableIncrementalCache = ${config.dangerous?.disableIncrementalCache ?? false};`,
|
|
25
24
|
`globalThis.disableDynamoDBCache = ${config.dangerous?.disableTagCache ?? false};`,
|
|
26
|
-
`globalThis.
|
|
25
|
+
`globalThis.nextVersion = "${options.nextVersion}";`,
|
|
27
26
|
].join(""),
|
|
28
27
|
},
|
|
29
28
|
}, options);
|
|
@@ -41,7 +40,7 @@ export function compileCache(options, format = "cjs") {
|
|
|
41
40
|
js: [
|
|
42
41
|
`globalThis.disableIncrementalCache = ${config.dangerous?.disableIncrementalCache ?? false};`,
|
|
43
42
|
`globalThis.disableDynamoDBCache = ${config.dangerous?.disableTagCache ?? false};`,
|
|
44
|
-
`globalThis.
|
|
43
|
+
`globalThis.nextVersion = "${options.nextVersion}";`,
|
|
45
44
|
].join(""),
|
|
46
45
|
},
|
|
47
46
|
}, options);
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export declare function copyFileAndMakeOwnerWritable(src: string, dest: string): void;
|
|
8
8
|
export declare function isExcluded(srcPath: string): boolean;
|
|
9
|
+
export declare function isNonLinuxPlatformPackage(srcPath: string): boolean;
|
|
9
10
|
interface CopyTracedFilesOptions {
|
|
10
11
|
buildOutputPath: string;
|
|
11
12
|
packagePath: string;
|
|
@@ -40,6 +40,12 @@ export function isExcluded(srcPath) {
|
|
|
40
40
|
escape: false,
|
|
41
41
|
})));
|
|
42
42
|
}
|
|
43
|
+
const NON_LINUX_PLATFORMS = ["darwin", "win32", "freebsd"];
|
|
44
|
+
const platformPattern = NON_LINUX_PLATFORMS.join("|");
|
|
45
|
+
const nonLinuxPlatformRegex = getCrossPlatformPathRegex(`/node_modules/(?:@[^/]+/)?(?:[^/]+-)?(${platformPattern})-[^/]+/`, { escape: false });
|
|
46
|
+
export function isNonLinuxPlatformPackage(srcPath) {
|
|
47
|
+
return nonLinuxPlatformRegex.test(srcPath);
|
|
48
|
+
}
|
|
43
49
|
function copyPatchFile(outputDir) {
|
|
44
50
|
const patchFile = path.join(__dirname, "patch", "patchedAsyncStorage.js");
|
|
45
51
|
const outputPatchFile = path.join(outputDir, "patchedAsyncStorage.cjs");
|
|
@@ -190,11 +196,22 @@ File ${serverPath} does not exist
|
|
|
190
196
|
const tracedFiles = [];
|
|
191
197
|
const erroredFiles = [];
|
|
192
198
|
//Actually copy the files
|
|
199
|
+
const skippedPlatformPackages = new Set();
|
|
193
200
|
filesToCopy.forEach((to, from) => {
|
|
194
201
|
// We don't want to copy excluded packages (e.g. sharp)
|
|
195
202
|
if (isExcluded(from)) {
|
|
196
203
|
return;
|
|
197
204
|
}
|
|
205
|
+
// Skip non-Linux platform-specific native binaries (e.g. @swc/core-darwin-arm64)
|
|
206
|
+
// Set OPEN_NEXT_SKIP_PLATFORM_FILTER=true to bypass this check
|
|
207
|
+
if (!process.env.OPEN_NEXT_SKIP_PLATFORM_FILTER &&
|
|
208
|
+
isNonLinuxPlatformPackage(from)) {
|
|
209
|
+
const match = from.match(/node_modules\/(?:\.pnpm\/.*\/node_modules\/)?((?:@[^/]+\/)?[^/]+)/);
|
|
210
|
+
if (match) {
|
|
211
|
+
skippedPlatformPackages.add(match[1]);
|
|
212
|
+
}
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
198
215
|
tracedFiles.push(to);
|
|
199
216
|
mkdirSync(path.dirname(to), { recursive: true });
|
|
200
217
|
let symlink = null;
|
|
@@ -229,6 +246,9 @@ File ${serverPath} does not exist
|
|
|
229
246
|
}
|
|
230
247
|
}
|
|
231
248
|
});
|
|
249
|
+
if (skippedPlatformPackages.size > 0) {
|
|
250
|
+
logger.debug("Skipped non-Linux platform packages:", [...skippedPlatformPackages].join(", "));
|
|
251
|
+
}
|
|
232
252
|
readdirSync(standaloneNextDir)
|
|
233
253
|
.filter((fileOrDir) => !statSync(path.join(standaloneNextDir, fileOrDir)).isDirectory())
|
|
234
254
|
.forEach((file) => {
|
|
@@ -39,7 +39,10 @@ export function createStaticAssets(options, { useBasePath = false } = {}) {
|
|
|
39
39
|
fs.copyFileSync(path.join(appBuildOutputPath, ".next/BUILD_ID"), path.join(outputPath, "BUILD_ID"));
|
|
40
40
|
fs.cpSync(path.join(appBuildOutputPath, ".next/static"), path.join(outputPath, "_next", "static"), { recursive: true });
|
|
41
41
|
if (fs.existsSync(appPublicPath)) {
|
|
42
|
-
fs.cpSync(appPublicPath, outputPath, {
|
|
42
|
+
fs.cpSync(appPublicPath, outputPath, {
|
|
43
|
+
recursive: true,
|
|
44
|
+
dereference: true,
|
|
45
|
+
});
|
|
43
46
|
}
|
|
44
47
|
const appSrcPath = fs.existsSync(path.join(appPath, "src"))
|
|
45
48
|
? "src/app"
|
package/dist/build/helper.d.ts
CHANGED
|
@@ -62,16 +62,8 @@ export declare function getHtmlPages(dotNextPath: string): Set<string>;
|
|
|
62
62
|
export declare function getBuildId(options: BuildOptions): string;
|
|
63
63
|
export declare function getOpenNextVersion(): string;
|
|
64
64
|
export declare function getNextVersion(appPath: string): string;
|
|
65
|
-
export type SemverOp
|
|
66
|
-
|
|
67
|
-
* Compare two semver versions.
|
|
68
|
-
*
|
|
69
|
-
* @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.
|
|
70
|
-
* @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.
|
|
71
|
-
* @example
|
|
72
|
-
* compareSemver("2.0.0", ">=", "1.0.0") === true
|
|
73
|
-
*/
|
|
74
|
-
export declare function compareSemver(v1: string, operator: SemverOp, v2: string): boolean;
|
|
65
|
+
export type { SemverOp } from "../utils/semver.js";
|
|
66
|
+
export { compareSemver } from "../utils/semver.js";
|
|
75
67
|
/**
|
|
76
68
|
* Check if a Next.js version is supported by the Next.js team.
|
|
77
69
|
* Major versions are supported for 2 years following their initial release.
|
package/dist/build/helper.js
CHANGED
|
@@ -214,61 +214,7 @@ export function getNextVersion(appPath) {
|
|
|
214
214
|
// Drop the -canary.n suffix
|
|
215
215
|
return version.split("-")[0];
|
|
216
216
|
}
|
|
217
|
-
|
|
218
|
-
* Compare two semver versions.
|
|
219
|
-
*
|
|
220
|
-
* @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.
|
|
221
|
-
* @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.
|
|
222
|
-
* @example
|
|
223
|
-
* compareSemver("2.0.0", ">=", "1.0.0") === true
|
|
224
|
-
*/
|
|
225
|
-
export function compareSemver(v1, operator, v2) {
|
|
226
|
-
// - = 0 when versions are equal
|
|
227
|
-
// - > 0 if v1 > v2
|
|
228
|
-
// - < 0 if v2 > v1
|
|
229
|
-
let versionDiff = 0;
|
|
230
|
-
if (v1 === "latest") {
|
|
231
|
-
versionDiff = 1;
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
if (/^[^\d]/.test(v1)) {
|
|
235
|
-
// biome-ignore lint/style/noParameterAssign:
|
|
236
|
-
v1 = v1.substring(1);
|
|
237
|
-
}
|
|
238
|
-
if (/^[^\d]/.test(v2)) {
|
|
239
|
-
// biome-ignore lint/style/noParameterAssign:
|
|
240
|
-
v2 = v2.substring(1);
|
|
241
|
-
}
|
|
242
|
-
const [major1, minor1 = 0, patch1 = 0] = v1.split(".").map(Number);
|
|
243
|
-
const [major2, minor2 = 0, patch2 = 0] = v2.split(".").map(Number);
|
|
244
|
-
if (Number.isNaN(major1) || Number.isNaN(major2)) {
|
|
245
|
-
throw new Error("The major version is required.");
|
|
246
|
-
}
|
|
247
|
-
if (major1 !== major2) {
|
|
248
|
-
versionDiff = major1 - major2;
|
|
249
|
-
}
|
|
250
|
-
else if (minor1 !== minor2) {
|
|
251
|
-
versionDiff = minor1 - minor2;
|
|
252
|
-
}
|
|
253
|
-
else if (patch1 !== patch2) {
|
|
254
|
-
versionDiff = patch1 - patch2;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
switch (operator) {
|
|
258
|
-
case "=":
|
|
259
|
-
return versionDiff === 0;
|
|
260
|
-
case ">=":
|
|
261
|
-
return versionDiff >= 0;
|
|
262
|
-
case "<=":
|
|
263
|
-
return versionDiff <= 0;
|
|
264
|
-
case ">":
|
|
265
|
-
return versionDiff > 0;
|
|
266
|
-
case "<":
|
|
267
|
-
return versionDiff < 0;
|
|
268
|
-
default:
|
|
269
|
-
throw new Error(`Unsupported operator: ${operator}`);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
217
|
+
export { compareSemver } from "../utils/semver.js";
|
|
272
218
|
/**
|
|
273
219
|
* Next.js major version release dates (YYYY-MM-DD).
|
|
274
220
|
* Each major version is supported for 2 years from its release date.
|
|
@@ -2,6 +2,7 @@ import type { CodePatcher } from "../codePatcher.js";
|
|
|
2
2
|
export declare const disablePreloadingRule = "\nrule:\n kind: statement_block\n inside:\n kind: if_statement\n any:\n - has:\n kind: member_expression\n pattern: this.nextConfig.experimental.preloadEntriesOnStart\n stopBy: end\n - has:\n kind: binary_expression\n pattern: appDocumentPreloading === true\n stopBy: end\nfix:\n '{}'\n";
|
|
3
3
|
export declare const removeMiddlewareManifestRule = "\nrule:\n kind: statement_block\n inside:\n kind: method_definition\n has:\n kind: property_identifier\n regex: ^getMiddlewareManifest$\nfix:\n '{return null;}'\n";
|
|
4
4
|
export declare const emptyHandleNextImageRequestRule = "\nrule:\n kind: assignment_expression\n pattern: this.handleNextImageRequest = $VALUE\n inside:\n kind: method_definition\n stopBy: end\n has:\n kind: property_identifier\n regex: ^constructor$\n inside:\n kind: class_body\nfix:\n this.handleNextImageRequest = async (req, res, parsedUrl) => false\n";
|
|
5
|
+
export declare const provideInternalWaitUntil = "\nrule:\n kind: return_statement\n inside:\n kind: method_definition\n stopBy: end\n has:\n kind: property_identifier\n regex: getInternalWaitUntil\n\nfix:\n return globalThis.__openNextAls.getStore()?.waitUntil;\n";
|
|
5
6
|
/**
|
|
6
7
|
* Swaps the body for a throwing implementation
|
|
7
8
|
*
|
|
@@ -47,6 +47,19 @@ rule:
|
|
|
47
47
|
fix:
|
|
48
48
|
this.handleNextImageRequest = async (req, res, parsedUrl) => false
|
|
49
49
|
`;
|
|
50
|
+
export const provideInternalWaitUntil = `
|
|
51
|
+
rule:
|
|
52
|
+
kind: return_statement
|
|
53
|
+
inside:
|
|
54
|
+
kind: method_definition
|
|
55
|
+
stopBy: end
|
|
56
|
+
has:
|
|
57
|
+
kind: property_identifier
|
|
58
|
+
regex: getInternalWaitUntil
|
|
59
|
+
|
|
60
|
+
fix:
|
|
61
|
+
return globalThis.__openNextAls.getStore()?.waitUntil;
|
|
62
|
+
`;
|
|
50
63
|
/**
|
|
51
64
|
* Swaps the body for a throwing implementation
|
|
52
65
|
*
|
|
@@ -119,6 +132,12 @@ export const patchNextServer = {
|
|
|
119
132
|
contentFilter: /getMiddlewareManifest/,
|
|
120
133
|
patchCode: createPatchCode(removeMiddlewareManifestRule),
|
|
121
134
|
},
|
|
135
|
+
{
|
|
136
|
+
versions: ">=16.0.0",
|
|
137
|
+
pathFilter,
|
|
138
|
+
contentFilter: /getInternalWaitUntil/,
|
|
139
|
+
patchCode: createPatchCode(provideInternalWaitUntil),
|
|
140
|
+
},
|
|
122
141
|
...babelPatches,
|
|
123
142
|
],
|
|
124
143
|
};
|
|
@@ -91,7 +91,8 @@ function getBodyForAppRouter(event, cachedValue) {
|
|
|
91
91
|
try {
|
|
92
92
|
const segmentHeader = `${event.headers[NEXT_SEGMENT_PREFETCH_HEADER]}`;
|
|
93
93
|
const isSegmentResponse = Boolean(segmentHeader) &&
|
|
94
|
-
segmentHeader in (cachedValue.segmentData || {})
|
|
94
|
+
segmentHeader in (cachedValue.segmentData || {}) &&
|
|
95
|
+
!NextConfig.experimental?.prefetchInlining;
|
|
95
96
|
const body = isSegmentResponse
|
|
96
97
|
? cachedValue.segmentData[segmentHeader]
|
|
97
98
|
: cachedValue.rsc;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextConfig } from "../../../adapters/config/index.js";
|
|
2
2
|
import { emptyReadableStream } from "../../../utils/stream.js";
|
|
3
3
|
import { debug } from "../../../adapters/logger.js";
|
|
4
|
-
import { constructNextUrl } from "../util.js";
|
|
4
|
+
import { constructNextUrl, convertToQueryString } from "../util.js";
|
|
5
5
|
import { acceptLanguage } from "./accept-header";
|
|
6
6
|
function isLocalizedPath(path) {
|
|
7
7
|
return (NextConfig.i18n?.locales.includes(path.split("/")[1].toLowerCase()) ?? false);
|
|
@@ -119,11 +119,13 @@ export function handleLocaleRedirect(internalEvent) {
|
|
|
119
119
|
}
|
|
120
120
|
const defaultLocale = domainLocale?.defaultLocale ?? i18n.defaultLocale;
|
|
121
121
|
if (detectedLocale.toLowerCase() !== defaultLocale.toLowerCase()) {
|
|
122
|
+
const nextUrl = constructNextUrl(internalEvent.url, `/${detectedLocale}${NextConfig.trailingSlash ? "/" : ""}`);
|
|
123
|
+
const queryString = convertToQueryString(internalEvent.query);
|
|
122
124
|
return {
|
|
123
125
|
type: "core",
|
|
124
126
|
statusCode: 307,
|
|
125
127
|
headers: {
|
|
126
|
-
Location:
|
|
128
|
+
Location: `${nextUrl}${queryString}`,
|
|
127
129
|
},
|
|
128
130
|
body: emptyReadableStream(),
|
|
129
131
|
isBase64Encoded: false,
|
|
@@ -12,7 +12,16 @@ const fetchProxy = {
|
|
|
12
12
|
});
|
|
13
13
|
const responseHeaders = {};
|
|
14
14
|
response.headers.forEach((value, key) => {
|
|
15
|
-
responseHeaders[key]
|
|
15
|
+
const cur = responseHeaders[key];
|
|
16
|
+
if (cur === undefined) {
|
|
17
|
+
responseHeaders[key] = value;
|
|
18
|
+
}
|
|
19
|
+
else if (Array.isArray(cur)) {
|
|
20
|
+
cur.push(value);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
responseHeaders[key] = [cur, value];
|
|
24
|
+
}
|
|
16
25
|
});
|
|
17
26
|
return {
|
|
18
27
|
type: "core",
|