Skip to content

feat(cache): extract Cloudflare cache adapters into @vinext/cloudflare - #1748

Merged
james-elicx merged 5 commits into
mainfrom
refactor/extract-vinext-cloudflare-cache
Jun 5, 2026
Merged

feat(cache): extract Cloudflare cache adapters into @vinext/cloudflare#1748
james-elicx merged 5 commits into
mainfrom
refactor/extract-vinext-cloudflare-cache

Conversation

@james-elicx

Copy link
Copy Markdown
Member

What

Moves the Cloudflare cache adapters out of vinext into a new publishable @vinext/cloudflare package (packages/cloudflare).

Moved (history preserved via rename):

  • cache/kv-data-adapter.ts + .runtime.tsKVCacheHandler, kvDataAdapter, ENTRY_PREFIX
  • cache/cdn-adapter.ts + .runtime.tsCloudflareCdnCacheAdapter, cdnAdapter

tpr.ts stays in vinext (only the cache adapters moved, per scope).

Dependency model

vinext now depends on @vinext/cloudflare (workspace:*); the new package declares vinext as a peer dependency. This is the intentional (approved) bidirectional edge:

  • shims/cdn-cache.ts imports CloudflareCdnCacheAdapter from @vinext/cloudflare/cache/cdn-adapter.runtime
  • cloudflare/tpr.ts imports ENTRY_PREFIX from there
  • The vinext/cloudflare barrel still re-exports KVCacheHandler for backward compatibility

Neither package needs the other's dist to build — type imports resolve to source via tsconfig paths, so there's no build-order deadlock.

