@opennextjs/aws 3.7.1 → 3.7.3

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 (31) hide show
  1. package/dist/adapters/cache.d.ts +1 -0
  2. package/dist/adapters/cache.js +14 -7
  3. package/dist/adapters/composable-cache.js +20 -7
  4. package/dist/adapters/middleware.js +2 -1
  5. package/dist/build/generateOutput.js +1 -1
  6. package/dist/build/installDeps.js +22 -0
  7. package/dist/build/patch/codePatcher.d.ts +19 -12
  8. package/dist/build/patch/codePatcher.js +21 -38
  9. package/dist/build/patch/patches/patchBackgroundRevalidation.d.ts +2 -4
  10. package/dist/build/patch/patches/patchBackgroundRevalidation.js +2 -4
  11. package/dist/build/patch/patches/patchEnvVar.js +9 -15
  12. package/dist/build/patch/patches/patchFetchCacheISR.d.ts +3 -3
  13. package/dist/build/patch/patches/patchFetchCacheISR.js +13 -19
  14. package/dist/build/patch/patches/patchFetchCacheWaitUntil.js +3 -5
  15. package/dist/build/patch/patches/patchNextServer.js +18 -30
  16. package/dist/core/requestHandler.js +3 -1
  17. package/dist/core/routing/cacheInterceptor.js +8 -0
  18. package/dist/core/routing/middleware.d.ts +1 -0
  19. package/dist/core/routing/middleware.js +7 -1
  20. package/dist/core/routing/util.d.ts +13 -0
  21. package/dist/core/routing/util.js +34 -1
  22. package/dist/core/routingHandler.d.ts +1 -0
  23. package/dist/core/routingHandler.js +4 -3
  24. package/dist/http/openNextResponse.d.ts +1 -1
  25. package/dist/http/openNextResponse.js +4 -1
  26. package/dist/overrides/wrappers/express-dev.js +6 -0
  27. package/dist/plugins/content-updater.d.ts +4 -3
  28. package/dist/plugins/content-updater.js +4 -5
  29. package/dist/types/open-next.d.ts +2 -0
  30. package/dist/types/overrides.d.ts +5 -0
  31. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import type { CacheHandlerValue, IncrementalCacheContext, IncrementalCacheValue } from "../types/cache";
2
+ export declare const SOFT_TAG_PREFIX = "_N_T_/";
2
3
  export default class Cache {
3
4
  get(key: string, options?: boolean | {
4
5
  fetchCache?: boolean;
@@ -1,6 +1,7 @@
1
1
  import { getTagsFromValue, hasBeenRevalidated, writeTags } from "../utils/cache";
2
2
  import { isBinaryContentType } from "../utils/binary";
3
3
  import { debug, error, warn } from "./logger";
4
+ export const SOFT_TAG_PREFIX = "_N_T_/";
4
5
  function isFetchCache(options) {
5
6
  if (typeof options === "boolean") {
6
7
  return options;
@@ -34,18 +35,22 @@ export default class Cache {
34
35
  return null;
35
36
  const _tags = [...(tags ?? []), ...(softTags ?? [])];
36
37
  const _lastModified = cachedEntry.lastModified ?? Date.now();
37
- const _hasBeenRevalidated = await hasBeenRevalidated(key, _tags, cachedEntry);
38
+ const _hasBeenRevalidated = cachedEntry.shouldBypassTagCache
39
+ ? false
40
+ : await hasBeenRevalidated(key, _tags, cachedEntry);
38
41
  if (_hasBeenRevalidated)
39
42
  return null;
40
43
  // For cases where we don't have tags, we need to ensure that the soft tags are not being revalidated
41
44
  // We only need to check for the path as it should already contain all the tags
42
45
  if ((tags ?? []).length === 0) {
43
46
  // Then we need to find the path for the given key
44
- const path = softTags?.find((tag) => tag.startsWith("_N_T_/") &&
47
+ const path = softTags?.find((tag) => tag.startsWith(SOFT_TAG_PREFIX) &&
45
48
  !tag.endsWith("layout") &&
46
49
  !tag.endsWith("page"));
47
50
  if (path) {
48
- const hasPathBeenUpdated = await hasBeenRevalidated(path.replace("_N_T_/", ""), [], cachedEntry);
51
+ const hasPathBeenUpdated = cachedEntry.shouldBypassTagCache
52
+ ? false
53
+ : await hasBeenRevalidated(path.replace(SOFT_TAG_PREFIX, ""), [], cachedEntry);
49
54
  if (hasPathBeenUpdated) {
50
55
  // In case the path has been revalidated, we don't want to use the fetch cache
51
56
  return null;
@@ -73,7 +78,9 @@ export default class Cache {
73
78
  const meta = cacheData.meta;
74
79
  const tags = getTagsFromValue(cacheData);
75
80
  const _lastModified = cachedEntry.lastModified ?? Date.now();
76
- const _hasBeenRevalidated = await hasBeenRevalidated(key, tags, cachedEntry);
81
+ const _hasBeenRevalidated = cachedEntry.shouldBypassTagCache
82
+ ? false
83
+ : await hasBeenRevalidated(key, tags, cachedEntry);
77
84
  if (_hasBeenRevalidated)
78
85
  return null;
79
86
  const store = globalThis.__openNextAls.getStore();
@@ -274,11 +281,11 @@ export default class Cache {
274
281
  tag,
275
282
  }));
276
283
  // If the tag is a soft tag, we should also revalidate the hard tags
277
- if (tag.startsWith("_N_T_/")) {
284
+ if (tag.startsWith(SOFT_TAG_PREFIX)) {
278
285
  for (const path of paths) {
279
286
  // We need to find all hard tags for a given path
280
287
  const _tags = await globalThis.tagCache.getByPath(path);
281
- const hardTags = _tags.filter((t) => !t.startsWith("_N_T_/"));
288
+ const hardTags = _tags.filter((t) => !t.startsWith(SOFT_TAG_PREFIX));
282
289
  // For every hard tag, we need to find all paths and revalidate them
283
290
  for (const hardTag of hardTags) {
284
291
  const _paths = await globalThis.tagCache.getByTag(hardTag);
@@ -296,7 +303,7 @@ export default class Cache {
296
303
  // This only applies to `revalidateTag`, not to `res.revalidate()`
297
304
  const uniquePaths = Array.from(new Set(toInsert
298
305
  // We need to filter fetch cache key as they are not in the CDN
299
- .filter((t) => t.tag.startsWith("_N_T_/"))
306
+ .filter((t) => t.tag.startsWith(SOFT_TAG_PREFIX))
300
307
  .map((t) => `/${t.path}`)));
301
308
  if (uniquePaths.length > 0) {
302
309
  await globalThis.cdnInvalidationHandler.invalidatePaths(uniquePaths.map((path) => ({
@@ -8,7 +8,13 @@ export default {
8
8
  // We first check if we have a pending write for this cache key
9
9
  // If we do, we return the pending promise instead of fetching the cache
10
10
  if (pendingWritePromiseMap.has(cacheKey)) {
11
- return pendingWritePromiseMap.get(cacheKey);
11
+ const stored = pendingWritePromiseMap.get(cacheKey);
12
+ if (stored) {
13
+ return stored.then((entry) => ({
14
+ ...entry,
15
+ value: toReadableStream(entry.value),
16
+ }));
17
+ }
12
18
  }
13
19
  const result = await globalThis.incrementalCache.get(cacheKey, "composable");
14
20
  if (!result?.value?.value) {
@@ -18,13 +24,17 @@ export default {
18
24
  // We need to check if the tags associated with this entry has been revalidated
19
25
  if (globalThis.tagCache.mode === "nextMode" &&
20
26
  result.value.tags.length > 0) {
21
- const hasBeenRevalidated = await globalThis.tagCache.hasBeenRevalidated(result.value.tags, result.lastModified);
27
+ const hasBeenRevalidated = result.shouldBypassTagCache
28
+ ? false
29
+ : await globalThis.tagCache.hasBeenRevalidated(result.value.tags, result.lastModified);
22
30
  if (hasBeenRevalidated)
23
31
  return undefined;
24
32
  }
25
33
  else if (globalThis.tagCache.mode === "original" ||
26
34
  globalThis.tagCache.mode === undefined) {
27
- const hasBeenRevalidated = (await globalThis.tagCache.getLastModified(cacheKey, result.lastModified)) === -1;
35
+ const hasBeenRevalidated = result.shouldBypassTagCache
36
+ ? false
37
+ : (await globalThis.tagCache.getLastModified(cacheKey, result.lastModified)) === -1;
28
38
  if (hasBeenRevalidated)
29
39
  return undefined;
30
40
  }
@@ -39,14 +49,17 @@ export default {
39
49
  }
40
50
  },
41
51
  async set(cacheKey, pendingEntry) {
42
- pendingWritePromiseMap.set(cacheKey, pendingEntry);
43
- const entry = await pendingEntry.finally(() => {
52
+ const promiseEntry = pendingEntry.then(async (entry) => ({
53
+ ...entry,
54
+ value: await fromReadableStream(entry.value),
55
+ }));
56
+ pendingWritePromiseMap.set(cacheKey, promiseEntry);
57
+ const entry = await promiseEntry.finally(() => {
44
58
  pendingWritePromiseMap.delete(cacheKey);
45
59
  });
46
- const valueToStore = await fromReadableStream(entry.value);
47
60
  await globalThis.incrementalCache.set(cacheKey, {
48
61
  ...entry,
49
- value: valueToStore,
62
+ value: entry.value,
50
63
  }, "composable");
51
64
  if (globalThis.tagCache.mode === "original") {
52
65
  const storedTags = await globalThis.tagCache.getByPath(cacheKey);
@@ -3,7 +3,7 @@ import { debug, error } from "../adapters/logger";
3
3
  import { createGenericHandler } from "../core/createGenericHandler";
4
4
  import { resolveAssetResolver, resolveIncrementalCache, resolveOriginResolver, resolveProxyRequest, resolveQueue, resolveTagCache, } from "../core/resolve";
5
5
  import { constructNextUrl } from "../core/routing/util";
6
- import routingHandler, { INTERNAL_EVENT_REQUEST_ID, INTERNAL_HEADER_INITIAL_URL, INTERNAL_HEADER_RESOLVED_ROUTES, } from "../core/routingHandler";
6
+ import routingHandler, { INTERNAL_EVENT_REQUEST_ID, INTERNAL_HEADER_REWRITE_STATUS_CODE, INTERNAL_HEADER_INITIAL_URL, INTERNAL_HEADER_RESOLVED_ROUTES, } from "../core/routingHandler";
7
7
  globalThis.internalFetch = fetch;
8
8
  globalThis.__openNextAls = new AsyncLocalStorage();
9
9
  const defaultHandler = async (internalEvent, options) => {
@@ -39,6 +39,7 @@ const defaultHandler = async (internalEvent, options) => {
39
39
  [INTERNAL_HEADER_INITIAL_URL]: internalEvent.url,
40
40
  [INTERNAL_HEADER_RESOLVED_ROUTES]: JSON.stringify(result.resolvedRoutes),
41
41
  [INTERNAL_EVENT_REQUEST_ID]: requestId,
42
+ [INTERNAL_HEADER_REWRITE_STATUS_CODE]: String(result.rewriteStatusCode),
42
43
  },
43
44
  },
44
45
  isExternalRewrite: result.isExternalRewrite,
@@ -74,7 +74,7 @@ export async function generateOutput(options) {
74
74
  Object.entries(config.functions ?? {}).forEach(async ([key, value]) => {
75
75
  if (value.placement === "global") {
76
76
  edgeFunctions[key] = {
77
- bundle: `.open-next/functions/${key}`,
77
+ bundle: `.open-next/server-functions/${key}`,
78
78
  handler: indexHandler,
79
79
  ...(await extractOverrideFn(value.override)),
80
80
  };
@@ -35,6 +35,28 @@ export function installDependencies(outputDir, installOptions) {
35
35
  });
36
36
  // Copy the node_modules to the outputDir
37
37
  fs.cpSync(path.join(tempInstallDir, "node_modules"), path.join(outputDir, "node_modules"), { recursive: true, force: true, dereference: true });
38
+ // This is a workaround for Node `22.17.0` and `22.17.1`
39
+ // https://github.com/nodejs/node/issues/59168
40
+ const nodeVersion = process.version;
41
+ if (nodeVersion === "v22.17.0" || nodeVersion === "v22.17.1") {
42
+ const tempBinDir = path.join(tempInstallDir, "node_modules", ".bin");
43
+ const outputBinDir = path.join(outputDir, "node_modules", ".bin");
44
+ for (const fileName of fs.readdirSync(tempBinDir)) {
45
+ const symlinkPath = path.join(tempBinDir, fileName);
46
+ const stat = fs.lstatSync(symlinkPath);
47
+ if (stat.isSymbolicLink()) {
48
+ const linkTarget = fs.readlinkSync(symlinkPath);
49
+ const realFilePath = path.resolve(tempBinDir, linkTarget);
50
+ const outputFilePath = path.join(outputBinDir, fileName);
51
+ if (fs.existsSync(outputFilePath)) {
52
+ fs.unlinkSync(outputFilePath);
53
+ }
54
+ fs.copyFileSync(realFilePath, outputFilePath);
55
+ fs.chmodSync(outputFilePath, "755");
56
+ logger.debug(`Replaced symlink ${fileName} with actual file`);
57
+ }
58
+ }
59
+ }
38
60
  // Cleanup tempDir
39
61
  fs.rmSync(tempInstallDir, { recursive: true, force: true });
40
62
  logger.info(`Dependencies installed for ${name}`);
@@ -1,15 +1,14 @@
1
1
  import type { getManifests } from "../copyTracedFiles.js";
2
2
  import * as buildHelper from "../helper.js";
3
- type Versions = `>=${number}.${number}.${number} <=${number}.${number}.${number}` | `>=${number}.${number}.${number}` | `<=${number}.${number}.${number}`;
4
- export interface VersionedField<T> {
5
- /**
6
- * The versions of Next.js that this field should be used for
7
- * Should be in the format `">=16.0.0 <=17.0.0"` or `">=16.0.0"` or `"<=17.0.0"`
8
- * **Be careful with spaces**
9
- */
10
- versions?: Versions;
11
- field: T;
12
- }
3
+ /**
4
+ * Accepted formats:
5
+ * - `">=16.0.0"`
6
+ * - `"<=16.0.0"`
7
+ * - `">=16.0.0 <=17.0.0"`
8
+ *
9
+ * **Be careful with spaces**
10
+ */
11
+ export type Versions = `>=${number}.${number}.${number} <=${number}.${number}.${number}` | `>=${number}.${number}.${number}` | `<=${number}.${number}.${number}`;
13
12
  export type PatchCodeFn = (args: {
14
13
  /**
15
14
  * The code of the file that needs to be patched
@@ -32,15 +31,23 @@ interface IndividualPatch {
32
31
  pathFilter: RegExp;
33
32
  contentFilter?: RegExp;
34
33
  patchCode: PatchCodeFn;
34
+ versions?: Versions;
35
35
  }
36
36
  export interface CodePatcher {
37
37
  name: string;
38
- patches: IndividualPatch | VersionedField<IndividualPatch>[];
38
+ patches: IndividualPatch[];
39
39
  }
40
40
  export declare function parseVersions(versions?: Versions): {
41
41
  before?: string;
42
42
  after?: string;
43
43
  };
44
- export declare function extractVersionedField<T>(fields: VersionedField<T>[], version: string): T[];
44
+ /**
45
+ * Check whether the version is in the range
46
+ *
47
+ * @param version A semver version
48
+ * @param versionRange A version range
49
+ * @returns whether the version satisfies the range
50
+ */
51
+ export declare function isVersionInRange(version: string, versionRange?: Versions): boolean;
45
52
  export declare function applyCodePatches(buildOptions: buildHelper.BuildOptions, tracedFiles: string[], manifests: ReturnType<typeof getManifests>, codePatcher: CodePatcher[]): Promise<void>;
46
53
  export {};
@@ -39,50 +39,33 @@ export function parseVersions(versions) {
39
39
  after,
40
40
  };
41
41
  }
42
- export function extractVersionedField(fields, version) {
43
- const result = [];
44
- for (const field of fields) {
45
- // No versions specified, the patch always apply
46
- if (!field.versions) {
47
- result.push(field.field);
48
- continue;
49
- }
50
- const { before, after } = parseVersions(field.versions);
51
- // range
52
- if (before && after) {
53
- if (buildHelper.compareSemver(version, "<=", before) &&
54
- buildHelper.compareSemver(version, ">=", after)) {
55
- result.push(field.field);
56
- }
57
- continue;
58
- }
59
- // before only
60
- if (before) {
61
- if (buildHelper.compareSemver(version, "<=", before)) {
62
- result.push(field.field);
63
- }
64
- continue;
65
- }
66
- // after only
67
- if (after) {
68
- if (buildHelper.compareSemver(version, ">=", after)) {
69
- result.push(field.field);
70
- }
71
- }
42
+ /**
43
+ * Check whether the version is in the range
44
+ *
45
+ * @param version A semver version
46
+ * @param versionRange A version range
47
+ * @returns whether the version satisfies the range
48
+ */
49
+ export function isVersionInRange(version, versionRange) {
50
+ const { before, after } = parseVersions(versionRange);
51
+ let inRange = true;
52
+ if (before) {
53
+ inRange &&= buildHelper.compareSemver(version, "<=", before);
54
+ }
55
+ if (after) {
56
+ inRange &&= buildHelper.compareSemver(version, ">=", after);
72
57
  }
73
- return result;
58
+ return inRange;
74
59
  }
75
60
  export async function applyCodePatches(buildOptions, tracedFiles, manifests, codePatcher) {
76
- const nextVersion = buildOptions.nextVersion;
77
61
  logger.time("Applying code patches");
78
62
  // We first filter against the version
79
63
  // We also flatten the array of patches so that we get both the name and all the necessary patches
80
- const patchesToApply = codePatcher.flatMap(({ name, patches }) => Array.isArray(patches)
81
- ? extractVersionedField(patches, nextVersion).map((patch) => ({
82
- name,
83
- patch,
84
- }))
85
- : [{ name, patch: patches }]);
64
+ const patchesToApply = codePatcher.flatMap(({ name, patches }) => {
65
+ return patches
66
+ .filter(({ versions }) => isVersionInRange(buildOptions.nextVersion, versions))
67
+ .map((patch) => ({ patch, name }));
68
+ });
86
69
  await Promise.all(tracedFiles.map(async (filePath) => {
87
70
  // We check the filename against the filter to see if we should apply the patch
88
71
  const patchMatchingPath = patchesToApply.filter(({ patch }) => {
@@ -3,9 +3,7 @@ export declare const patchBackgroundRevalidation: {
3
3
  name: string;
4
4
  patches: {
5
5
  versions: ">=14.1.0";
6
- field: {
7
- pathFilter: RegExp;
8
- patchCode: import("../codePatcher.js").PatchCodeFn;
9
- };
6
+ pathFilter: RegExp;
7
+ patchCode: import("../codePatcher.js").PatchCodeFn;
10
8
  }[];
11
9
  };
@@ -18,10 +18,8 @@ export const patchBackgroundRevalidation = {
18
18
  {
19
19
  // TODO: test for earlier versions of Next
20
20
  versions: ">=14.1.0",
21
- field: {
22
- pathFilter: getCrossPlatformPathRegex("server/response-cache/index.js"),
23
- patchCode: createPatchCode(rule),
24
- },
21
+ pathFilter: getCrossPlatformPathRegex("server/response-cache/index.js"),
22
+ patchCode: createPatchCode(rule),
25
23
  },
26
24
  ],
27
25
  };
@@ -24,29 +24,23 @@ export const patchEnvVars = {
24
24
  // This patch will set the `NEXT_RUNTIME` env var to "node" to avoid loading unnecessary edge deps at runtime
25
25
  {
26
26
  versions: ">=15.0.0",
27
- field: {
28
- pathFilter: /module\.compiled\.js$/,
29
- contentFilter: /process\.env\.NEXT_RUNTIME/,
30
- patchCode: createPatchCode(envVarRuleCreator("NEXT_RUNTIME", '"node"')),
31
- },
27
+ pathFilter: /module\.compiled\.js$/,
28
+ contentFilter: /process\.env\.NEXT_RUNTIME/,
29
+ patchCode: createPatchCode(envVarRuleCreator("NEXT_RUNTIME", '"node"')),
32
30
  },
33
31
  // This patch will set `NODE_ENV` to production to avoid loading unnecessary dev deps at runtime
34
32
  {
35
33
  versions: ">=15.0.0",
36
- field: {
37
- pathFilter: /(module\.compiled|react\/index|react\/jsx-runtime|react-dom\/index)\.js$/,
38
- contentFilter: /process\.env\.NODE_ENV/,
39
- patchCode: createPatchCode(envVarRuleCreator("NODE_ENV", '"production"')),
40
- },
34
+ pathFilter: /(module\.compiled|react\/index|react\/jsx-runtime|react-dom\/index)\.js$/,
35
+ contentFilter: /process\.env\.NODE_ENV/,
36
+ patchCode: createPatchCode(envVarRuleCreator("NODE_ENV", '"production"')),
41
37
  },
42
38
  // This patch will set `TURBOPACK` env to false to avoid loading turbopack related deps at runtime
43
39
  {
44
40
  versions: ">=15.0.0",
45
- field: {
46
- pathFilter: /module\.compiled\.js$/,
47
- contentFilter: /process\.env\.TURBOPACK/,
48
- patchCode: createPatchCode(envVarRuleCreator("TURBOPACK", "false")),
49
- },
41
+ pathFilter: /module\.compiled\.js$/,
42
+ contentFilter: /process\.env\.TURBOPACK/,
43
+ patchCode: createPatchCode(envVarRuleCreator("TURBOPACK", "false")),
50
44
  },
51
45
  ],
52
46
  };
@@ -1,7 +1,7 @@
1
1
  import type { CodePatcher } from "../codePatcher.js";
2
- export declare const fetchRule = "\nrule:\n kind: member_expression\n pattern: $WORK_STORE.isOnDemandRevalidate\n inside:\n kind: ternary_expression\n all:\n - has: {kind: 'null'}\n - has: \n kind: await_expression\n has:\n kind: call_expression\n all:\n - has:\n kind: member_expression\n has:\n kind: property_identifier\n field: property\n regex: get\n - has:\n kind: arguments\n has:\n kind: object\n has:\n kind: pair\n all:\n - has:\n kind: property_identifier\n field: key\n regex: softTags\n inside:\n kind: variable_declarator\n\nfix:\n ($WORK_STORE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)\n";
3
- export declare const unstable_cacheRule = "\nrule:\n kind: member_expression\n pattern: $STORE_OR_CACHE.isOnDemandRevalidate\n inside:\n kind: if_statement\n stopBy: end\n has:\n kind: statement_block\n has:\n kind: variable_declarator\n has: \n kind: await_expression\n has:\n kind: call_expression\n all:\n - has:\n kind: member_expression\n has:\n kind: property_identifier\n field: property\n regex: get\n - has:\n kind: arguments\n has:\n kind: object\n has:\n kind: pair\n all:\n - has:\n kind: property_identifier\n field: key\n regex: softTags\n stopBy: end\nfix:\n ($STORE_OR_CACHE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)\n";
4
- export declare const useCacheRule = "\nrule:\n kind: member_expression\n pattern: $STORE_OR_CACHE.isOnDemandRevalidate\n inside:\n kind: binary_expression\n has: \n kind: member_expression\n pattern: $STORE_OR_CACHE.isDraftMode\n inside:\n kind: if_statement\n stopBy: end\n has: \n kind: return_statement\n any:\n - has:\n kind: 'true'\n - has:\n regex: '!0'\n stopBy: end\nfix:\n '($STORE_OR_CACHE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)'";
2
+ export declare const fetchRule = "\nrule:\n kind: member_expression\n pattern: $WORK_STORE.isOnDemandRevalidate\n inside:\n kind: ternary_expression\n all:\n - has: {kind: 'null'}\n - has:\n kind: await_expression\n has:\n kind: call_expression\n all:\n - has:\n kind: member_expression\n has:\n kind: property_identifier\n field: property\n regex: get\n - has:\n kind: arguments\n has:\n kind: object\n has:\n kind: pair\n all:\n - has:\n kind: property_identifier\n field: key\n regex: softTags\n inside:\n kind: variable_declarator\n\nfix:\n ($WORK_STORE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)\n";
3
+ export declare const unstable_cacheRule = "\nrule:\n kind: member_expression\n pattern: $STORE_OR_CACHE.isOnDemandRevalidate\n inside:\n kind: if_statement\n stopBy: end\n has:\n kind: statement_block\n has:\n kind: variable_declarator\n has:\n kind: await_expression\n has:\n kind: call_expression\n all:\n - has:\n kind: member_expression\n has:\n kind: property_identifier\n field: property\n regex: get\n - has:\n kind: arguments\n has:\n kind: object\n has:\n kind: pair\n all:\n - has:\n kind: property_identifier\n field: key\n regex: softTags\n stopBy: end\nfix:\n ($STORE_OR_CACHE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)\n";
4
+ export declare const useCacheRule = "\nrule:\n kind: member_expression\n pattern: $STORE_OR_CACHE.isOnDemandRevalidate\n inside:\n kind: binary_expression\n has:\n kind: member_expression\n pattern: $STORE_OR_CACHE.isDraftMode\n inside:\n kind: if_statement\n stopBy: end\n has:\n kind: return_statement\n any:\n - has:\n kind: 'true'\n - has:\n regex: '!0'\n stopBy: end\nfix:\n '($STORE_OR_CACHE.isOnDemandRevalidate && !globalThis.__openNextAls?.getStore()?.isISRRevalidation)'";
5
5
  export declare const patchFetchCacheForISR: CodePatcher;
6
6
  export declare const patchUnstableCacheForISR: CodePatcher;
7
7
  export declare const patchUseCacheForISR: CodePatcher;
@@ -9,7 +9,7 @@ rule:
9
9
  kind: ternary_expression
10
10
  all:
11
11
  - has: {kind: 'null'}
12
- - has:
12
+ - has:
13
13
  kind: await_expression
14
14
  has:
15
15
  kind: call_expression
@@ -48,7 +48,7 @@ rule:
48
48
  kind: statement_block
49
49
  has:
50
50
  kind: variable_declarator
51
- has:
51
+ has:
52
52
  kind: await_expression
53
53
  has:
54
54
  kind: call_expression
@@ -80,13 +80,13 @@ rule:
80
80
  pattern: $STORE_OR_CACHE.isOnDemandRevalidate
81
81
  inside:
82
82
  kind: binary_expression
83
- has:
83
+ has:
84
84
  kind: member_expression
85
85
  pattern: $STORE_OR_CACHE.isDraftMode
86
86
  inside:
87
87
  kind: if_statement
88
88
  stopBy: end
89
- has:
89
+ has:
90
90
  kind: return_statement
91
91
  any:
92
92
  - has:
@@ -101,11 +101,9 @@ export const patchFetchCacheForISR = {
101
101
  patches: [
102
102
  {
103
103
  versions: ">=14.0.0",
104
- field: {
105
- pathFilter: getCrossPlatformPathRegex(String.raw `(server/chunks/.*\.js|.*\.runtime\..*\.js|patch-fetch\.js)$`, { escape: false }),
106
- contentFilter: /\.isOnDemandRevalidate/,
107
- patchCode: createPatchCode(fetchRule, Lang.JavaScript),
108
- },
104
+ pathFilter: getCrossPlatformPathRegex(String.raw `(server/chunks/.*\.js|.*\.runtime\..*\.js|patch-fetch\.js)$`, { escape: false }),
105
+ contentFilter: /\.isOnDemandRevalidate/,
106
+ patchCode: createPatchCode(fetchRule, Lang.JavaScript),
109
107
  },
110
108
  ],
111
109
  };
@@ -114,11 +112,9 @@ export const patchUnstableCacheForISR = {
114
112
  patches: [
115
113
  {
116
114
  versions: ">=14.2.0",
117
- field: {
118
- pathFilter: getCrossPlatformPathRegex(String.raw `(server/chunks/.*\.js|.*\.runtime\..*\.js|spec-extension/unstable-cache\.js)$`, { escape: false }),
119
- contentFilter: /\.isOnDemandRevalidate/,
120
- patchCode: createPatchCode(unstable_cacheRule, Lang.JavaScript),
121
- },
115
+ pathFilter: getCrossPlatformPathRegex(String.raw `(server/chunks/.*\.js|.*\.runtime\..*\.js|spec-extension/unstable-cache\.js)$`, { escape: false }),
116
+ contentFilter: /\.isOnDemandRevalidate/,
117
+ patchCode: createPatchCode(unstable_cacheRule, Lang.JavaScript),
122
118
  },
123
119
  ],
124
120
  };
@@ -127,11 +123,9 @@ export const patchUseCacheForISR = {
127
123
  patches: [
128
124
  {
129
125
  versions: ">=15.3.0",
130
- field: {
131
- pathFilter: getCrossPlatformPathRegex(String.raw `(server/chunks/.*\.js|\.runtime\..*\.js|use-cache/use-cache-wrapper\.js)$`, { escape: false }),
132
- contentFilter: /\.isOnDemandRevalidate/,
133
- patchCode: createPatchCode(useCacheRule, Lang.JavaScript),
134
- },
126
+ pathFilter: getCrossPlatformPathRegex(String.raw `(server/chunks/.*\.js|\.runtime\..*\.js|use-cache/use-cache-wrapper\.js)$`, { escape: false }),
127
+ contentFilter: /\.isOnDemandRevalidate/,
128
+ patchCode: createPatchCode(useCacheRule, Lang.JavaScript),
135
129
  },
136
130
  ],
137
131
  };
@@ -26,11 +26,9 @@ export const patchFetchCacheSetMissingWaitUntil = {
26
26
  patches: [
27
27
  {
28
28
  versions: ">=15.0.0",
29
- field: {
30
- pathFilter: getCrossPlatformPathRegex(String.raw `(server/chunks/.*\.js|.*\.runtime\..*\.js|patch-fetch\.js)$`, { escape: false }),
31
- contentFilter: /arrayBuffer\(\)\s*\.then/,
32
- patchCode: createPatchCode(rule),
33
- },
29
+ pathFilter: getCrossPlatformPathRegex(String.raw `(server/chunks/.*\.js|.*\.runtime\..*\.js|patch-fetch\.js)$`, { escape: false }),
30
+ contentFilter: /arrayBuffer\(\)\s*\.then/,
31
+ patchCode: createPatchCode(rule),
34
32
  },
35
33
  ],
36
34
  };
@@ -71,27 +71,21 @@ const pathFilter = getCrossPlatformPathRegex(String.raw `/next/dist/server/next-
71
71
  const babelPatches = [
72
72
  // Empty the body of `NextServer#runMiddleware`
73
73
  {
74
- field: {
75
- pathFilter,
76
- contentFilter: /runMiddleware\(/,
77
- patchCode: createPatchCode(createEmptyBodyRule("runMiddleware")),
78
- },
74
+ pathFilter,
75
+ contentFilter: /runMiddleware\(/,
76
+ patchCode: createPatchCode(createEmptyBodyRule("runMiddleware")),
79
77
  },
80
78
  // Empty the body of `NextServer#runEdgeFunction`
81
79
  {
82
- field: {
83
- pathFilter,
84
- contentFilter: /runEdgeFunction\(/,
85
- patchCode: createPatchCode(createEmptyBodyRule("runEdgeFunction")),
86
- },
80
+ pathFilter,
81
+ contentFilter: /runEdgeFunction\(/,
82
+ patchCode: createPatchCode(createEmptyBodyRule("runEdgeFunction")),
87
83
  },
88
84
  // Drop `error-inspect` that pulls babel
89
85
  {
90
- field: {
91
- pathFilter,
92
- contentFilter: /error-inspect/,
93
- patchCode: createPatchCode(errorInspectRule),
94
- },
86
+ pathFilter,
87
+ contentFilter: /error-inspect/,
88
+ patchCode: createPatchCode(errorInspectRule),
95
89
  },
96
90
  ];
97
91
  export const patchNextServer = {
@@ -99,30 +93,24 @@ export const patchNextServer = {
99
93
  patches: [
100
94
  // Empty the body of `NextServer#imageOptimizer` - unused in OpenNext
101
95
  {
102
- field: {
103
- pathFilter,
104
- contentFilter: /imageOptimizer\(/,
105
- patchCode: createPatchCode(createEmptyBodyRule("imageOptimizer")),
106
- },
96
+ pathFilter,
97
+ contentFilter: /imageOptimizer\(/,
98
+ patchCode: createPatchCode(createEmptyBodyRule("imageOptimizer")),
107
99
  },
108
100
  // Disable Next background preloading done at creation of `NextServer`
109
101
  {
110
102
  versions: ">=14.0.0",
111
- field: {
112
- pathFilter,
113
- contentFilter: /this\.nextConfig\.experimental\.preloadEntriesOnStart/,
114
- patchCode: createPatchCode(disablePreloadingRule),
115
- },
103
+ pathFilter,
104
+ contentFilter: /this\.nextConfig\.experimental\.preloadEntriesOnStart/,
105
+ patchCode: createPatchCode(disablePreloadingRule),
116
106
  },
117
107
  // Don't match edge functions in `NextServer`
118
108
  {
119
109
  // Next 12 and some version of 13 use the bundled middleware/edge function
120
110
  versions: ">=14.0.0",
121
- field: {
122
- pathFilter,
123
- contentFilter: /getMiddlewareManifest/,
124
- patchCode: createPatchCode(removeMiddlewareManifestRule),
125
- },
111
+ pathFilter,
112
+ contentFilter: /getMiddlewareManifest/,
113
+ patchCode: createPatchCode(removeMiddlewareManifestRule),
126
114
  },
127
115
  ...babelPatches,
128
116
  ],
@@ -5,7 +5,7 @@ import { NextConfig } from "../adapters/config/index";
5
5
  import { debug, error } from "../adapters/logger";
6
6
  import { patchAsyncStorage } from "./patchAsyncStorage";
7
7
  import { constructNextUrl, convertRes, convertToQuery, convertToQueryString, createServerResponse, } from "./routing/util";
8
- import routingHandler, { INTERNAL_EVENT_REQUEST_ID, INTERNAL_HEADER_INITIAL_URL, INTERNAL_HEADER_RESOLVED_ROUTES, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_HEADER_PREFIX_LEN, } from "./routingHandler";
8
+ import routingHandler, { INTERNAL_EVENT_REQUEST_ID, INTERNAL_HEADER_REWRITE_STATUS_CODE, INTERNAL_HEADER_INITIAL_URL, INTERNAL_HEADER_RESOLVED_ROUTES, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_HEADER_PREFIX_LEN, } from "./routingHandler";
9
9
  import { requestHandler, setNextjsPrebundledReact } from "./util";
10
10
  // This is used to identify requests in the cache
11
11
  globalThis.__openNextAls = new AsyncLocalStorage();
@@ -37,6 +37,7 @@ export async function openNextHandler(internalEvent, options) {
37
37
  resolvedRoutes: initialHeaders[INTERNAL_HEADER_RESOLVED_ROUTES]
38
38
  ? JSON.parse(initialHeaders[INTERNAL_HEADER_RESOLVED_ROUTES])
39
39
  : [],
40
+ rewriteStatusCode: Number.parseInt(initialHeaders[INTERNAL_HEADER_REWRITE_STATUS_CODE]),
40
41
  };
41
42
  let routingResult = {
42
43
  internalEvent,
@@ -263,6 +264,7 @@ async function tryRenderError(type, res, internalEvent) {
263
264
  }
264
265
  catch (e) {
265
266
  error("NextJS request failed.", e);
267
+ res.statusCode = 500;
266
268
  res.setHeader("Content-Type", "application/json");
267
269
  res.end(JSON.stringify({
268
270
  message: "Server failed to respond.",
@@ -144,6 +144,14 @@ export async function cacheInterceptor(event) {
144
144
  if (Boolean(event.headers["next-action"]) ||
145
145
  Boolean(event.headers["x-prerender-revalidate"]))
146
146
  return event;
147
+ // Check for Next.js preview mode cookies
148
+ const cookies = event.headers.cookie || "";
149
+ const hasPreviewData = cookies.includes("__prerender_bypass") ||
150
+ cookies.includes("__next_preview_data");
151
+ if (hasPreviewData) {
152
+ debug("Preview mode detected, passing through to handler");
153
+ return event;
154
+ }
147
155
  // We localize the path in case i18n is enabled
148
156
  let localizedPath = localizePath(event);
149
157
  // If using basePath we need to remove it from the path
@@ -2,6 +2,7 @@ import type { InternalEvent, InternalResult } from "../../types/open-next.js";
2
2
  type MiddlewareEvent = InternalEvent & {
3
3
  responseHeaders?: Record<string, string | string[]>;
4
4
  isExternalRewrite?: boolean;
5
+ rewriteStatusCode?: number;
5
6
  };
6
7
  type Middleware = (request: Request) => Response | Promise<Response>;
7
8
  type MiddlewareLoader = () => Promise<{
@@ -2,10 +2,11 @@ import { FunctionsConfigManifest, MiddlewareManifest, NextConfig, PrerenderManif
2
2
  import { emptyReadableStream } from "../../utils/stream.js";
3
3
  import { getQueryFromSearchParams } from "../../overrides/converters/utils.js";
4
4
  import { localizePath } from "./i18n/index.js";
5
- import { convertBodyToReadableStream, getMiddlewareMatch, isExternal, } from "./util.js";
5
+ import { convertBodyToReadableStream, getMiddlewareMatch, isExternal, normalizeLocationHeader, } from "./util.js";
6
6
  const middlewareManifest = MiddlewareManifest;
7
7
  const functionsConfigManifest = FunctionsConfigManifest;
8
8
  const middleMatch = getMiddlewareMatch(middlewareManifest, functionsConfigManifest);
9
+ const REDIRECTS = new Set([301, 302, 303, 307, 308]);
9
10
  function defaultMiddlewareLoader() {
10
11
  // @ts-expect-error - This is bundled
11
12
  return import("./middleware.mjs");
@@ -94,6 +95,10 @@ export async function handleMiddleware(internalEvent, initialSearch, middlewareL
94
95
  ? [...resHeaders[key], value]
95
96
  : [value];
96
97
  }
98
+ else if (REDIRECTS.has(statusCode) &&
99
+ key.toLowerCase() === "location") {
100
+ resHeaders[key] = normalizeLocationHeader(value, internalEvent.url);
101
+ }
97
102
  else {
98
103
  resHeaders[key] = value;
99
104
  }
@@ -146,5 +151,6 @@ export async function handleMiddleware(internalEvent, initialSearch, middlewareL
146
151
  cookies: internalEvent.cookies,
147
152
  remoteAddress: internalEvent.remoteAddress,
148
153
  isExternalRewrite,
154
+ rewriteStatusCode: statusCode,
149
155
  };
150
156
  }
@@ -104,3 +104,16 @@ export declare function fixISRHeaders(headers: OutgoingHttpHeaders): void;
104
104
  */
105
105
  export declare function createServerResponse(routingResult: RoutingResult, headers: Record<string, string | string[] | undefined>, responseStream?: StreamCreator): OpenNextNodeResponse;
106
106
  export declare function invalidateCDNOnRequest(params: RoutingResult, headers: OutgoingHttpHeaders): Promise<void>;
107
+ /**
108
+ * Normalizes the Location header to either be a relative path or a full URL.
109
+ * If the Location header is relative to the origin, it will return a relative path.
110
+ * If it is an absolute URL, it will return the full URL.
111
+ * Redirects from Next config query parameters are encoded using `stringifyQs`
112
+ * Redirects from the middleware the query parameters are not encoded.
113
+ *
114
+ * @param location The Location header value
115
+ * @param baseUrl The base URL to use for relative paths (i.e the original request URL)
116
+ * @param encodeQuery Optional flag to indicate if query parameters should be encoded in the Location header
117
+ * @returns An absolute or relative Location header value
118
+ */
119
+ export declare function normalizeLocationHeader(location: string, baseUrl: string, encodeQuery?: boolean): string;
@@ -1,4 +1,5 @@
1
1
  import crypto from "node:crypto";
2
+ import { parse as parseQs, stringify as stringifyQs } from "node:querystring";
2
3
  import { Readable } from "node:stream";
3
4
  import { BuildId, HtmlPages, NextConfig } from "../../adapters/config/index.js";
4
5
  import { OpenNextNodeResponse } from "../../http/openNextResponse.js";
@@ -338,7 +339,7 @@ export function createServerResponse(routingResult, headers, responseStream) {
338
339
  }, async (_headers) => {
339
340
  await revalidateIfRequired(internalEvent.headers.host, internalEvent.rawPath, _headers);
340
341
  await invalidateCDNOnRequest(routingResult, _headers);
341
- }, responseStream, headers);
342
+ }, responseStream, headers, routingResult.rewriteStatusCode);
342
343
  }
343
344
  // This function is used only for `res.revalidate()`
344
345
  export async function invalidateCDNOnRequest(params, headers) {
@@ -356,3 +357,35 @@ export async function invalidateCDNOnRequest(params, headers) {
356
357
  ]);
357
358
  }
358
359
  }
360
+ /**
361
+ * Normalizes the Location header to either be a relative path or a full URL.
362
+ * If the Location header is relative to the origin, it will return a relative path.
363
+ * If it is an absolute URL, it will return the full URL.
364
+ * Redirects from Next config query parameters are encoded using `stringifyQs`
365
+ * Redirects from the middleware the query parameters are not encoded.
366
+ *
367
+ * @param location The Location header value
368
+ * @param baseUrl The base URL to use for relative paths (i.e the original request URL)
369
+ * @param encodeQuery Optional flag to indicate if query parameters should be encoded in the Location header
370
+ * @returns An absolute or relative Location header value
371
+ */
372
+ export function normalizeLocationHeader(location, baseUrl, encodeQuery = false) {
373
+ if (!URL.canParse(location)) {
374
+ // If the location is not a valid URL, return it as-is
375
+ return location;
376
+ }
377
+ const locationURL = new URL(location);
378
+ const origin = new URL(baseUrl).origin;
379
+ let search = locationURL.search;
380
+ // If encodeQuery is true, we need to encode the query parameters
381
+ // We could have used URLSearchParams, but that doesn't match what Next does.
382
+ if (encodeQuery && search) {
383
+ search = `?${stringifyQs(parseQs(search.slice(1)))}`;
384
+ }
385
+ const href = `${locationURL.origin}${locationURL.pathname}${search}${locationURL.hash}`;
386
+ // The URL is relative if the origin is the same as the base URL's origin
387
+ if (locationURL.origin === origin) {
388
+ return href.slice(origin.length);
389
+ }
390
+ return href;
391
+ }
@@ -6,6 +6,7 @@ export declare const INTERNAL_HEADER_PREFIX = "x-opennext-";
6
6
  export declare const INTERNAL_HEADER_INITIAL_URL = "x-opennext-initial-url";
7
7
  export declare const INTERNAL_HEADER_LOCALE = "x-opennext-locale";
8
8
  export declare const INTERNAL_HEADER_RESOLVED_ROUTES = "x-opennext-resolved-routes";
9
+ export declare const INTERNAL_HEADER_REWRITE_STATUS_CODE = "x-opennext-rewrite-status-code";
9
10
  export declare const INTERNAL_EVENT_REQUEST_ID = "x-opennext-request-id";
10
11
  export default function routingHandler(event: InternalEvent, { assetResolver }: {
11
12
  assetResolver?: AssetResolver;
@@ -5,13 +5,14 @@ import { detectLocale } from "./routing/i18n";
5
5
  import { fixDataPage, getNextConfigHeaders, handleFallbackFalse, handleRedirects, handleRewrites, } from "./routing/matcher";
6
6
  import { handleMiddleware } from "./routing/middleware";
7
7
  import { dynamicRouteMatcher, staticRouteMatcher, } from "./routing/routeMatcher";
8
- import { constructNextUrl } from "./routing/util";
8
+ import { constructNextUrl, normalizeLocationHeader } from "./routing/util";
9
9
  export const MIDDLEWARE_HEADER_PREFIX = "x-middleware-response-";
10
10
  export const MIDDLEWARE_HEADER_PREFIX_LEN = MIDDLEWARE_HEADER_PREFIX.length;
11
11
  export const INTERNAL_HEADER_PREFIX = "x-opennext-";
12
12
  export const INTERNAL_HEADER_INITIAL_URL = `${INTERNAL_HEADER_PREFIX}initial-url`;
13
13
  export const INTERNAL_HEADER_LOCALE = `${INTERNAL_HEADER_PREFIX}locale`;
14
14
  export const INTERNAL_HEADER_RESOLVED_ROUTES = `${INTERNAL_HEADER_PREFIX}resolved-routes`;
15
+ export const INTERNAL_HEADER_REWRITE_STATUS_CODE = `${INTERNAL_HEADER_PREFIX}rewrite-status-code`;
15
16
  export const INTERNAL_EVENT_REQUEST_ID = `${INTERNAL_HEADER_PREFIX}request-id`;
16
17
  // Geolocation headers starting from Nextjs 15
17
18
  // See https://github.com/vercel/vercel/blob/7714b1c/packages/functions/src/headers.ts
@@ -66,8 +67,7 @@ export default async function routingHandler(event, { assetResolver }) {
66
67
  const redirect = handleRedirects(eventOrResult, RoutesManifest.redirects);
67
68
  if (redirect) {
68
69
  // We need to encode the value in the Location header to make sure it is valid according to RFC
69
- // https://stackoverflow.com/a/7654605/16587222
70
- redirect.headers.Location = new URL(redirect.headers.Location).href;
70
+ redirect.headers.Location = normalizeLocationHeader(redirect.headers.Location, event.url, true);
71
71
  debug("redirect", redirect);
72
72
  return redirect;
73
73
  }
@@ -171,6 +171,7 @@ export default async function routingHandler(event, { assetResolver }) {
171
171
  locale: NextConfig.i18n
172
172
  ? detectLocale(eventOrResult, NextConfig.i18n)
173
173
  : undefined,
174
+ rewriteStatusCode: middlewareEventOrResult.rewriteStatusCode,
174
175
  };
175
176
  }
176
177
  catch (e) {
@@ -34,7 +34,7 @@ export declare class OpenNextNodeResponse extends Transform implements ServerRes
34
34
  socket: Socket | null;
35
35
  setTimeout(_msecs: number, _callback?: (() => void) | undefined): this;
36
36
  addTrailers(_headers: OutgoingHttpHeaders | readonly [string, string][]): void;
37
- constructor(fixHeaders: (headers: OutgoingHttpHeaders) => void, onEnd: (headers: OutgoingHttpHeaders) => Promise<void>, streamCreator?: StreamCreator | undefined, initialHeaders?: OutgoingHttpHeaders | undefined);
37
+ constructor(fixHeaders: (headers: OutgoingHttpHeaders) => void, onEnd: (headers: OutgoingHttpHeaders) => Promise<void>, streamCreator?: StreamCreator | undefined, initialHeaders?: OutgoingHttpHeaders | undefined, statusCode?: number);
38
38
  get originalResponse(): this;
39
39
  get finished(): boolean;
40
40
  setHeader(name: string, value: string | string[]): this;
@@ -51,12 +51,15 @@ export class OpenNextNodeResponse extends Transform {
51
51
  addTrailers(_headers) {
52
52
  throw new Error(CANNOT_BE_USED);
53
53
  }
54
- constructor(fixHeaders, onEnd, streamCreator, initialHeaders) {
54
+ constructor(fixHeaders, onEnd, streamCreator, initialHeaders, statusCode) {
55
55
  super();
56
56
  this.fixHeaders = fixHeaders;
57
57
  this.onEnd = onEnd;
58
58
  this.streamCreator = streamCreator;
59
59
  this.initialHeaders = initialHeaders;
60
+ if (statusCode !== undefined) {
61
+ this.statusCode = statusCode;
62
+ }
60
63
  }
61
64
  // Necessary for next 12
62
65
  // We might have to implement all the methods here
@@ -20,6 +20,12 @@ const wrapper = async (handler, converter) => {
20
20
  await imageHandler(internalEvent, { streamCreator });
21
21
  });
22
22
  app.all("*paths", async (req, res) => {
23
+ if (req.protocol === "http" && req.hostname === "localhost") {
24
+ // This is used internally by Next.js during redirects in server actions. We need to set it to the origin of the request.
25
+ process.env.__NEXT_PRIVATE_ORIGIN = `${req.protocol}://${req.hostname}`;
26
+ // This is to make `next-auth` and other libraries that rely on this header to work locally out of the box.
27
+ req.headers["x-forwarded-proto"] = req.protocol;
28
+ }
23
29
  const internalEvent = await converter.convertFrom(req);
24
30
  const streamCreator = {
25
31
  writeHeaders: (prelude) => {
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import type { OnLoadOptions, Plugin, PluginBuild } from "esbuild";
7
7
  import type { BuildOptions } from "../build/helper";
8
- import { type VersionedField } from "../build/patch/codePatcher.js";
8
+ import { type Versions } from "../build/patch/codePatcher.js";
9
9
  export type * from "esbuild";
10
10
  /**
11
11
  * The callbacks returns either an updated content or undefined if the content is unchanged.
@@ -23,6 +23,7 @@ export type OnUpdateOptions = OnLoadOptions & {
23
23
  };
24
24
  export type Updater = OnUpdateOptions & {
25
25
  callback: Callback;
26
+ versions?: Versions;
26
27
  };
27
28
  export declare class ContentUpdater {
28
29
  private buildOptions;
@@ -34,10 +35,10 @@ export declare class ContentUpdater {
34
35
  * The callbacks are called in order of registration.
35
36
  *
36
37
  * @param name The name of the plugin (must be unique).
37
- * @param updater A versioned field with the callback and `OnUpdateOptions`.
38
+ * @param updaters A list of code updaters
38
39
  * @returns A noop ESBuild plugin.
39
40
  */
40
- updateContent(name: string, versionedUpdaters: VersionedField<Updater>[]): Plugin;
41
+ updateContent(name: string, updaters: Updater[]): Plugin;
41
42
  /**
42
43
  * Returns an ESBuild plugin applying the registered updates.
43
44
  */
@@ -4,7 +4,7 @@
4
4
  * The updater allows multiple plugins to update the content.
5
5
  */
6
6
  import { readFile } from "node:fs/promises";
7
- import { extractVersionedField, } from "../build/patch/codePatcher.js";
7
+ import { isVersionInRange } from "../build/patch/codePatcher.js";
8
8
  export class ContentUpdater {
9
9
  buildOptions;
10
10
  updaters = new Map();
@@ -17,15 +17,14 @@ export class ContentUpdater {
17
17
  * The callbacks are called in order of registration.
18
18
  *
19
19
  * @param name The name of the plugin (must be unique).
20
- * @param updater A versioned field with the callback and `OnUpdateOptions`.
20
+ * @param updaters A list of code updaters
21
21
  * @returns A noop ESBuild plugin.
22
22
  */
23
- updateContent(name, versionedUpdaters) {
23
+ updateContent(name, updaters) {
24
24
  if (this.updaters.has(name)) {
25
25
  throw new Error(`Plugin "${name}" already registered`);
26
26
  }
27
- const updaters = extractVersionedField(versionedUpdaters, this.buildOptions.nextVersion);
28
- this.updaters.set(name, updaters);
27
+ this.updaters.set(name, updaters.filter(({ versions }) => isVersionInRange(this.buildOptions.nextVersion, versions)));
29
28
  return {
30
29
  name,
31
30
  setup() { },
@@ -20,6 +20,7 @@ export type InternalResult = {
20
20
  headers: Record<string, string | string[]>;
21
21
  body: ReadableStream;
22
22
  isBase64Encoded: boolean;
23
+ rewriteStatusCode?: number;
23
24
  } & BaseEventOrResult<"core">;
24
25
  export interface StreamCreator {
25
26
  writeHeaders(prelude: {
@@ -92,6 +93,7 @@ export interface RoutingResult {
92
93
  initialURL: string;
93
94
  locale?: string;
94
95
  resolvedRoutes: ResolvedRoute[];
96
+ rewriteStatusCode?: number;
95
97
  }
96
98
  export interface MiddlewareResult extends RoutingResult, BaseEventOrResult<"middleware"> {
97
99
  }
@@ -63,6 +63,11 @@ export type CachedFetchValue = {
63
63
  export type WithLastModified<T> = {
64
64
  lastModified?: number;
65
65
  value?: T;
66
+ /**
67
+ * If set to true, we will not check the tag cache for this entry.
68
+ * `revalidateTag` and `revalidatePath` may not work as expected.
69
+ */
70
+ shouldBypassTagCache?: boolean;
66
71
  };
67
72
  export type CacheEntryType = Extension;
68
73
  export type CacheValue<CacheType extends CacheEntryType> = (CacheType extends "fetch" ? CachedFetchValue : CacheType extends "cache" ? CachedFile : StoredComposableCacheEntry) & {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "access": "public"
4
4
  },
5
5
  "name": "@opennextjs/aws",
6
- "version": "3.7.1",
6
+ "version": "3.7.3",
7
7
  "bin": {
8
8
  "open-next": "./dist/index.js"
9
9
  },