Commit Graph

18 Commits

Author SHA1 Message Date
github-actions[bot] 5471b41eec chore: version packages (#2428)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-03 09:40:50 +00:00
James Anderson e0db846716 fix(cloudflare): respect TPR cache opt-outs (#2493) 2026-07-02 23:38:48 +01:00
James Anderson 5f8048f660 fix(app-router): align app static ISR lifecycle (#2472)
* fix(app-router): align app static ISR lifecycle

* fix(fetch-cache): honor shorter revalidate on hits

* fix(cache): honor read-time revalidate in KV cache
2026-07-02 20:19:02 +01:00
James Anderson b5aded498a fix(cloudflare): allow pages deploy without custom worker (#2429) 2026-06-30 08:21:47 +00:00
github-actions[bot] a399fc55e2 chore: version packages (#2293)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-29 19:46:44 +01:00
James Anderson bcd4e44d96 feat(build): support prerender vite config (#2415)
* feat(build): support prerender vite config

* feat(init): offer prerender config opt-in

* fix(cloudflare): export prerender deploy helper

* fix(cloudflare): avoid redundant prerender config load

* fix(build): quiet adapter fallback warnings
2026-06-29 19:16:08 +01:00
James Anderson 0a48f0f7fb feat(cloudflare): move deploy command to cloudflare package (#2405)
* feat(cloudflare): move deploy command to cloudflare package

* fix(cloudflare): expose deploy cli bin
2026-06-29 16:47:28 +00:00
James Anderson 25a3c26f7a feat(init): scaffold for cloudflare and node (#2279)
* feat(init): select deployment platform

* refactor(init): AST-update deployment config

* feat(init): configure Cloudflare cache and images

* fix(init): avoid backtracking in config updates

* fix(init): cover packaged Cloudflare setup

* ci: allow Cloudflare init smoke builds

* ci: fix packaged Cloudflare dependency install

* test(e2e): isolate browser fixture servers

* chore: allow Windows taskkill binary

* fix(init): configure Cloudflare image adapter

* fix(init): address platform setup review

* fix(init): address cache setup review

* fix(init): hide workers cache option

* fix(init): align generated config indentation

* fix(init): validate cloudflare setup before mutation

* fix(init): preserve additive config syntax

* fix(init): tidy interactive prompt spacing

* fix(images): rename Cloudflare optimizer builder

* refactor(images): remove draft name migration

* fix(init): install dependencies after setup

* fix(init): clarify setup recovery steps

* fix(init): ignore Wrangler state for Cloudflare

* fix lockfile
2026-06-26 17:09:19 +01:00
James Anderson b4843d75c1 feat(images): configure image optimization via vinext({ images }) adapter (#1873)
* feat(images): configure image optimization via vinext({ images }) adapter

Move server-side image optimization from a hand-wired custom worker entry to a
declarative `vinext({ images: { optimizer } })` option, mirroring the cache
adapter pattern. The default entries now handle `/_next/image` through a
registered optimizer, so no custom worker is required, and the same config
works across all targets — optimizing on Cloudflare, gracefully serving images
unoptimized on Node/dev where the binding is unavailable (like KV cache
degrading to in-memory).

- add an ImageOptimizer registry (set/getImageOptimizer +
  handleConfiguredImageOptimization) in server/image-optimization.ts
- generate `virtual:vinext-image-adapters` (registerConfiguredImageOptimizer)
  from the new `images` plugin option
- add @vinext/cloudflare/image/image-adapter: imageAdapter() builder + runtime
  factory reading the env.IMAGES binding
- handle /_next/image in the default app-router entry and the generated Pages
  worker via the registry; inline next.config `images` (allowed widths +
  security headers) into the RSC entry
- vinext deploy points App Router `main` at vinext/server/app-router-entry
  (no generated worker) and prints a hint to enable the optimizer

next.config `images` (remotePatterns, deviceSizes, dangerouslyAllowSVG, etc.)
continues to drive the standard Next.js options; the vite-config
`images.optimizer` only selects the runtime transform backend.

* fix(images): cover deploy image-hint helpers and preserve optimizer this-binding

The Check CI job failed because knip flagged viteConfigHasImageAdapter and
formatImageOptimizationHint as unused exports — they were only called inside
deploy.ts. Cover both with unit tests in tests/deploy.test.ts (mirroring the
existing viteConfigHasCacheAdapter / formatMissingCacheAdapterError suites),
which also closes the coverage gap for the new deploy hint path.

Also wrap the registered optimizer's transformImage in
handleConfiguredImageOptimization instead of detaching the method, so an
optimizer implemented as a class instance keeps its this binding.

* fix(images): honor configured deviceSizes/imageSizes on the App Router Node prod server

Review follow-up (ask-bonk):

- The App Router prod server (vinext start) validated /_next/image widths
  against the hardcoded Next.js defaults, rejecting valid optimizer URLs with
  400 when the app configures custom images.deviceSizes/imageSizes — while the
  Cloudflare worker entry and the Pages prod path already honored them. Read
  the __imageAllowedWidths constant inlined into the RSC entry (falling back to
  the defaults for older builds), matching how __assetPrefix/__basePath are read.
- Lock in the this-binding behavior of handleConfiguredImageOptimization with a
  class-instance optimizer test.

* fix(images): pass an explicit empty allowed-widths config through on vinext start

Review follow-up (ask-bonk, awareness note): the old-build fallback guard
conflated a missing __imageAllowedWidths export with an explicit empty
deviceSizes/imageSizes config, mapping the latter to the Next.js defaults on
the Node App Router path while the Cloudflare worker passes the empty array
straight through. Only fall back to the defaults when the export is absent.

* refactor(images): read App Router image config from the RSC entry, retire the JSON sidecar

Review follow-up (ask-bonk): the App Router had two parallel build-time sources
for next.config images security/header settings — the __imageConfig constant
inlined into the RSC entry (read by the Cloudflare worker entry) and the
image-config.json sidecar written by the vinext:image-config plugin (read by
vinext start). Unify on the RSC entry export: prod-server now reads
rscModule.__imageConfig, keeping image-config.json only as a read-side fallback
for dist outputs built by older vinext versions, and the sidecar writer plugin
is removed.

* fix(deploy): keep wrangler main on a user-authored worker entry for App Router

Review follow-up (ask-bonk): an App Router app with a custom worker/index.ts
but no wrangler.jsonc would have had its custom worker silently dropped —
generateWranglerConfig unconditionally pointed main at the default
vinext/server/app-router-entry. Respect hasWorkerEntry so a user-authored
worker keeps winning for both routers, with a regression test.

* fix(images): expose Cloudflare optimizer under images path

* fix(deploy): install Cloudflare image adapter package

* fix(examples): declare Cloudflare image adapter package

* test(images): update App Router image config codegen assertions

* fix(examples): configure image optimizer adapters
2026-06-23 20:20:36 +01:00
github-actions[bot] 5310d14170 chore: version packages (#1940)
* chore: version packages

* Apply suggestion from @james-elicx

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: James Anderson <james@eli.cx>
2026-06-15 09:54:40 +00:00
Nathan Nguyen 47b38a91f3 fix(app-router): recover SSR shell render errors via __next_error__ document (#1908)
* fix(plugin): resolve vinext/shims/* package subpaths to local shim files

Runtime helper modules embedded into generated entries import vinext's
own shims by package subpath (e.g. `vinext/shims/headers`), while source
checkouts alias userland `next/*` imports to the local shim files. The
two specifiers resolved to different module instances, so request-scoped
singleton state (navigation context, headers) split between the source
shim copy and the package export copy.

The violated invariant is that every shim module must be a per-request
singleton regardless of import specifier. Resolve `vinext/shims/*`
through the same plugin path as the `next/*` aliases so both forms land
on the local shim files.

Exercised by the SSR shell-error recovery browser spec, whose fixture
imports vinext from the source checkout and depends on shared
navigation state across both import forms.

* fix(app-router): recover SSR shell render errors via __next_error__ document

When the HTML (Fizz) render rejects during SSR, vinext re-rendered a
server-side global-error page whose flight payload encodes the error
tree. For an app without a custom global-error.tsx that meant the
default error card with no path back to the real page: an SSR-phase-only
throw (e.g. a client component using the "throw to opt out of server
rendering" pattern) left the browser stuck on the card even though the
client render would succeed.

The violated expectation is Next.js's shell-error semantics: a failed
HTML shell is served as the default `__next_error__` error document
carrying the ORIGINAL flight payload and the bootstrap module, and the
browser re-renders the real tree from that payload with createRoot
instead of hydrating. Local error.tsx boundaries still win — they ship
inside the flight payload and catch the re-thrown error client-side.

handleSsr now resolves to that recovery document instead of rejecting,
but only when both hold:
- the error did not originate in the RSC render (no string `digest`),
  so flight errors, redirect()/notFound(), and server-component throws
  keep driving the existing rejection-based boundary machinery, and
- the app has no custom global-error.tsx (the generated entry knows at
  build time and threads hasCustomGlobalError through dispatch/render
  options); apps with one keep the server-rendered boundary re-render.

The browser entry switches from hydrateRoot to createRoot when the
document root carries id="__next_error__", dropping the error-shell
styles first.

Covered by the new ssr-error-shell-recovery browser spec (recovery to
real content, local error.tsx for SSR-only and unconditional client
throws) and the existing tests/nextjs-compat/global-error.test.ts
boundary-semantics suite.

* refactor(review): address PR 1908 non-blocking notes

- Add static prerender no-boundary recovery regression test to
  ssr-error-shell-recovery.browser.spec.ts
- Document broad __next_error__ browser marker in app-browser-entry.ts
- Extract stripJsExtension to utils/path.ts and wire into all shim
  resolution sites (vinext/shims/* + react-server shims)

Non-functional: targeted regression coverage + code documentation
+ minor resolver hardening per reviewer feedback.

* refactor(review): clarify shell recovery assumptions

* fix(ssr): cancel abandoned prerender streams

* refactor(ssr): clarify error shell root options

* fix(isr): recover shell errors during regeneration

* fix(ssr): scope client recovery to marked shells

* fix(plugin): explicitly filter vinext shim subpaths

* fix(plugin): retain null-prefixed shim resolution

* test(nextjs-compat): cover no-boundary shell recovery fallback

An unconditional client throw during SSR shell recovery without a local error boundary must still land on the default global-error card. Without a regression test, a future change could tear down the recovery shell and leave a blank document.\n\nAdd a production browser case that exercises the no-boundary route and asserts the default error UI after the client re-render throws again.

* fix(app-router): preserve shell recovery error semantics

* test(app-router): harden shell recovery cache semantics

* fix(app-router): mark global error responses uncacheable

* test(app-router): align error response status expectations

* fix(build): preserve null-prefixed og resolution

* fix(cache): delegate cache header cleanup to adapters

---------

Co-authored-by: James <james@eli.cx>
2026-06-14 22:28:25 +00:00
github-actions[bot] 91c169a8fc chore: version packages (#1875)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-12 10:30:11 +00:00
James Anderson 206b016303 fix(cloudflare): update cache adapter jsdoc and examples (#1898)
* docs: remove cdn adapter from examples

* update adapter docs
2026-06-10 19:16:11 +00:00
github-actions[bot] 2b2ed00f22 chore: version packages (#1759)
* chore: version packages

* add header

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: James <james@eli.cx>
2026-06-08 09:45:43 +00:00
James Anderson d3870efe57 refactor(cache): deprecate imperative cache handler setters and exports (#1800)
Steer consumers toward the declarative `cache` option on the vinext()
plugin (with the @vinext/cloudflare builders kvDataAdapter/cdnAdapter)
instead of imperatively wiring cache handlers from a worker entry.

- Mark setDataCacheHandler, setCacheHandler, and setCdnCacheAdapter as
  @deprecated with config-based migration examples. They remain functional
  as the internal registration target used by virtual:vinext-cache-adapters.
- Mark the manually-instantiated handler exports MemoryCacheHandler and
  KVCacheHandler as @deprecated for consumers.
- Update the next/cache type declarations in next-shims.d.ts to match.
- Fix stale AGENTS.md claim: the default data cache is in-memory
  (MemoryCacheHandler) in all runtimes; KV is opt-in, not the default.
2026-06-07 00:05:49 +00:00
James Anderson 584661f37f chore(release): stop changesets force-majoring @vinext/cloudflare on every vinext bump (#1764)
Changesets' shouldBumpMajor rule force-bumps a dependent to MAJOR when a
peerDependency receives any non-patch bump. Combined with workspace:* being
resolved as an exact version pin (always out of range on any vinext change),
@vinext/cloudflare was majored on essentially every release (e.g. 0.0.1 ->
1.0.0 in PR #1759), even with zero cloudflare changes.

Fix is two parts:
- .changeset/config.json: enable onlyUpdatePeerDependentsWhenOutOfRange so
  peer-dependent bumps respect the declared range instead of force-majoring.
- packages/cloudflare/package.json: widen the published peer range off the
  exact-pin (workspace:* -> >=0.0.0). devDependencies.vinext: workspace:*
  is kept so local linking is unaffected.
2026-06-05 15:37:35 +01:00
Stephen Zhou 5a0fd69b37 chore: disable package sourcemaps (#1762) 2026-06-05 15:33:21 +01:00
James Anderson 8d00797ada feat(cache): extract Cloudflare cache adapters into @vinext/cloudflare (#1748)
* refactor(cache): extract Cloudflare cache adapters into @vinext/cloudflare

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.

* ci(create-next-app): install @vinext/cloudflare from local tarball

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.

* refactor(cloudflare): address review feedback

- 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.

* chore(cloudflare): drop unused vite devDependency

The @vinext/cloudflare config uses vite-plus and nothing imports vite, so
the vite devDependency was unused. build/check/knip stay green without it.

* Apply suggestion from @james-elicx
2026-06-05 14:57:20 +01:00