@opennextjs/cloudflare 1.19.6 → 1.19.7

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.
@@ -4,6 +4,10 @@ import type { Unstable_Config as WranglerConfig } from "wrangler";
4
4
  import type yargs from "yargs";
5
5
  import { type WorkerEnvVar } from "./utils/helpers.js";
6
6
  import type { WranglerTarget } from "./utils/run-wrangler.js";
7
+ export declare const MAX_REQUEST_RETRIES = 15;
8
+ export declare const BASE_RETRY_DELAY_MS = 250;
9
+ export declare const MAX_RETRY_DELAY_MS = 10000;
10
+ export declare const BACKOFF_FACTOR: number;
7
11
  export declare function populateCache(buildOpts: BuildOptions, config: OpenNextConfig, wranglerConfig: WranglerConfig, populateCacheOptions: PopulateCacheOptions, envVars: WorkerEnvVar): Promise<void>;
8
12
  export type CacheAsset = {
9
13
  isFetch: boolean;
@@ -19,6 +19,14 @@ import { normalizePath } from "../utils/normalize-path.js";
19
19
  import { getEnvFromPlatformProxy, quoteShellMeta } from "./utils/helpers.js";
20
20
  import { runWrangler } from "./utils/run-wrangler.js";
21
21
  import { getNormalizedOptions, printHeaders, readWranglerConfig, retrieveCompiledConfig, withWranglerOptions, withWranglerPassthroughArgs, } from "./utils/utils.js";
22
+ // Maximum number of attempts to send the request
23
+ export const MAX_REQUEST_RETRIES = 15;
24
+ // Base delay for retries
25
+ export const BASE_RETRY_DELAY_MS = 250;
26
+ // Maximum delay for retries, used to calculate the backoff factor
27
+ export const MAX_RETRY_DELAY_MS = 10_000;
28
+ // Backoff factor for retries, calculated to ensure that the delay grows exponentially up to the maximum delay
29
+ export const BACKOFF_FACTOR = (MAX_RETRY_DELAY_MS / BASE_RETRY_DELAY_MS) ** (1 / (MAX_REQUEST_RETRIES - 1));
22
30
  /**
23
31
  * Implementation of the `opennextjs-cloudflare populateCache` command.
24
32
  *
@@ -199,49 +207,32 @@ async function populateR2IncrementalCache(buildOpts, config, populateCacheOption
199
207
  /**
200
208
  * Sends cache entries to the R2 worker, one entry per request.
201
209
  *
202
- * Up to `concurrency` requests are in-flight at any given time.
203
- * Retry logic for transient R2 write failures is handled by the worker.
204
- *
205
210
  * @param options
206
211
  * @param options.workerUrl - The URL of the local R2 worker's `/populate` endpoint.
207
212
  * @param options.assets - The cache assets to write, as collected by {@link getCacheAssets}.
208
213
  * @param options.prefix - Optional prefix prepended to each R2 key.
209
- * @param options.concurrency - Maximum number of concurrent in-flight requests.
214
+ * @param options.maxConcurrency - Maximum number of concurrent in-flight requests.
210
215
  * @returns Resolves when all entries have been written successfully.
211
216
  * @throws {Error} If any entry fails after all retries or encounters a non-retryable error.
212
217
  */
213
218
  async function sendEntriesToR2Worker(options) {
214
219
  const { workerUrl, assets, prefix, maxConcurrency } = options;
215
- // Build the list of entries to send (key + filename).
216
- // File contents are read lazily in sendEntryToR2Worker to avoid
217
- // loading all cache values into memory at once.
218
- const entries = assets.map(({ fullPath, key, buildId, isFetch }) => ({
219
- key: computeCacheKey(key, {
220
- prefix,
221
- buildId,
222
- cacheType: isFetch ? "fetch" : "cache",
223
- }),
224
- filename: fullPath,
225
- }));
226
- // Use a concurrency-limited loop with a progress bar.
227
- // `pending` tracks in-flight promises so we can cap concurrency.
228
220
  const pending = new Set();
229
- let concurrency = 1;
230
- for (const entry of tqdm(entries)) {
221
+ for (const asset of tqdm(assets)) {
222
+ const { fullPath, key, buildId, isFetch } = asset;
231
223
  const task = sendEntryToR2Worker({
232
224
  workerUrl,
233
- key: entry.key,
234
- filename: entry.filename,
225
+ key: computeCacheKey(key, {
226
+ prefix,
227
+ buildId,
228
+ cacheType: isFetch ? "fetch" : "cache",
229
+ }),
230
+ filename: fullPath,
235
231
  }).finally(() => pending.delete(task));
236
232
  pending.add(task);
237
233
  // If we've reached the concurrency limit, wait for one to finish.
238
- if (pending.size >= concurrency) {
234
+ if (pending.size >= maxConcurrency) {
239
235
  await Promise.race(pending);
240
- // Increase concurrency gradually to avoid overwhelming the worker
241
- // with too many requests at once.
242
- if (concurrency < maxConcurrency) {
243
- concurrency++;
244
- }
245
236
  }
246
237
  }
247
238
  await Promise.all(pending);
@@ -261,9 +252,7 @@ class RetryableWorkerError extends Error {
261
252
  */
262
253
  async function sendEntryToR2Worker(options) {
263
254
  const { workerUrl, key, filename } = options;
264
- const CLIENT_RETRY_ATTEMPTS = 5;
265
- const CLIENT_RETRY_BASE_DELAY_MS = 250;
266
- for (let attempt = 0; attempt < CLIENT_RETRY_ATTEMPTS; attempt++) {
255
+ for (let attempt = 0; attempt < MAX_REQUEST_RETRIES; attempt++) {
267
256
  try {
268
257
  let response;
269
258
  try {
@@ -280,9 +269,7 @@ async function sendEntryToR2Worker(options) {
280
269
  });
281
270
  }
282
271
  catch (e) {
283
- throw new RetryableWorkerError(`Failed to send request to R2 worker: ${e instanceof Error ? e.message : String(e)}`, {
284
- cause: e,
285
- });
272
+ throw new RetryableWorkerError(`Failed to send request to R2 worker: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
286
273
  }
287
274
  const body = await response.text();
288
275
  let result;
@@ -290,6 +277,7 @@ async function sendEntryToR2Worker(options) {
290
277
  result = JSON.parse(body);
291
278
  }
292
279
  catch (e) {
280
+ // https://developers.cloudflare.com/support/troubleshooting/http-status-codes/cloudflare-1xxx-errors/error-1102
293
281
  if (body.includes("Worker exceeded resource limits")) {
294
282
  throw new RetryableWorkerError("Worker exceeded resource limits", { cause: e });
295
283
  }
@@ -300,21 +288,20 @@ async function sendEntryToR2Worker(options) {
300
288
  cause: e,
301
289
  });
302
290
  }
303
- if (!result.success && response.status >= 500) {
304
- throw new RetryableWorkerError(result.error);
305
- }
306
291
  if (!result.success) {
307
- throw new Error(`Failed to write "${key}" to R2: ${result.error}`);
292
+ throw response.status >= 500
293
+ ? new RetryableWorkerError(result.error)
294
+ : new Error(`Failed to write "${key}" to R2: ${result.error}`);
308
295
  }
309
296
  return;
310
297
  }
311
298
  catch (e) {
312
- if (e instanceof RetryableWorkerError && attempt < CLIENT_RETRY_ATTEMPTS - 1) {
299
+ if (e instanceof RetryableWorkerError && attempt < MAX_REQUEST_RETRIES - 1) {
313
300
  logger.error(`Attempt ${attempt + 1} to write "${key}" failed with a retryable error: ${e.message}. Retrying...`);
314
- await setTimeout(CLIENT_RETRY_BASE_DELAY_MS * Math.pow(2, attempt));
301
+ await setTimeout(BASE_RETRY_DELAY_MS * Math.pow(BACKOFF_FACTOR, attempt));
315
302
  continue;
316
303
  }
317
- throw new Error(`Failed to write "${key}" to R2 after ${CLIENT_RETRY_ATTEMPTS} attempts`, {
304
+ throw new Error(`Failed to write "${key}" to R2 after ${MAX_REQUEST_RETRIES} attempts`, {
318
305
  cause: e,
319
306
  });
320
307
  }
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.19.6",
4
+ "version": "1.19.7",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "opennextjs-cloudflare": "dist/cli/index.js"
@@ -54,7 +54,7 @@
54
54
  "yargs": "^18.0.0"
55
55
  },
56
56
  "devDependencies": {
57
- "@cloudflare/workers-types": "^4.20260423.1",
57
+ "@cloudflare/workers-types": "^4.20260426.1",
58
58
  "@eslint/js": "^9.11.1",
59
59
  "@tsconfig/strictest": "^2.0.5",
60
60
  "@types/mock-fs": "^4.13.4",
@@ -69,7 +69,7 @@
69
69
  "eslint-plugin-unicorn": "^55.0.0",
70
70
  "globals": "^15.9.0",
71
71
  "mock-fs": "^5.4.1",
72
- "next": "~15.5.15",
72
+ "next": "^15.5.16",
73
73
  "picomatch": "^4.0.2",
74
74
  "rimraf": "^6.0.1",
75
75
  "typescript": "^5.9.3",
@@ -77,7 +77,7 @@
77
77
  "vitest": "^4.1.4"
78
78
  },
79
79
  "peerDependencies": {
80
- "next": ">=15.5.15 <16 || >=16.2.3",
80
+ "next": ">=15.5.16 <16 || >=16.2.5",
81
81
  "wrangler": "^4.86.0"
82
82
  },
83
83
  "scripts": {