@opennextjs/cloudflare 1.0.3 → 1.0.4

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.
@@ -46,6 +46,13 @@ interface OpenNextConfig extends AwsOpenNextConfig {
46
46
  * @default true
47
47
  */
48
48
  useWorkerdCondition?: boolean;
49
+ /**
50
+ * Disable throwing an error when the config validation fails.
51
+ * This is useful for overriding some of the default provided by cloudflare.
52
+ * **USE AT YOUR OWN RISK**
53
+ * @default false
54
+ */
55
+ dangerousDisableConfigValidation?: boolean;
49
56
  };
50
57
  }
51
58
  /**
@@ -1,4 +1,4 @@
1
- import { debug, error } from "@opennextjs/aws/adapters/logger.js";
1
+ import { debug, error, warn } from "@opennextjs/aws/adapters/logger.js";
2
2
  import { FatalError, IgnorableError, isOpenNextError, RecoverableError, } from "@opennextjs/aws/utils/error.js";
3
3
  import { DurableObject } from "cloudflare:workers";
4
4
  const DEFAULT_MAX_REVALIDATION = 5;
@@ -47,6 +47,9 @@ export class DOQueueHandler extends DurableObject {
47
47
  debug(`Durable object initialized`);
48
48
  }
49
49
  async revalidate(msg) {
50
+ if (this.ongoingRevalidations.size > 2 * this.maxRevalidations) {
51
+ warn(`Your durable object has 2 times the maximum number of revalidations (${this.maxRevalidations}) in progress. If this happens often, you should consider increasing the NEXT_CACHE_DO_QUEUE_MAX_REVALIDATION or the number of durable objects with the MAX_REVALIDATE_CONCURRENCY env var.`);
52
+ }
50
53
  // If there is already an ongoing revalidation, we don't need to revalidate again
51
54
  if (this.ongoingRevalidations.has(msg.MessageDeduplicationId))
52
55
  return;
@@ -59,19 +62,18 @@ export class DOQueueHandler extends DurableObject {
59
62
  return;
60
63
  if (this.ongoingRevalidations.size >= this.maxRevalidations) {
61
64
  debug(`The maximum number of revalidations (${this.maxRevalidations}) is reached. Blocking until one of the revalidations finishes.`);
62
- const ongoingRevalidations = this.ongoingRevalidations.values();
63
- // When there is more than the max revalidations, we block concurrency until one of the revalidations finishes
64
- // We still await the promise to ensure the revalidation is completed
65
- // This is fine because the queue itself run inside a waitUntil
66
- await this.ctx.blockConcurrencyWhile(async () => {
65
+ // TODO: need more investigation
66
+ // We don't use `blockConcurrencyWhile` here because it block the whole durable object for 30 seconds
67
+ // if we exceed the max revalidations too fast
68
+ while (this.ongoingRevalidations.size >= this.maxRevalidations) {
69
+ const ongoingRevalidations = this.ongoingRevalidations.values();
67
70
  debug(`Waiting for one of the revalidations to finish`);
68
71
  await Promise.race(ongoingRevalidations);
69
- });
72
+ }
70
73
  }
71
74
  const revalidationPromise = this.executeRevalidation(msg);
72
75
  // We store the promise to dedupe the revalidation
73
76
  this.ongoingRevalidations.set(msg.MessageDeduplicationId, revalidationPromise);
74
- // TODO: check if the object stays up during waitUntil so that the internal state is maintained
75
77
  this.ctx.waitUntil(revalidationPromise);
76
78
  }
77
79
  async executeRevalidation(msg) {
@@ -86,6 +88,7 @@ export class DOQueueHandler extends DurableObject {
86
88
  "x-prerender-revalidate": process.env.__NEXT_PREVIEW_MODE_ID,
87
89
  "x-isr": "1",
88
90
  },
91
+ // This one is kind of problematic, it will always show the wall time of the revalidation to `this.revalidationTimeout`
89
92
  signal: AbortSignal.timeout(this.revalidationTimeout),
90
93
  });
91
94
  // Now we need to handle errors from the fetch
@@ -202,6 +205,7 @@ export class DOQueueHandler extends DurableObject {
202
205
  // We create the sync table to handle eventually consistent incremental cache
203
206
  this.sql.exec("CREATE TABLE IF NOT EXISTS sync (id TEXT PRIMARY KEY, lastSuccess INTEGER, buildId TEXT)");
204
207
  // Before doing anything else, we clear the DB for any potential old data
208
+ // TODO: extract this to a function so that it could be called by the user at another time than init
205
209
  this.sql.exec("DELETE FROM failed_state WHERE buildId != ?", process.env.__NEXT_BUILD_ID);
206
210
  this.sql.exec("DELETE FROM sync WHERE buildId != ?", process.env.__NEXT_BUILD_ID);
207
211
  const failedStateCursor = this.sql.exec("SELECT * FROM failed_state");
@@ -82,9 +82,6 @@ describe("DurableObjectQueue", () => {
82
82
  expect(queue.ongoingRevalidations.size).toBe(queue.maxRevalidations);
83
83
  expect(queue.ongoingRevalidations.has("id6")).toBe(false);
84
84
  expect(Array.from(queue.ongoingRevalidations.keys())).toEqual(["id", "id2", "id3", "id4", "id5"]);
85
- // BlockConcurrencyWhile is called twice here, first time during creation of the object and second time when we try to revalidate
86
- // @ts-expect-error
87
- expect(queue.ctx.blockConcurrencyWhile).toHaveBeenCalledTimes(2);
88
85
  // Here we await the blocked request to ensure it's resolved
89
86
  await blockedReq;
90
87
  // We then need to await for the actual revalidation to finish
@@ -28,7 +28,7 @@ export function ensureCloudflareConfig(config) {
28
28
  logger.warn("The direct mode queue is not recommended for use in production.");
29
29
  }
30
30
  if (Object.values(requirements).some((satisfied) => !satisfied)) {
31
- throw new Error("The `open-next.config.ts` should have a default export like this:\n\n" +
31
+ const errorMessage = "The `open-next.config.ts` should have a default export like this:\n\n" +
32
32
  `{
33
33
  default: {
34
34
  override: {
@@ -52,6 +52,11 @@ export function ensureCloudflareConfig(config) {
52
52
  queue: "dummy" | "direct" | function,
53
53
  },
54
54
  },
55
- }\n\n`.replace(/^ {8}/gm, ""));
55
+ }\n\n`.replace(/^ {8}/gm, "");
56
+ if (config.cloudflare?.dangerousDisableConfigValidation) {
57
+ logger.warn(errorMessage);
58
+ return;
59
+ }
60
+ throw new Error(errorMessage);
56
61
  }
57
62
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@opennextjs/cloudflare",
3
3
  "description": "Cloudflare builder for next apps",
4
- "version": "1.0.3",
4
+ "version": "1.0.4",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "opennextjs-cloudflare": "dist/cli/index.js"
@@ -43,7 +43,7 @@
43
43
  "homepage": "https://github.com/opennextjs/opennextjs-cloudflare",
44
44
  "dependencies": {
45
45
  "@dotenvx/dotenvx": "1.31.0",
46
- "@opennextjs/aws": "^3.6.1",
46
+ "@opennextjs/aws": "^3.6.2",
47
47
  "enquirer": "^2.4.1",
48
48
  "glob": "^11.0.0",
49
49
  "ts-tqdm": "^0.8.6"
@@ -54,7 +54,7 @@
54
54
  "@tsconfig/strictest": "^2.0.5",
55
55
  "@types/mock-fs": "^4.13.4",
56
56
  "@types/node": "^22.2.0",
57
- "esbuild": "^0.24.2",
57
+ "esbuild": "^0.25.4",
58
58
  "eslint": "^9.11.1",
59
59
  "eslint-plugin-import": "^2.31.0",
60
60
  "eslint-plugin-simple-import-sort": "^12.1.1",