[PR #670] [CLOSED] feat: populate Workers KV with pre-rendered pages at deploy time #759

Closed
opened 2026-05-06 13:09:58 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/670
Author: @NathanDrake2406
Created: 3/23/2026
Status: Closed

Base: mainHead: feat/populate-kv-deploy


📝 Commits (7)

  • 7c2f8c8 feat: add deploy-time KV population module
  • 6cce829 feat: integrate KV population into deploy pipeline
  • 833a935 refactor: TPR uses shared populate-kv helpers for KV upload
  • 754a7f9 refactor: remove as casts and clean up test imports
  • 68f3762 fix: filter to App Router routes and remove --app-prefix flag
  • e8fb96d refactor: hoist invariant URL and headers out of upload loop
  • 92d84d3 ci: retrigger

📊 Changes

4 files changed (+1049 additions, -64 deletions)

View changed files

packages/vinext/src/cloudflare/populate-kv.ts (+330 -0)
📝 packages/vinext/src/cloudflare/tpr.ts (+25 -63)
📝 packages/vinext/src/deploy.ts (+34 -1)
tests/populate-kv.test.ts (+660 -0)

📄 Description

Summary

Closes #562. Depends on #653 — requires buildId, trailingSlash, and router fields in the prerender manifest. Should be merged after #653.

  • New module cloudflare/populate-kv.ts — reads prerender manifest + HTML/RSC files, constructs cache entries matching the runtime format exactly, uploads via Cloudflare REST bulk API
  • Deploy integration — automatic step 6d between prerender asset copy and wrangler deploy when CLOUDFLARE_API_TOKEN + VINEXT_CACHE KV namespace + prerender manifest all exist. --no-populate-kv to opt out.
  • TPR refactor — replaces TPR's inline uploadToKV (80 lines) with shared helpers, fixing three bugs: wrong key format (cache:<path>cache:app:<buildId>:<path>:html), wrong TTL (10x revalidate → fixed 30-day), missing tags (empty [] → full hierarchy for revalidatePath())

Key format parity

Deploy-time keys match the runtime __isrCacheKey in app-rsc-entry.ts exactly:

[appPrefix:]cache:app:<buildId>:<pathname>:<suffix>

Two entries per route (:html + :rsc) with full tag hierarchy (_N_T_/layout, intermediate layouts, leaf page tag) so revalidatePath() works on seeded entries.

Design decisions

  • App Router only — Pages Router uses different keys (pages:...) and value shape (kind: "PAGES"). Routes without router: "app" are skipped.
  • Batching — by count (10,000 max) and bytes (95 MB with 5 MB headroom from 100 MB API limit)
  • Fixed 30-day KV TTL — matches KVCacheHandler.set() default
  • No --app-prefix flag — removed after review; it would write prefixed keys without wiring the prefix into the generated runtime, creating silently unreadable entries. appPrefix remains on the programmatic PopulateKVOptions for advanced users who configure both sides.

Test plan

  • 40 unit tests covering key parity, tag parity, entry serialization, bulk batching, end-to-end flow, router filtering, graceful skips
  • vp check passes (0 warnings, 0 errors)
  • Manual: CLOUDFLARE_API_TOKEN=... vinext deploy --prerender-all on a test project with ISR routes
  • Verify KV entries via wrangler kv key list --binding VINEXT_CACHE
  • First request shows X-Vinext-Cache: HIT

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/cloudflare/vinext/pull/670 **Author:** [@NathanDrake2406](https://github.com/NathanDrake2406) **Created:** 3/23/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `feat/populate-kv-deploy` --- ### 📝 Commits (7) - [`7c2f8c8`](https://github.com/cloudflare/vinext/commit/7c2f8c825071ad7f1a92c1322ce49810b8540e06) feat: add deploy-time KV population module - [`6cce829`](https://github.com/cloudflare/vinext/commit/6cce829ce8044965492752c443ae68fe9488fbcf) feat: integrate KV population into deploy pipeline - [`833a935`](https://github.com/cloudflare/vinext/commit/833a9357c8d8fb836e22271477d6eea607f38481) refactor: TPR uses shared populate-kv helpers for KV upload - [`754a7f9`](https://github.com/cloudflare/vinext/commit/754a7f990731681e6272a2bbaf9c9f70d607d24a) refactor: remove as casts and clean up test imports - [`68f3762`](https://github.com/cloudflare/vinext/commit/68f3762e52d5857f3875977243707b65a9484528) fix: filter to App Router routes and remove --app-prefix flag - [`e8fb96d`](https://github.com/cloudflare/vinext/commit/e8fb96d8bb82c8d164b2aa034ae0e638b5a09c03) refactor: hoist invariant URL and headers out of upload loop - [`92d84d3`](https://github.com/cloudflare/vinext/commit/92d84d3756fc9d3d6aedd049a45b39488b1ff3b7) ci: retrigger ### 📊 Changes **4 files changed** (+1049 additions, -64 deletions) <details> <summary>View changed files</summary> ➕ `packages/vinext/src/cloudflare/populate-kv.ts` (+330 -0) 📝 `packages/vinext/src/cloudflare/tpr.ts` (+25 -63) 📝 `packages/vinext/src/deploy.ts` (+34 -1) ➕ `tests/populate-kv.test.ts` (+660 -0) </details> ### 📄 Description ## Summary Closes #562. **Depends on #653** — requires `buildId`, `trailingSlash`, and `router` fields in the prerender manifest. Should be merged after #653. - **New module** `cloudflare/populate-kv.ts` — reads prerender manifest + HTML/RSC files, constructs cache entries matching the runtime format exactly, uploads via Cloudflare REST bulk API - **Deploy integration** — automatic step 6d between prerender asset copy and wrangler deploy when `CLOUDFLARE_API_TOKEN` + `VINEXT_CACHE` KV namespace + prerender manifest all exist. `--no-populate-kv` to opt out. - **TPR refactor** — replaces TPR's inline `uploadToKV` (80 lines) with shared helpers, fixing three bugs: wrong key format (`cache:<path>` → `cache:app:<buildId>:<path>:html`), wrong TTL (10x revalidate → fixed 30-day), missing tags (empty `[]` → full hierarchy for `revalidatePath()`) ### Key format parity Deploy-time keys match the runtime `__isrCacheKey` in `app-rsc-entry.ts` exactly: ``` [appPrefix:]cache:app:<buildId>:<pathname>:<suffix> ``` Two entries per route (`:html` + `:rsc`) with full tag hierarchy (`_N_T_/layout`, intermediate layouts, leaf page tag) so `revalidatePath()` works on seeded entries. ### Design decisions - **App Router only** — Pages Router uses different keys (`pages:...`) and value shape (`kind: "PAGES"`). Routes without `router: "app"` are skipped. - **Batching** — by count (10,000 max) and bytes (95 MB with 5 MB headroom from 100 MB API limit) - **Fixed 30-day KV TTL** — matches `KVCacheHandler.set()` default - **No `--app-prefix` flag** — removed after review; it would write prefixed keys without wiring the prefix into the generated runtime, creating silently unreadable entries. `appPrefix` remains on the programmatic `PopulateKVOptions` for advanced users who configure both sides. ## Test plan - [x] 40 unit tests covering key parity, tag parity, entry serialization, bulk batching, end-to-end flow, router filtering, graceful skips - [x] `vp check` passes (0 warnings, 0 errors) - [ ] Manual: `CLOUDFLARE_API_TOKEN=... vinext deploy --prerender-all` on a test project with ISR routes - [ ] Verify KV entries via `wrangler kv key list --binding VINEXT_CACHE` - [ ] First request shows `X-Vinext-Cache: HIT` --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:09:58 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#759
No description provided.