Wiring

  • tsconfig.json: added @vinext/cloudflare + @vinext/cloudflare/cache/* path mappings to source
  • vite.config.ts: added @vinext/cloudflare/cache → source alias in both test projects (mirrors the existing vinext/shims alias) so tests run against source without a prior build
  • Build: root build / postinstall build @vinext/cloudflare#build before vinext#build
  • Publish: preview-release.yml (pkg-pr-new) publishes both packages; publish.yml bumps + publishes @vinext/cloudflare in lockstep with vinext
  • knip.ts: added the new workspace; removed a now-unused re-export
  • Consumers updated: apps/web, examples/workers-cache, READMEs, the deploy.ts error message, and the cache plugin-option JSDoc now reference @vinext/cloudflare/cache/*

Breaking change

External users importing vinext/cloudflare/cache/* must switch to @vinext/cloudflare/cache/*. The vinext/cloudflare barrel still re-exports KVCacheHandler for compatibility.

Verification

  • vp check — clean (format + lint + types)
  • pnpm knip — clean
  • Targeted tests pass: kv-cache-handler, cloudflare-cdn-cache, cache-adapters-config, app-route-handler-response, deploy, tpr-kv-keys, shims, isr-cache, fetch-cache, app-router

Letting CI run the full suite + Playwright E2E.

@pkg-pr-new

pkg-pr-new Bot commented Jun 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/cloudflare/vinext/@vinext/cloudflare@1748
npm i https://pkg.pr.new/cloudflare/vinext@1748

commit: 427a4e6

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor
Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
workers-cache preview production
web preview production

Comment thread packages/cloudflare/src/index.ts Outdated
Comment thread packages/cloudflare/.npmignore Outdated
Comment thread packages/cloudflare/package.json Outdated
Comment thread packages/cloudflare/package.json Outdated
Comment thread package.json
"private": true,
"scripts": {
"build": "vp run vinext#build",
"build": "vp run @vinext/cloudflare#build && vp run vinext#build",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

cant we use vp task runner to run this in all packages?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I tried, but the task runner can't order this one. vinext depends on @vinext/cloudflare, and @vinext/cloudflare has vinext as a peer dep, so the workspace graph is a cycle. Every graph-ordered variant fails:

$ vp run --filter "vinext..." build
error: Cycle dependency detected: vinext#build -> @vinext/cloudflare#build -> vinext#build

-r, -F vinext -F @vinext/cloudflare, and --ignore-depends-on all hit the same cycle (vp counts peerDependencies in the graph). The build order is actually irrelevant here — both packages compile from source via tsconfig paths, so neither needs the other's dist first — so I kept the two explicit invocations. Open to a better idea if vp has a 'select these packages, don't topo-order' mode I missed.

Comment thread README.md
Comment thread vite.config.ts
Comment thread packages/vinext/src/cloudflare/index.ts Outdated
Comment thread packages/cloudflare/README.md Outdated
@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review

@james-elicx james-elicx changed the title refactor(cache): extract Cloudflare cache adapters into @vinext/cloudflare feat(cache): extract Cloudflare cache adapters into @vinext/cloudflare Jun 5, 2026
@james-elicx
james-elicx marked this pull request as ready for review June 5, 2026 13:01
@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

1 similar comment
@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

…flare

Move the Cloudflare KV data cache and edge CDN cache adapters out of
vinext into a new publishable @vinext/cloudflare package:

  - cache/kv-data-adapter(.runtime).ts  (KVCacheHandler, kvDataAdapter)
  - cache/cdn-adapter(.runtime).ts       (CloudflareCdnCacheAdapter, cdnAdapter)

tpr.ts stays in vinext. vinext now depends on @vinext/cloudflare
(workspace:*) and the package declares vinext as a peer dep; both build
from source via tsconfig paths so there is no build-order cycle. The
vinext/cloudflare barrel still re-exports KVCacheHandler for back-compat.

Wires up tsconfig paths, a vitest source alias, root build/postinstall,
and the preview/publish workflows for the new package. Updates internal
consumers (apps/web, examples/workers-cache), docs, and tests.
vinext now depends on @vinext/cloudflare, which isn't published to npm
yet. The create-next-app smoke test packs vinext locally and resolves
its deps from the registry, so the install (and dev server) failed with
ERR_PNPM_FETCH_404 for @vinext/cloudflare.

Pack @vinext/cloudflare alongside vinext and add a pnpm override in the
scaffolded project pointing at the local tarball so the dependency
resolves offline.
- Remove the root barrel export from @vinext/cloudflare; expose only the
  ./cache/* subpaths via a wildcard export (no root main/types).
- vinext/cloudflare re-exports KVCacheHandler from the full subpath.
- Drop the redundant .npmignore (the package.json "files" allowlist
  already restricts the publish to dist).
- Remove the unsupported imperative setCacheHandler/KVCacheHandler usage
  from both READMEs; the cache plugin config is the supported approach.
- Simplify test wiring: drop the now-unused @vinext/cloudflare tsconfig
  path and dedupe the vitest source alias into a shared constant.
The @vinext/cloudflare config uses vite-plus and nothing imports vite, so
the vite devDependency was unused. build/check/knip stay green without it.
@james-elicx
james-elicx force-pushed the refactor/extract-vinext-cloudflare-cache branch from 8473ca4 to 9d9c09a Compare June 5, 2026 13:41
@cloudflare cloudflare deleted a comment from ask-bonk Bot Jun 5, 2026
@cloudflare cloudflare deleted a comment from ask-bonk Bot Jun 5, 2026
@cloudflare cloudflare deleted a comment from ask-bonk Bot Jun 5, 2026
@cloudflare cloudflare deleted a comment from ask-bonk Bot Jun 5, 2026
@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

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.

Review: extract Cloudflare cache adapters into @vinext/cloudflare

I checked out the branch, ran the full verification (vp check, vp exec knip, and the targeted test files), simulated the published-package exports resolution, and traced the runtime/build wiring. The change is correct and ships cleanly — all local checks are green and the packaging resolves end-to-end.

Verified working

  • vp check — clean (format + lint + 755 files type-checked).
  • vp exec knip — clean. The entriesFromPackageJson("packages/cloudflare/package.json") entry derives src/cache/*.{ts,tsx} from the ./cache/* wildcard, which globs all four files (incl. the .runtime.ts ones), so they aren't flagged as unused.
  • Targeted tests pass: kv-cache-handler, cloudflare-cdn-cache, cache-adapters-config, shims, tpr-kv-keys, deploy, app-route-handler-response, isr-cache, fetch-cache.
  • Exports resolution@vinext/cloudflare/cache/cdn-adapter.runtime./dist/cache/cdn-adapter.runtime.js (+ matching .d.ts), and all four dist files exist after build. The ./cache/* wildcard correctly covers the .runtime subpaths consumers import.
  • Build externalizationskipNodeModulesBundle: true keeps the vinext@vinext/cloudflare edge external in both dists. The built cloudflare runtime keeps vinext/shims/* as external imports (resolved via the vinext peer dep + vinext's ./shims/* wildcard export). vinext's built cloudflare/index.js correctly re-exports KVCacheHandler from @vinext/cloudflare/cache/kv-data-adapter.runtime, preserving the vinext/cloudflare barrel for backward compat.
  • Circular runtime edge is real (cdn-adapter.runtime imports the getRequestExecutionContext value from vinext/shims/request-context, and shims/cdn-cache imports CloudflareCdnCacheAdapter from @vinext/cloudflare). ESM tolerates it because both bindings are only used lazily (class/fn definitions, not top-level execution), and the instanceof CloudflareCdnCacheAdapter check stays single-instance via the source alias in tests and the single dist file in prod. Confirmed by the passing cdn-cache resolver tests.
  • No lingering vinext/cloudflare/cache/* references anywhere in source/docs/fixtures.

Minor observations (non-blocking)

  1. PR description says publish.yml bumps + publishes the new package — that file doesn't exist. Releases run through changesets (release.yml). The actual mechanism is fine: discoverPublishablePackages() in scripts/create-changeset.mts auto-discovers any non-private packages/*, and changeset publish will publish @vinext/cloudflare@0.0.5 on the next release run because that version isn't on npm yet (independent of whether a changeset bumps it). Worth fixing the PR body to avoid future confusion, but no code change needed.

  2. First-publish ordering of the bidirectional edge. Published vinext will pin @vinext/cloudflare at its converted workspace:* version (currently 0.0.5). changeset publish publishes in dependency order, so @vinext/cloudflare lands before vinext references it — but it's worth a one-time sanity check on the first real release that the npm consumer of vinext resolves @vinext/cloudflare without a registry gap (the CI create-next-app job already exercises this offline via the local-tarball override, which is a good guard).

  3. Duplicated cache-control-metadata helpers. packages/cloudflare/src/utils/cache-control-metadata.ts re-implements isUnknownRecord / readCacheControlNumberField (and a private readRecordField) that already live in vinext core. This is a deliberate self-containment trade-off (avoids importing an unexported deep vinext internal), and the JSDoc says so. Fine as-is; just note the two copies can drift — if the cache-control shape ever changes, both need updating.

LGTM. The packaging, build externalization, exports map, and backward-compat barrel all check out.

@ask-bonk

ask-bonk Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #1748. Summary of my findings:

Verdict: LGTM — ships cleanly

I checked out the branch, ran the full local verification, and traced the packaging/runtime/build wiring end-to-end.

Verified working:

  • vp check clean, vp exec knip clean, all targeted test files pass (1125+418+ tests across the cache/shim/deploy suites).
  • The ./cache/* exports wildcard correctly resolves the .runtime subpaths to existing dist files (./dist/cache/*.js + .d.ts).
  • skipNodeModulesBundle: true keeps the bidirectional vinext@vinext/cloudflare edge external in both built dists; vinext/shims/* stays external in the cloudflare runtime and resolves via the vinext peer dep + ./shims/* wildcard export.
  • The circular runtime edge is real but ESM-safe (lazy bindings only), and instanceof CloudflareCdnCacheAdapter stays single-instance.
  • Built vinext/cloudflare barrel re-exports KVCacheHandler from the new package — backward compat preserved.
  • No lingering old vinext/cloudflare/cache/* references anywhere.

Minor (non-blocking) notes:

  1. PR body claims a publish.yml bumps/publishes the package — that file doesn't exist; releases go through changesets, which auto-discovers the new package and will publish 0.0.5 on next run. Worth correcting the body.
  2. First-publish ordering of the bidirectional edge is worth a one-time sanity check on the real release (the CI create-next-app local-tarball override already guards this).
  3. cache-control-metadata helpers are duplicated in the new package (deliberate self-containment) — flagged only as a future drift risk.

No code changes required.

github run

Comment thread packages/cloudflare/package.json Outdated
@james-elicx
james-elicx enabled auto-merge (squash) June 5, 2026 13:48
@james-elicx
james-elicx disabled auto-merge June 5, 2026 13:48
@james-elicx
james-elicx merged commit 8d00797 into main Jun 5, 2026
39 checks passed
@james-elicx
james-elicx deleted the refactor/extract-vinext-cloudflare-cache branch June 5, 2026 13:57
@github-actions github-actions Bot mentioned this pull request Jun 5, 2026
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.

1 participant