Skip to content

LTRAC-1732: build(cli) - Bump @opennextjs/cloudflare to 1.20.6 - #3203

Merged
jorgemoya merged 1 commit into
canaryfrom
jorgemoya/ltrac-1732-opennext-1-20-5
Sep 3, 2026
Merged

LTRAC-1732: build(cli) - Bump @opennextjs/cloudflare to 1.20.6#3203
jorgemoya merged 1 commit into
canaryfrom
jorgemoya/ltrac-1732-opennext-1-20-5

Conversation

@jorgemoya

@jorgemoya jorgemoya commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Linear: LTRAC-1732

Follows #3202 (Next.js 16.3.4), which this required — 1.20.6's peer range is next >=15.5.24 <16 || >=16.3.3.

What/Why?

Moves the Cloudflare adapter 1.17.3 → 1.20.6 and the Wrangler version the build shells out to 4.90.0 → 4.128.0.

Both leave this repo's dependency graph. The adapter and Wrangler only belong to a project transformed for Commerce Hosting, so the root package.json should never have carried the adapter as a dependency. It moves to a devDependency of packages/catalyst, which is where it is actually needed: cloudflare-context-symbol.spec.ts imports it, and tsc typechecks templates/open-next.config.ts (the tsconfig sets no include). Wrangler is now declared nowhere — the build invokes a pinned version via dlx, and in the monorepo pnpm resolves it from the adapter's own ^4.125.0 peer.

That is also what fixed the stale Wrangler pin, rather than declaring it. pnpm had auto-installed 4.31.0 and recorded it in the lockfile — short of even the old ^4.65.0 peer — and it treats auto-installed peer resolutions as sticky, so pnpm update, pnpm install --force, and stripping the lock entries all failed to move it. Removing the root declaration and letting it resolve fresh lands 4.128.0 with no OpenNext peer warnings left.

API compatibility was verified by diffing the published tarballs, not by reading the changelog. Unchanged: defineCloudflareConfig, the OpenNextConfig type (index.d.ts and config.d.ts byte-identical), the four override subpaths the generated open-next.config.ts imports, ShardedDOTagCacheOptions, and the Queue/QueueMessage interfaces the custom selfFetchQueue implements. @opennextjs/aws goes 3.9.16 → 4.1.3, but v4.0.0 was a mislabeled major — upstream says it "should really have been 3.10.5" with no breaking changes.

No Ignition change is required, checked against the Go source rather than assumed. Ignition pins neither the adapter nor Wrangler (no version constant, no exec.Command, no Node toolchain — it consumes a pre-built worker.js + /assets/). What it does mirror is unchanged: the compat flags in pkg/cloudflare/upload/metadata.go (1.20.5 needs the same single nodejs_compat flag as 1.17.3) and the DO migration tag + DOQueueHandler/DOShardedTagCache/BucketCachePurge class names in pkg/cloudflare/upload/migrations/migrations.go.

Node 20 dropped from engines. WRANGLER_VERSION is what catalyst build/deploy invoke, and every Wrangler satisfying the adapter's ^4.125.0 peer requires Node ≥22 (the last supporting Node 20 is 4.86.0, below the floor). It was already inaccurate: the previous wrangler@4.90.0 pin also declares node >=22.0.0, so build/deploy could not work on Node 20 before this either. What genuinely regresses is catalyst start, which ran against the locally resolved 4.31.0 (Node ≥18).

catalyst build and catalyst deploy now offer to move the adapter pin, and reinstall when it moves. The pin lives in the project's own package.json and deploy skips setupCommerceHosting once isTransformed, so bumping the constant alone never reaches an existing Commerce Hosting project. catalyst upgrade cannot move it either — the adapter is injected into the project and absent from the upstream tree, so no 3-way merge ever sees it (upgrade.ts:49-51 records the same constraint for the CLI's own pin, which is why findStaleCli checks it out-of-band).

The check lives in buildCatalystProject, the path both commands share, so it runs exactly once immediately before the adapter is invoked — early enough for the install to land first. Putting it in a single command would have missed the catalyst builddeploy --prebuilt flow that setupCoreProject scaffolds scripts for.

reconcileOpenNextVersion acts only on a project genuinely behind (validRangesatisfiesminVersionlt), so one pinned ahead, one on a range that already admits the target, and one on a git/file specifier are left alone. Four gates stand before it writes anything:

