Skip to content

DEVEX-1485: Consolidate cache profiles ahead of Cloudflare compatibility work - #25

Merged
CNanninga merged 4 commits into
mainfrom
DEVEX-1485-cloudflare-compatibility
Aug 27, 2026
Merged

DEVEX-1485: Consolidate cache profiles ahead of Cloudflare compatibility work#25
CNanninga merged 4 commits into
mainfrom
DEVEX-1485-cloudflare-compatibility

Conversation

@CNanninga

Copy link
Copy Markdown
Collaborator

Description

Groundwork for hosting this app on Cloudflare Workers. Cloudflare can't run Cache Components: the PPR staged-render path corrupts streamed HTML on workerd via @opennextjs/cloudflare — chunks of the RSC flight payload get emitted outside their <script> wrapper and render as raw JSON on the page. That's tracked upstream in opennextjs-cloudflare#1225 and #1318, and no released adapter version fixes it. A Cloudflare build therefore has to cache at the fetch level instead.

This PR changes no caching behavior. It moves the parts that don't depend on where caching attaches into one module with the same name and shape it has on the Cloudflare branch, so the two implementations differ only where they genuinely must.

Consolidates the profiles and the enable switch into lib/cache/cache-profiles.ts. cacheLife accepts an inline { stale, revalidate, expire } object as well as a named profile, so the durations no longer need a cacheLife block in next.config.ts. Each use cache boundary now selects one with cacheLife(cacheProfile(CACHE_PROFILE_STANDARD)), and isCachingEnabled lives beside the profiles it gates rather than in the Next config. The module sits under lib/cache rather than lib/bc-api-client because it describes the app's caching policy, not that client — one of the boundaries selecting a profile is fetchStoreExtensionStatus, which reads the credentials store and never calls the API.

Renames CACHE_COMPONENTS_ENABLED to CACHE_ENABLED. The old name described the cacheComponents flag it used to work around by overriding profiles. It now simply switches caching on and off, and the Cloudflare branch has no such flag for the name to refer to. CHANGELOG.md keeps the old name, since that entry records a released version.

Adds a real profile interface and name constants. CacheLifetimeProfile types the profile constants and cacheProfile's return value. All three fields are required, unlike Next's own CacheLife where each is optional: every profile here sets all three, and leaving one implicit would silently inherit Next's default rather than this app's intent. CACHE_PROFILE_STANDARD / CACHE_PROFILE_EXTENDED let call sites name a profile by constant instead of repeating a bare string; PROFILES is keyed by those constants and checked with satisfies, so CacheProfile still derives from the map and an unknown name remains a type error.

Every "use cache: remote" directive, every cacheTag / updateTag call, and all four duration values are unchanged.

Verification

  • tsc --noEmit — clean

  • eslint src/ — 0 errors (11 pre-existing warnings in postgres-driver-loader.unavailable.ts, unrelated)

  • next build — succeeds; all 12 PPR routes still build as , confirming Cache Components is untouched

  • Type enforcement checked with a throwaway type-test: an unknown profile name and an incomplete profile object are both rejected

  • Behavioral equivalence against unmodified main, measured side by side in a separate worktree:

    main (baseline) this branch
    CACHE_ENABLED=true: /gift-certs req 1 / repeat / /customers 3 / +0 / +2 3 / +0 / +2
    CACHE_ENABLED=false: same 3 / +3 / +2 3 / +3 / +2
    PPR routes 12 12

    Identical in every cell. (Counts are upstream BigCommerce API requests. Note the app's own request logger runs inside Next's patched fetch, so it also logs cache-served responses — hit/miss flags and sub-millisecond durations are the reliable signal, not raw log counts.)

Follow-up

  • Per AGENTS.md, these changes still need replicating onto the progressive history via bcedu-lab-sync + validate-sync. Not included here.
  • The fetch-level caching implementation this is converging toward lives on feature/gift-certs-manager-cloudflare-no-remote-cache, which is not part of this PR.

Groundwork for Cloudflare compatibility. The Cloudflare target can't run
Cache Components (its staged-render path corrupts streamed HTML on
workerd), so that build caches at the fetch level instead. To keep the two
implementations as close as possible, the parts that don't depend on
*where* caching attaches now live in the same module, with the same name
and shape, in both.

lib/bc-api-client/cache-profiles.ts holds the lifetime profiles and the
enable switch. cacheLife accepts an inline { stale, revalidate, expire }
object as well as a named profile, so the profiles no longer need a
cacheLife block in next.config.ts — call sites select one with
cacheLife(cacheProfile("standard")).

Also renames CACHE_COMPONENTS_ENABLED to CACHE_ENABLED, matching the
Cloudflare branch, where there is no cacheComponents flag for the name to
refer to. CHANGELOG keeps the old name, since that entry records a
released version.

Caching behavior is unchanged. Verified against unmodified main: same
upstream request counts with caching on (a repeat page load adds none) and
off (it re-fetches all three), and the same 12 routes still build as PPR.
CacheLifetimeProfile types both the profile constants and cacheProfile's
return value. It requires all three fields, unlike Next's own CacheLife
where each is optional: every profile here sets all of them, and leaving
one implicit would silently inherit Next's default rather than this app's
intent.

CACHE_PROFILE_STANDARD and CACHE_PROFILE_EXTENDED let call sites select a
profile by constant instead of repeating a bare string. PROFILES is keyed
by those constants and checked with `satisfies`, so CacheProfile still
derives from the map and an unknown name remains a type error.

No behavior change: same durations, same enable switch, same 12 PPR
routes, same upstream request counts with caching on and off.
The profiles and the enable switch aren't specific to the BigCommerce API
client. On this branch the `use cache` boundaries that select a profile
include component render boundaries and fetchStoreExtensionStatus, which
reads the credentials store rather than calling the API at all, so
lib/bc-api-client was the wrong home for it.

Import paths and the prose references in next.config.ts, .env.example, and
docs/ARCHITECTURE.md updated to match. No behavior change.
@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
starter-sc-app-next Ready Ready Preview Aug 27, 2026 8:22pm

Request Review

@CNanninga
CNanninga merged commit 0ec73a1 into main Aug 27, 2026
1 of 2 checks passed
@CNanninga
CNanninga deleted the DEVEX-1485-cloudflare-compatibility branch August 27, 2026 20:22
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