Skip to content

Commit 049c2d4

Browse files
committed
Merge branch 'main' into edmundhung/prefer-opennext-build-id
2 parents bebeac3 + d577521 commit 049c2d4

17 files changed

Lines changed: 700 additions & 165 deletions

File tree

.changeset/five-gifts-hope.md

Lines changed: 0 additions & 13 deletions
This file was deleted.

.changeset/fix-peerdeps-next-range.md

Lines changed: 0 additions & 9 deletions
This file was deleted.

.changeset/red-yaks-go.md

Lines changed: 0 additions & 9 deletions
This file was deleted.

packages/cloudflare/CHANGELOG.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,66 @@
11
# @opennextjs/cloudflare
22

3+
## 1.19.3
4+
5+
### Patch Changes
6+
7+
- [#1215](https://github.com/opennextjs/opennextjs-cloudflare/pull/1215) [`608893e`](https://github.com/opennextjs/opennextjs-cloudflare/commit/608893e63e1ee16d07c7ec42da979657cf2a62bd) Thanks [@vicb](https://github.com/vicb)! - Factor large repeated values in manifests
8+
9+
This reduce the size of the generated code.
10+
11+
- [#1218](https://github.com/opennextjs/opennextjs-cloudflare/pull/1218) [`f0d0226`](https://github.com/opennextjs/opennextjs-cloudflare/commit/f0d022685b24881a142bb01005ff78089be8c8d3) Thanks [@314systems](https://github.com/314systems)! - remove `process.version` override
12+
13+
Remove process.version / process.versions.node overrides now that [unjs/unenv#493](https://github.com/unjs/unenv/pull/493) is merged and shipped in [[email protected]](https://github.com/unjs/unenv/releases/tag/v2.0.0-rc.16) (project uses 2.0.0-rc.24)
14+
15+
- [#1199](https://github.com/opennextjs/opennextjs-cloudflare/pull/1199) [`32594d6`](https://github.com/opennextjs/opennextjs-cloudflare/commit/32594d6a921c5ebdbe25f38635bb2c9dabdcbff1) Thanks [@SdSadat](https://github.com/SdSadat)! - fix(cli): fail fast in non-TTY environments instead of hanging on config-creation prompts
16+
17+
When `open-next.config.ts` (or `wrangler.(toml|json|jsonc)`) is missing, the CLI
18+
prompts the user to auto-create it. In non-TTY environments (Cloudflare Workers
19+
Builds, Docker, CI) the Enquirer prompt can't read stdin, so the build hangs or
20+
fails with a truncated prompt and a cryptic exit code — the user sees
21+
`? Missing required open-next.config.ts file, do you want to create one? (Y/n)`
22+
and then ` ELIFECYCLE Command failed with exit code 13`, with no hint at what
23+
to do next.
24+
25+
Now, in non-interactive environments, both prompts throw an actionable error
26+
with the exact template to paste (for `open-next.config.ts`) or point at the
27+
existing `--skipWranglerConfigCheck` / `SKIP_WRANGLER_CONFIG_CHECK` escape
28+
hatch (for the wrangler config). Interactive behavior is unchanged.
29+
30+
## 1.19.2
31+
32+
### Patch Changes
33+
34+
- [#1207](https://github.com/opennextjs/opennextjs-cloudflare/pull/1207) [`0958726`](https://github.com/opennextjs/opennextjs-cloudflare/commit/0958726939d59e4a5c5a3062190278ffdfde38f5) Thanks [@edmundhung](https://github.com/edmundhung)! - bump `@opennextjs/aws` to 3.10.2
35+
36+
See details at <https://github.com/opennextjs/opennextjs-aws/releases/tag/v3.10.2>
37+
38+
- [#1139](https://github.com/opennextjs/opennextjs-cloudflare/pull/1139) [`79b01b8`](https://github.com/opennextjs/opennextjs-cloudflare/commit/79b01b84fd92191517b7a11516c04208f9d474a6) Thanks [@james-elicx](https://github.com/james-elicx)! - Fix Turbopack external module resolution by dynamically discovering external imports at build time.
39+
40+
When packages are listed in `serverExternalPackages`, Turbopack externalizes them via `externalImport()` which uses dynamic `await import(id)`. The bundler (ESBuild) can't statically analyze `import(id)` with a variable, so these modules aren't included in the worker bundle.
41+
42+
This patch:
43+
44+
- Discovers hashed Turbopack external module mappings from `.next/node_modules/` symlinks (e.g. `shiki-43d062b67f27bbdc``shiki`)
45+
- Scans traced chunk files for bare external imports (e.g. `externalImport("shiki")`) and subpath imports (e.g. `shiki/engine/javascript`)
46+
- Generates explicit `switch/case` entries so the bundler can statically resolve and include these modules
47+
48+
- [#1203](https://github.com/opennextjs/opennextjs-cloudflare/pull/1203) [`6f02d12`](https://github.com/opennextjs/opennextjs-cloudflare/commit/6f02d12a75a78410711cc0d9db13ab0d41ed903a) Thanks [@314systems](https://github.com/314systems)! - fix: exclude unsupported Next.js 16 releases from peer dependencies.
49+
50+
The previous range allowed Next.js 16.0.0 through 16.2.2 without a peer dependency warning because `>=16.2.3` was already covered by `>=15.5.15`.
51+
52+
The range now explicitly supports Next.js 15.5.15 and above in the 15.x line, and Next.js 16.2.3 and above in the 16.x line.
53+
54+
- [#1200](https://github.com/opennextjs/opennextjs-cloudflare/pull/1200) [`7820ad0`](https://github.com/opennextjs/opennextjs-cloudflare/commit/7820ad0a0e5f57aba0580f3cabfdd0caa75cc9bb) Thanks [@NathanDrake2406](https://github.com/NathanDrake2406)! - fix: reuse sharded tag data when filling the regional cache.
55+
56+
The sharded tag cache miss path already reads tag data from the Durable Object before answering the request. Reuse that fetched data when populating the regional cache so a shard miss does not immediately trigger a second identical Durable Object read.
57+
58+
- [#1206](https://github.com/opennextjs/opennextjs-cloudflare/pull/1206) [`585795d`](https://github.com/opennextjs/opennextjs-cloudflare/commit/585795dbe20fe20d8662addbf9b7be64d82e3184) Thanks [@314systems](https://github.com/314systems)! - fix: regression where getEnvFromPlatformProxy received wrong options type
59+
60+
This fixes a regression introduced in [32ba91a](https://github.com/opennextjs/opennextjs-cloudflare/commit/32ba91a6d3fa6b9a8b2cd5a8c973c3b3eb1108f0) where `getEnvFromPlatformProxy` call sites passed `OpenNextConfig` even though the function expects Wrangler `GetPlatformProxyOptions`.
61+
62+
The fix restores the pre-[32ba91a](https://github.com/opennextjs/opennextjs-cloudflare/commit/32ba91a6d3fa6b9a8b2cd5a8c973c3b3eb1108f0) argument shape by passing `{ configPath, environment }` from CLI arguments, so env resolution follows the selected Wrangler config/environment.
63+
364
## 1.19.1
465

566
### Patch Changes

packages/cloudflare/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@opennextjs/cloudflare",
33
"description": "Cloudflare builder for next apps",
4-
"version": "1.19.1",
4+
"version": "1.19.3",
55
"type": "module",
66
"scripts": {
77
"clean": "rimraf dist",
@@ -54,7 +54,8 @@
5454
"dependencies": {
5555
"@ast-grep/napi": "^0.40.5",
5656
"@dotenvx/dotenvx": "catalog:",
57-
"@opennextjs/aws": "3.10.1",
57+
"@opennextjs/aws": "3.10.2",
58+
"ci-info": "^4.2.0",
5859
"cloudflare": "^4.4.1",
5960
"comment-json": "^4.5.1",
6061
"enquirer": "^2.4.1",

packages/cloudflare/src/api/overrides/tag-cache/do-sharded-tag-cache.spec.ts

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,35 @@ describe("DOShardedTagCache", () => {
269269
expect(cache.putToRegionalCache).toHaveBeenCalled();
270270
});
271271

272+
it("should only read tag data once on a regional-cache miss", async () => {
273+
const putMock = vi.fn();
274+
// @ts-expect-error - Defined on cloudflare context
275+
globalThis.caches = {
276+
open: vi.fn().mockResolvedValue({
277+
match: vi.fn().mockResolvedValue(null),
278+
put: putMock,
279+
}),
280+
};
281+
const cache = shardedDOTagCache({ baseShardSize: 4, regionalCache: true });
282+
cache.getFromRegionalCache = vi.fn().mockResolvedValueOnce([]);
283+
getTagDataMock.mockResolvedValueOnce({
284+
tag1: { revalidatedAt: 123455, stale: null, expire: null },
285+
});
286+
287+
const result = await cache.hasBeenRevalidated(["tag1"], 123456);
288+
289+
expect(result).toBe(false);
290+
expect(getTagDataMock).toHaveBeenCalledTimes(1);
291+
expect(getTagDataMock).toHaveBeenCalledWith(["tag1"]);
292+
expect(putMock).toHaveBeenCalledTimes(1);
293+
expect(putMock).toHaveBeenCalledWith(
294+
"http://local.cache/shard/tag-hard;shard-1?tag=tag1",
295+
expect.any(Response)
296+
);
297+
// @ts-expect-error - Defined on cloudflare context
298+
globalThis.caches = undefined;
299+
});
300+
272301
it("should call all the durable object instances", async () => {
273302
const cache = shardedDOTagCache();
274303
cache.getFromRegionalCache = vi.fn().mockResolvedValue([]);
@@ -408,15 +437,15 @@ describe("DOShardedTagCache", () => {
408437
});
409438

410439
it("should try to return the cache instance if regional cache is enabled", async () => {
411-
// @ts-expect-error - Defined on cloudfare context
440+
// @ts-expect-error - Defined on cloudflare context
412441
globalThis.caches = {
413442
open: vi.fn().mockResolvedValue("cache"),
414443
};
415444
const cache = shardedDOTagCache({ baseShardSize: 4, regionalCache: true });
416445
expect(cache.localCache).toBeUndefined();
417446
expect(await cache.getCacheInstance()).toBe("cache");
418447
expect(cache.localCache).toBe("cache");
419-
// @ts-expect-error - Defined on cloudfare context
448+
// @ts-expect-error - Defined on cloudflare context
420449
globalThis.caches = undefined;
421450
});
422451
});
@@ -433,7 +462,7 @@ describe("DOShardedTagCache", () => {
433462
});
434463

435464
it("should call .match on the cache", async () => {
436-
// @ts-expect-error - Defined on cloudfare context
465+
// @ts-expect-error - Defined on cloudflare context
437466
globalThis.caches = {
438467
open: vi.fn().mockResolvedValue({
439468
match: vi.fn().mockResolvedValue(new Response("1234567")),
@@ -449,13 +478,13 @@ describe("DOShardedTagCache", () => {
449478
expect(cacheResult.length).toBe(1);
450479
// "1234567" is a plain number (old format) → backward-compat parse
451480
expect(cacheResult[0]).toEqual({ tag: "tag1", revalidatedAt: 1234567, stale: 1234567, expire: null });
452-
// @ts-expect-error - Defined on cloudfare context
481+
// @ts-expect-error - Defined on cloudflare context
453482
globalThis.caches = undefined;
454483
});
455484

456485
it("should parse new JSON object format from the cache", async () => {
457486
const stored = JSON.stringify({ revalidatedAt: 1000, stale: 500, expire: 9999 });
458-
// @ts-expect-error - Defined on cloudfare context
487+
// @ts-expect-error - Defined on cloudflare context
459488
globalThis.caches = {
460489
open: vi.fn().mockResolvedValue({
461490
match: vi.fn().mockResolvedValue(new Response(stored)),
@@ -465,7 +494,7 @@ describe("DOShardedTagCache", () => {
465494
const doId = new DOId({ baseShardId: "shard-1", numberOfReplicas: 1, shardType: "hard" });
466495
const cacheResult = await cache.getFromRegionalCache({ doId, tags: ["tag1"] });
467496
expect(cacheResult[0]).toEqual({ tag: "tag1", revalidatedAt: 1000, stale: 500, expire: 9999 });
468-
// @ts-expect-error - Defined on cloudfare context
497+
// @ts-expect-error - Defined on cloudflare context
469498
globalThis.caches = undefined;
470499
});
471500
});
@@ -484,7 +513,7 @@ describe("DOShardedTagCache", () => {
484513

485514
it("should put the tags in the regional cache if the tags exists in the DO", async () => {
486515
const putMock = vi.fn();
487-
// @ts-expect-error - Defined on cloudfare context
516+
// @ts-expect-error - Defined on cloudflare context
488517
globalThis.caches = {
489518
open: vi.fn().mockResolvedValue({
490519
put: putMock,
@@ -506,13 +535,13 @@ describe("DOShardedTagCache", () => {
506535
"http://local.cache/shard/tag-hard;shard-1?tag=tag1",
507536
expect.any(Response)
508537
);
509-
// @ts-expect-error - Defined on cloudfare context
538+
// @ts-expect-error - Defined on cloudflare context
510539
globalThis.caches = undefined;
511540
});
512541

513542
it("should not put the tags in the regional cache if the tags does not exists in the DO", async () => {
514543
const putMock = vi.fn();
515-
// @ts-expect-error - Defined on cloudfare context
544+
// @ts-expect-error - Defined on cloudflare context
516545
globalThis.caches = {
517546
open: vi.fn().mockResolvedValue({
518547
put: putMock,
@@ -531,13 +560,13 @@ describe("DOShardedTagCache", () => {
531560

532561
expect(getTagDataMock).toHaveBeenCalledWith(["tag1"]);
533562
expect(putMock).not.toHaveBeenCalled();
534-
// @ts-expect-error - Defined on cloudfare context
563+
// @ts-expect-error - Defined on cloudflare context
535564
globalThis.caches = undefined;
536565
});
537566

538567
it("should put multiple tags in the regional cache", async () => {
539568
const putMock = vi.fn();
540-
// @ts-expect-error - Defined on cloudfare context
569+
// @ts-expect-error - Defined on cloudflare context
541570
globalThis.caches = {
542571
open: vi.fn().mockResolvedValue({
543572
put: putMock,
@@ -566,13 +595,13 @@ describe("DOShardedTagCache", () => {
566595
"http://local.cache/shard/tag-hard;shard-1?tag=tag2",
567596
expect.any(Response)
568597
);
569-
// @ts-expect-error - Defined on cloudfare context
598+
// @ts-expect-error - Defined on cloudflare context
570599
globalThis.caches = undefined;
571600
});
572601

573602
it("should put missing tag in the regional cache if `regionalCacheDangerouslyPersistMissingTags` is true", async () => {
574603
const putMock = vi.fn();
575-
// @ts-expect-error - Defined on cloudfare context
604+
// @ts-expect-error - Defined on cloudflare context
576605
globalThis.caches = {
577606
open: vi.fn().mockResolvedValue({
578607
put: putMock,
@@ -598,13 +627,13 @@ describe("DOShardedTagCache", () => {
598627
"http://local.cache/shard/tag-hard;shard-1?tag=tag1",
599628
expect.any(Response)
600629
);
601-
// @ts-expect-error - Defined on cloudfare context
630+
// @ts-expect-error - Defined on cloudflare context
602631
globalThis.caches = undefined;
603632
});
604633

605634
it("should not put missing tag in the regional cache if `regionalCacheDangerouslyPersistMissingTags` is false", async () => {
606635
const putMock = vi.fn();
607-
// @ts-expect-error - Defined on cloudfare context
636+
// @ts-expect-error - Defined on cloudflare context
608637
globalThis.caches = {
609638
open: vi.fn().mockResolvedValue({
610639
put: putMock,
@@ -627,7 +656,7 @@ describe("DOShardedTagCache", () => {
627656

628657
expect(getTagDataMock).toHaveBeenCalledWith(["tag1"]);
629658
expect(putMock).not.toHaveBeenCalled();
630-
// @ts-expect-error - Defined on cloudfare context
659+
// @ts-expect-error - Defined on cloudflare context
631660
globalThis.caches = undefined;
632661
});
633662
});

packages/cloudflare/src/api/overrides/tag-cache/do-sharded-tag-cache.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,12 +322,16 @@ class ShardedDOTagCache implements NextModeTagCache {
322322
}
323323
}
324324

325-
public async putToRegionalCache(optsKey: CacheTagKeyOptions, stub: DurableObjectStub<DOShardedTagCache>) {
325+
public async putToRegionalCache(
326+
optsKey: CacheTagKeyOptions,
327+
stub: DurableObjectStub<DOShardedTagCache>,
328+
prefetchedTagData?: Record<string, TagData>
329+
) {
326330
if (!this.opts.regionalCache) return;
327331
const cache = await this.getCacheInstance();
328332
if (!cache) return;
329333
const tags = optsKey.tags;
330-
const tagData = await stub.getTagData(tags);
334+
const tagData = prefetchedTagData ?? (await stub.getTagData(tags));
331335
await Promise.all(
332336
tags.map(async (tag) => {
333337
let data = tagData[tag];
@@ -451,7 +455,9 @@ class ShardedDOTagCache implements NextModeTagCache {
451455
result.set(tag, tagData[tag] ?? null);
452456
}
453457

454-
getCloudflareContext().ctx.waitUntil(this.putToRegionalCache({ doId, tags: remainingTags }, stub));
458+
getCloudflareContext().ctx.waitUntil(
459+
this.putToRegionalCache({ doId, tags: remainingTags }, stub, tagData)
460+
);
455461
})
456462
);
457463

0 commit comments

Comments
 (0)