@opennextjs/aws 3.7.5 → 3.7.6

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.
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { loadConfig } from "../adapters/config/util.js";
4
+ import { safeParseJsonFile } from "../utils/safe-json-parse.js";
4
5
  import logger from "../logger.js";
5
6
  import { isBinaryContentType } from "../utils/binary.js";
6
7
  import * as buildHelper from "./helper.js";
@@ -106,15 +107,20 @@ export function createCacheAssets(options) {
106
107
  // Generate cache file
107
108
  Object.entries(cacheFilesPath).forEach(([cacheFilePath, files]) => {
108
109
  const cacheFileMeta = files.meta
109
- ? JSON.parse(fs.readFileSync(files.meta, "utf8"))
110
+ ? safeParseJsonFile(fs.readFileSync(files.meta, "utf8"), cacheFilePath)
110
111
  : undefined;
112
+ const cacheJson = files.json
113
+ ? safeParseJsonFile(fs.readFileSync(files.json, "utf8"), cacheFilePath)
114
+ : undefined;
115
+ if ((files.meta && !cacheFileMeta) || (files.json && !cacheJson)) {
116
+ logger.warn(`Skipping invalid cache file: ${cacheFilePath}`);
117
+ return;
118
+ }
111
119
  const cacheFileContent = {
112
120
  type: files.body ? "route" : files.json ? "page" : "app",
113
121
  meta: cacheFileMeta,
114
122
  html: files.html ? fs.readFileSync(files.html, "utf8") : undefined,
115
- json: files.json
116
- ? JSON.parse(fs.readFileSync(files.json, "utf8"))
117
- : undefined,
123
+ json: cacheJson,
118
124
  rsc: files.rsc ? fs.readFileSync(files.rsc, "utf8") : undefined,
119
125
  body: files.body
120
126
  ? fs
@@ -138,7 +144,7 @@ export function createCacheAssets(options) {
138
144
  fs.cpSync(fetchCachePath, fetchOutputPath, { recursive: true });
139
145
  buildHelper.traverseFiles(fetchCachePath, () => true, ({ absolutePath, relativePath }) => {
140
146
  const fileContent = fs.readFileSync(absolutePath, "utf8");
141
- const fileData = JSON.parse(fileContent);
147
+ const fileData = safeParseJsonFile(fileContent, absolutePath);
142
148
  fileData?.tags?.forEach((tag) => {
143
149
  metaFiles.push({
144
150
  tag: { S: path.posix.join(buildId, tag) },
@@ -156,8 +162,8 @@ export function createCacheAssets(options) {
156
162
  sourceDirs.forEach((sourceDir) => {
157
163
  buildHelper.traverseFiles(sourceDir, ({ absolutePath, relativePath }) => absolutePath.endsWith(".meta") && !isFileSkipped(relativePath), ({ absolutePath, relativePath }) => {
158
164
  const fileContent = fs.readFileSync(absolutePath, "utf8");
159
- const fileData = JSON.parse(fileContent);
160
- if (fileData.headers?.["x-next-cache-tags"]) {
165
+ const fileData = safeParseJsonFile(fileContent, absolutePath);
166
+ if (fileData?.headers?.["x-next-cache-tags"]) {
161
167
  fileData.headers["x-next-cache-tags"]
162
168
  .split(",")
163
169
  .forEach((tag) => {
@@ -76,7 +76,8 @@ export async function createImageOptimizationBundle(options) {
76
76
  const sharpVersion = process.env.SHARP_VERSION ?? "0.32.6";
77
77
  installDependencies(outputPath, config.imageOptimization?.install ?? {
78
78
  packages: [`sharp@${sharpVersion}`],
79
- arch: "arm64",
79
+ // By not specifying an arch, `npm install` will choose one for us (i.e. our system one)
80
+ arch: config.imageOptimization?.loader === "fs-dev" ? undefined : "arm64",
80
81
  nodeVersion: "18",
81
82
  libc: "glibc",
82
83
  });
@@ -175,7 +175,8 @@ export async function cacheInterceptor(event) {
175
175
  return event;
176
176
  }
177
177
  // We need to check the tag cache now
178
- if (cachedData.value?.type === "app") {
178
+ if (cachedData.value?.type === "app" ||
179
+ cachedData.value?.type === "route") {
179
180
  const tags = getTagsFromValue(cachedData.value);
180
181
  const _hasBeenRevalidated = await hasBeenRevalidated(localizedPath, tags, cachedData);
181
182
  if (_hasBeenRevalidated) {
@@ -0,0 +1,8 @@
1
+ declare const _default: {
2
+ name: string;
3
+ mode: "nextMode";
4
+ getLastRevalidated: (tags: string[]) => Promise<number>;
5
+ hasBeenRevalidated: (tags: string[], lastModified?: number) => Promise<boolean>;
6
+ writeTags: (tags: string[]) => Promise<void>;
7
+ };
8
+ export default _default;
@@ -0,0 +1,42 @@
1
+ import { debug } from "../../adapters/logger";
2
+ const tagsMap = new Map();
3
+ export default {
4
+ name: "fs-dev-nextMode",
5
+ mode: "nextMode",
6
+ getLastRevalidated: async (tags) => {
7
+ if (globalThis.openNextConfig.dangerous?.disableTagCache) {
8
+ return 0;
9
+ }
10
+ let lastRevalidated = 0;
11
+ tags.forEach((tag) => {
12
+ const tagTime = tagsMap.get(tag);
13
+ if (tagTime && tagTime > lastRevalidated) {
14
+ lastRevalidated = tagTime;
15
+ }
16
+ });
17
+ debug("getLastRevalidated result:", lastRevalidated);
18
+ return lastRevalidated;
19
+ },
20
+ hasBeenRevalidated: async (tags, lastModified) => {
21
+ if (globalThis.openNextConfig.dangerous?.disableTagCache) {
22
+ return false;
23
+ }
24
+ const hasRevalidatedTag = tags.some((tag) => {
25
+ const tagRevalidatedAt = tagsMap.get(tag);
26
+ return tagRevalidatedAt ? tagRevalidatedAt > (lastModified ?? 0) : false;
27
+ });
28
+ debug("hasBeenRevalidated result:", hasRevalidatedTag);
29
+ return hasRevalidatedTag;
30
+ },
31
+ writeTags: async (tags) => {
32
+ if (globalThis.openNextConfig.dangerous?.disableTagCache ||
33
+ tags.length === 0) {
34
+ return;
35
+ }
36
+ debug("writeTags", { tags: tags });
37
+ tags.forEach((tag) => {
38
+ tagsMap.set(tag, Date.now());
39
+ });
40
+ debug("writeTags completed, written", tags.length, "tags");
41
+ },
42
+ };
@@ -4,31 +4,35 @@ import { getMonorepoRelativePath } from "../../utils/normalize-path";
4
4
  const tagFile = path.join(getMonorepoRelativePath(), "dynamodb-provider/dynamodb-cache.json");
5
5
  const tagContent = fs.readFileSync(tagFile, "utf-8");
6
6
  let tags = JSON.parse(tagContent);
7
+ const { NEXT_BUILD_ID } = process.env;
8
+ function buildKey(key) {
9
+ return path.posix.join(NEXT_BUILD_ID ?? "", key);
10
+ }
7
11
  const tagCache = {
8
12
  name: "fs-dev",
9
13
  mode: "original",
10
14
  getByPath: async (path) => {
11
15
  return tags
12
- .filter((tagPathMapping) => tagPathMapping.path.S === path)
13
- .map((tag) => tag.tag.S);
16
+ .filter((tagPathMapping) => tagPathMapping.path.S === buildKey(path))
17
+ .map((tag) => tag.tag.S.replace(`${NEXT_BUILD_ID}/`, ""));
14
18
  },
15
19
  getByTag: async (tag) => {
16
20
  return tags
17
- .filter((tagPathMapping) => tagPathMapping.tag.S === tag)
18
- .map((tag) => tag.path.S);
21
+ .filter((tagPathMapping) => tagPathMapping.tag.S === buildKey(tag))
22
+ .map((tagEntry) => tagEntry.path.S.replace(`${NEXT_BUILD_ID}/`, ""));
19
23
  },
20
24
  getLastModified: async (path, lastModified) => {
21
- const revalidatedTags = tags.filter((tagPathMapping) => tagPathMapping.path.S === path &&
25
+ const revalidatedTags = tags.filter((tagPathMapping) => tagPathMapping.path.S === buildKey(path) &&
22
26
  Number.parseInt(tagPathMapping.revalidatedAt.N) > (lastModified ?? 0));
23
27
  return revalidatedTags.length > 0 ? -1 : (lastModified ?? Date.now());
24
28
  },
25
29
  writeTags: async (newTags) => {
26
- const newTagsSet = new Set(newTags.map(({ tag, path }) => `${tag}-${path}`));
30
+ const newTagsSet = new Set(newTags.map(({ tag, path }) => `${buildKey(tag)}-${buildKey(path)}`));
27
31
  const unchangedTags = tags.filter(({ tag, path }) => !newTagsSet.has(`${tag.S}-${path.S}`));
28
- tags = unchangedTags.concat(newTags.map((tag) => ({
29
- tag: { S: tag.tag },
30
- path: { S: tag.path },
31
- revalidatedAt: { N: String(tag.revalidatedAt ?? 1) },
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()}` },
32
36
  })));
33
37
  },
34
38
  };
@@ -4,6 +4,9 @@ import { NextConfig } from "../../adapters/config/index";
4
4
  import { getMonorepoRelativePath } from "../../utils/normalize-path";
5
5
  const wrapper = async (handler, converter) => {
6
6
  const app = express();
7
+ // We disable this cause we wanna use it ourself
8
+ // https://stackoverflow.com/a/13055495/16587222
9
+ app.disable("x-powered-by");
7
10
  // To serve static assets
8
11
  const basePath = NextConfig.basePath ?? "";
9
12
  app.use(basePath, express.static(path.join(getMonorepoRelativePath(), "assets")));
@@ -100,7 +100,7 @@ export interface MiddlewareResult extends RoutingResult, BaseEventOrResult<"midd
100
100
  }
101
101
  export type IncludedQueue = "sqs" | "sqs-lite" | "direct" | "dummy";
102
102
  export type IncludedIncrementalCache = "s3" | "s3-lite" | "multi-tier-ddb-s3" | "fs-dev" | "dummy";
103
- export type IncludedTagCache = "dynamodb" | "dynamodb-lite" | "dynamodb-nextMode" | "fs-dev" | "dummy";
103
+ export type IncludedTagCache = "dynamodb" | "dynamodb-lite" | "dynamodb-nextMode" | "fs-dev" | "fs-dev-nextMode" | "dummy";
104
104
  export type IncludedImageLoader = "s3" | "s3-lite" | "host" | "fs-dev" | "dummy";
105
105
  export type IncludedOriginResolver = "pattern-env" | "dummy";
106
106
  export type IncludedWarmer = "aws-lambda" | "dummy";
@@ -0,0 +1 @@
1
+ export declare function safeParseJsonFile<T = any>(input: string, filePath: string, fallback?: T): T | undefined;
@@ -0,0 +1,10 @@
1
+ import logger from "../logger.js";
2
+ export function safeParseJsonFile(input, filePath, fallback) {
3
+ try {
4
+ return JSON.parse(input);
5
+ }
6
+ catch (err) {
7
+ logger.warn(`Failed to parse JSON file "${filePath}". Error: ${err.message}`);
8
+ return fallback;
9
+ }
10
+ }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "access": "public"
4
4
  },
5
5
  "name": "@opennextjs/aws",
6
- "version": "3.7.5",
6
+ "version": "3.7.6",
7
7
  "bin": {
8
8
  "open-next": "./dist/index.js"
9
9
  },