Condition Behaviour Why
Project's Next too old for the adapter send to catalyst upgrade, don't offer would hand them an unsupported peer set
deploy --prebuilt warn only skips the build, so the bundle would never be compiled against the new adapter
No TTY (CI, Docker, scripted) warn only pnpm treats the lockfile as frozen; the required install would fail the deploy
User declines keep the pin their call

Every path that declines to act reports the exact command to run. OPENNEXT_REQUIRED_NEXT_RANGE backs the compatibility gate and is hand-synced with the version constant — read it off the new adapter's peerDependencies.next when bumping.

The peer stays an optional ^1.17.3 range, deliberately. An exact peer would make the upgraded CLI unresolvable for merchants still on the old adapter, so they could never install the version that offers the upgrade. Verified rather than assumed: npm fails with ERESOLVE — Conflicting peer dependency on a present-but-mismatched peer even when it is marked optional; optional excuses only an absent peer, which is what keeps it quiet for projects hosted somewhere that never installs the adapter.

Areas worth careful review

  • reconcileOpenNextVersion and its call site in deploy.ts — the only behavioural change; everything else is dependency and version bookkeeping.
  • The engines narrowing, released as minor. engines is advisory (pnpm warns, npm doesn't block without engine-strict) and no working Node 20 configuration is being removed, but flagging it as a judgement call.

Rollout/Rollback

Rollout is gradual, not fleet-wide: since Ignition doesn't pin the adapter, deployed stores keep running whatever version they last built with. The bump only takes effect per-store on next deploy.

On that first deploy, DOShardedTagCache runs an in-place SQLite migration adding stale/expire columns to its revalidations table, backing the SWR revalidateTag support added in 1.19.0. It is guarded and automatic. Rollback to 1.17.3 is tolerable — the older code ignores the extra columns.

withRegionalCache defaults were re-derived for Next 16 (bypassTagCacheOnCacheHit now defaults false, shouldLazilyUpdateOnCacheHit to !bypassTagCacheOnCacheHit). The generated config declares neither (see LTRAC-1458), so effective behaviour is unchanged and no config edit is needed.

Rollback is reverting this PR; no data migration blocks rolling forward.

Testing

  • CLI tsc --noEmit, eslint . --max-warnings 0, tsup build — all clean
  • CLI tests 683/683 passing (677 pre-upgrade baseline + 6 new)
  • cloudflare-context-symbol.spec.ts passes against the real 1.20.6 — confirming both that the __cloudflare-context__ key core/lib/kv/adapters/cloudflare-kv.ts reads directly is unchanged, and that the adapter still resolves after moving to a devDependency
  • pnpm install --frozen-lockfile clean after rebasing onto merged canary
  • No OpenNext peer warnings remain; wrangler@4.31.0 is gone from the lockfile
  • Coverage: 91.24% lines vs a 91.21% baseline on canary — the test:coverage threshold already fails on canary and CI runs pnpm run test, so this is a marginal improvement, not a regression

Not covered: a real catalyst build / OpenNext bundle, which needs store credentials. This is the main residual risk, because 1.20.3 rewrote Turbopack wasm-helper patching for Next 16.3's emit shape, and it would also confirm the output still fits Ignition's bundle limits (worker.js ≤ 40 MiB, each asset ≤ 25 MiB, ≤ 1000 files per pkg/cloudflare/bundle/limits.go). Worth running before merge.

Migration

None for this repo. Merchants on an older adapter pin are now prompted on deploy to move it, with the safe-to-take gates above.

Refs LTRAC-1732

@vercel

vercel Bot commented Sep 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
catalyst Ready Ready Preview Sep 3, 2026 2:56pm UTC

Request Review

@changeset-bot

changeset-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4ce6c0b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@bigcommerce/catalyst Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Report

Comparing against baseline from 715f481 (2026-09-03).

No bundle size changes detected.

@jorgemoya
jorgemoya force-pushed the jorgemoya/ltrac-1732-opennext-1-20-5 branch from dad23e0 to a589857 Compare September 2, 2026 16:24
@jorgemoya
jorgemoya force-pushed the jorgemoya/ltrac-1732-opennext-1-20-5 branch from a589857 to 620157a Compare September 2, 2026 16:31
@jorgemoya
jorgemoya force-pushed the jorgemoya/ltrac-1732-opennext-1-20-5 branch from 620157a to 9f87fe8 Compare September 2, 2026 16:42
@jorgemoya
jorgemoya force-pushed the jorgemoya/ltrac-1732-opennext-1-20-5 branch from 9f87fe8 to 2b46c2e Compare September 2, 2026 17:04
@jorgemoya jorgemoya changed the title LTRAC-1732: build(cli) - Bump @opennextjs/cloudflare to 1.20.5 LTRAC-1732: build(cli) - Bump @opennextjs/cloudflare to 1.20.6 Sep 2, 2026
@jorgemoya
jorgemoya force-pushed the jorgemoya/ltrac-1732-opennext-1-20-5 branch from 2b46c2e to 37d5565 Compare September 2, 2026 17:11
@jorgemoya
jorgemoya force-pushed the jorgemoya/ltrac-1732-opennext-1-20-5 branch from 37d5565 to 4acebf5 Compare September 2, 2026 17:25
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Unlighthouse Performance Comparison — Vercel

Comparing PR preview deployment Unlighthouse scores vs production Unlighthouse scores.

Summary Score

Aggregate score across all categories as reported by Unlighthouse.

Prod Desktop Prod Mobile Preview Desktop Preview Mobile
Score 89 93 92 95

Category Scores

Category Prod Desktop Prod Mobile Preview Desktop Preview Mobile
Performance 74 87 71 89
Accessibility 95 92 95 92
Best Practices 100 100 100 100
SEO 88 100 88 100

Core Web Vitals

Metric Prod Desktop Prod Mobile Preview Desktop Preview Mobile
LCP 3.6 s 4.0 s 5.7 s 3.8 s
CLS 0.05 0 0.039 0
FCP 1.2 s 1.2 s 1.2 s 1.2 s
TBT 50 ms 10 ms 0 ms 0 ms
Max Potential FID 100 ms 80 ms 30 ms 50 ms
Time to Interactive 3.7 s 4.4 s 5.7 s 4.4 s

Full Unlighthouse report →

@jorgemoya
jorgemoya force-pushed the jorgemoya/ltrac-1732-opennext-1-20-5 branch from 4acebf5 to 7a3be0b Compare September 2, 2026 19:28
@jorgemoya
jorgemoya force-pushed the jorgemoya/ltrac-1732-opennext-1-20-5 branch from 7a3be0b to 9a99d40 Compare September 2, 2026 19:36
@jorgemoya
jorgemoya force-pushed the jorgemoya/ltrac-1732-opennext-1-20-5 branch from 9a99d40 to c930d66 Compare September 2, 2026 19:54
@jorgemoya
jorgemoya marked this pull request as ready for review September 2, 2026 20:01
@jorgemoya
jorgemoya requested a review from a team as a code owner September 2, 2026 20:01

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we maybe make the comments more succinct if we need them? At this length they don't seem that useful

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, cleaning them up.

@jorgemoya
jorgemoya force-pushed the jorgemoya/ltrac-1732-opennext-1-20-5 branch from c930d66 to c2f3060 Compare September 3, 2026 14:33
@jorgemoya
jorgemoya force-pushed the jorgemoya/ltrac-1732-opennext-1-20-5 branch from c2f3060 to 6491f7b Compare September 3, 2026 14:41
@jorgemoya

Copy link
Copy Markdown
Contributor Author

Local test:

❯ node ../packages/catalyst/dist/cli.js build
◢ @bigcommerce/catalyst v1.3.0

Loaded environment variables from .env.local

✔ This project pins @opennextjs/cloudflare 1.17.3, but this CLI targets 1.20.6. Adapter fixes released since then are not applied. Update it to 1.20.6 and reinstall now?
 Yes
ℹ Updated @opennextjs/cloudflare to 1.20.6.
✔ Dependencies installed successfully.
◐ Copying templates...
✔ Templates copied

Move the Cloudflare adapter from 1.17.3 to 1.20.6 and the Wrangler version the
build shells out to from 4.90.0 to 4.128.0.

Remove the stray @opennextjs/cloudflare dependency from the repo root. It was
never meant to be there -- the adapter only belongs to a project that has been
transformed for Commerce Hosting. It was also redundant: autoInstallPeers is on,
so the peer declared by packages/catalyst already installs the adapter into that
package, which is what resolves it for the Cloudflare context contract spec and
for tsc typechecking templates/open-next.config.ts (its tsconfig sets no
`include`). Wrangler is deliberately not declared anywhere either -- the build
invokes a pinned version through `dlx`.

Dropping the root declaration is also what fixed the stale Wrangler pin. pnpm
had auto-installed 4.31.0 and recorded it in the lockfile, short of even the old
^4.65.0 peer, and it treats auto-installed peer resolutions as sticky -- pnpm
update, pnpm install --force, and stripping the lock entries all failed to move
it. Letting it resolve fresh lands 4.128.0 with no OpenNext peer warnings left.

Offer to move a stale adapter pin on the shared build path, and reinstall when
it moves. The pin lives in the project's own package.json and deploy skips
setupCommerceHosting once isTransformed is true, so bumping the constant alone
never reaches an existing Commerce Hosting project. `catalyst upgrade` cannot
move it either: the adapter is injected into the project and absent from the
upstream tree, so no 3-way merge ever sees it, which is the same reason
findStaleCli checks the CLI's own pin out-of-band.

The check lives in buildCatalystProject rather than in a single command, so
`catalyst build` and `catalyst deploy` both get it exactly once, immediately
before the adapter is invoked and early enough for the install to land first.
`catalyst deploy --prebuilt` skips the build entirely, so it warns instead --
changing dependencies there would upload a bundle the new adapter never
compiled.

reconcileOpenNextVersion only acts on a project that is genuinely behind
(validRange -> satisfies -> minVersion -> lt), so one pinned ahead, one on a
range that already admits the target, and one on a git or file specifier are all
left alone. It also refuses when the project's Next version is older than the
adapter supports, sending the user to `catalyst upgrade` instead of handing them
an unsupported peer set, and never prompts without a TTY: pnpm treats the
lockfile as frozen in CI, so the install that must follow would fail the deploy.
Every path that declines to act reports the exact command to run.

OPENNEXT_REQUIRED_NEXT_RANGE records the `next` peer range the pinned adapter
declares, so the compatibility gate has something to check against. It is
hand-synced with OPENNEXT_CLOUDFLARE_VERSION -- read it off the new adapter's
peerDependencies.next when bumping.

Keep the adapter peer an optional ^1.17.3 range. An exact peer would make the
upgraded CLI unresolvable for projects still on the older adapter -- npm fails
with ERESOLVE on a present-but-mismatched peer, and `optional` does not excuse
that, only an absent one -- so those merchants could never install the CLI
version that offers the upgrade. Optional keeps it quiet on projects hosted
somewhere that never installs the adapter at all.

Keep OPENNEXT_CLOUDFLARE_VERSION an exact version. core's Cloudflare KV adapter
reads globalThis[Symbol.for('__cloudflare-context__')] directly, and if a
version changed that key it would silently return null and every native-hosted
store would quietly degrade to an in-process cache with no error and no signal.
cloudflare-context-symbol.spec.ts guards that by asserting this literal. 1.20.6
was verified against it: the symbol key, index.d.ts, config.d.ts, all four
override subpaths and their types, and the three Durable Object class names are
identical to 1.20.5.

Drop Node 20 from the CLI's engines range. Every Wrangler release satisfying the
adapter's ^4.125.0 peer requires Node 22 or later, so advertising Node 20 would
be inaccurate -- and already was, since the previously pinned wrangler@4.90.0
also declares node >=22.0.0, meaning catalyst build and deploy could not work on
Node 20 before this change either. What does regress is catalyst start, which
ran against a locally resolved 4.31.0 supporting Node 18+.

The API this repo depends on is unchanged: defineCloudflareConfig, the
OpenNextConfig type, the four override subpaths the generated open-next.config
imports, ShardedDOTagCacheOptions, and the Queue interface selfFetchQueue
implements. The Durable Object class names and compatibility flags Ignition
mirrors are unchanged too, so no Ignition change is required.

build.spec.ts now imports WRANGLER_VERSION rather than duplicating the literal,
so the two cannot drift again.

Refs LTRAC-1732
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jorgemoya
jorgemoya added this pull request to the merge queue Sep 3, 2026
Merged via the queue into canary with commit b68192a Sep 3, 2026
18 of 19 checks passed
@jorgemoya
jorgemoya deleted the jorgemoya/ltrac-1732-opennext-1-20-5 branch September 3, 2026 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants