[PR #979] [MERGED] fix: unstable_io() returns hanging promise during prerendering #1000

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

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/979
Author: @Divkix
Created: 4/30/2026
Status: Merged
Merged: 5/1/2026
Merged by: @james-elicx

Base: mainHead: fix/972-unstable-io-prerender-hang


📝 Commits (7)

  • ea50693 fix: unstable_io() returns hanging promise during prerendering (#972)
  • f415e57 fix: add knip entries for prerender-work-unit and work-unit types (#972)
  • 035a54f fix(prerender): address PR review feedback for unstable_io work unit store
  • 7963603 fix(app-rsc-entry): place route option in correct location
  • 0617b21 fix: address prerender hanging promise review feedback
  • 01b5c18 fix: resolve merge conflicts with main for prerender work unit
  • feab64b fix: resolve merge conflicts with main for test files

📊 Changes

9 files changed (+1239 additions, -2941 deletions)

View changed files

📝 knip.ts (+6 -0)
📝 packages/vinext/src/entries/app-rsc-entry.ts (+98 -399)
packages/vinext/src/server/prerender-work-unit-setup.ts (+34 -0)
📝 packages/vinext/src/shims/cache.ts (+48 -5)
packages/vinext/src/shims/internal/make-hanging-promise.ts (+64 -0)
📝 packages/vinext/src/shims/internal/work-unit-async-storage.ts (+46 -4)
📝 tests/__snapshots__/entry-templates.test.ts.snap (+504 -2388)
📝 tests/app-router.test.ts (+317 -145)
📝 tests/shims.test.ts (+122 -0)

📄 Description

Fixes #972

Summary

  • unstable_io() now branches on work unit store type, matching Next.js implementation in packages/next/src/server/request/io.ts
  • During prerendering (prerender/prerender-client/prerender-runtime), returns a hanging promise that suspends React past the IO boundary
  • During requests, cache scopes, and other contexts, resolves immediately (existing behavior)
  • When no work unit store is present, falls back to resolved promise (client/standalone)

What changed

  • New: make-hanging-promise.ts — promise that never resolves, rejects when AbortSignal fires (ported from Next.js dynamic-rendering-utils.ts)
  • Modified: work-unit-async-storage.ts — typed WorkUnitStore discriminated union with store types
  • Modified: cache.ts — unstable_io() checks work unit store type and delegates to makeHangingPromise during prerendering
  • New: prerender-work-unit-setup.ts — helper that sets up prerender store in workUnitAsyncStorage when VINEXT_PRERENDER=1
  • Modified: app-rsc-entry.ts — wraps request handler in prerender work unit scope
  • Updated: entry template snapshots to reflect generated code changes

Test plan

  • Added 5 new tests in tests/shims.test.ts covering: hanging promise during prerender, resolved promise with request/cache stores, rejection on abort, rejection on pre-aborted signal
  • All 835 existing shim tests pass
  • Entry template snapshot tests updated

🔄 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/979 **Author:** [@Divkix](https://github.com/Divkix) **Created:** 4/30/2026 **Status:** ✅ Merged **Merged:** 5/1/2026 **Merged by:** [@james-elicx](https://github.com/james-elicx) **Base:** `main` ← **Head:** `fix/972-unstable-io-prerender-hang` --- ### 📝 Commits (7) - [`ea50693`](https://github.com/cloudflare/vinext/commit/ea5069383ba205488222a718980f62217e10f7eb) fix: unstable_io() returns hanging promise during prerendering (#972) - [`f415e57`](https://github.com/cloudflare/vinext/commit/f415e57feb32f3d7bd828a85ab2d6738a8999456) fix: add knip entries for prerender-work-unit and work-unit types (#972) - [`035a54f`](https://github.com/cloudflare/vinext/commit/035a54fedf7946f08e24fd308ad9a3e788c2da5e) fix(prerender): address PR review feedback for unstable_io work unit store - [`7963603`](https://github.com/cloudflare/vinext/commit/79636031f889fc0a6ae23cfd326e2353b04837b6) fix(app-rsc-entry): place route option in correct location - [`0617b21`](https://github.com/cloudflare/vinext/commit/0617b21c1f923acf3bc666f67aaaddd2283f7abb) fix: address prerender hanging promise review feedback - [`01b5c18`](https://github.com/cloudflare/vinext/commit/01b5c183cb981c7aa2af09a44972e061b52aa176) fix: resolve merge conflicts with main for prerender work unit - [`feab64b`](https://github.com/cloudflare/vinext/commit/feab64b29a4dcba3205151c3764142f89be7a083) fix: resolve merge conflicts with main for test files ### 📊 Changes **9 files changed** (+1239 additions, -2941 deletions) <details> <summary>View changed files</summary> 📝 `knip.ts` (+6 -0) 📝 `packages/vinext/src/entries/app-rsc-entry.ts` (+98 -399) ➕ `packages/vinext/src/server/prerender-work-unit-setup.ts` (+34 -0) 📝 `packages/vinext/src/shims/cache.ts` (+48 -5) ➕ `packages/vinext/src/shims/internal/make-hanging-promise.ts` (+64 -0) 📝 `packages/vinext/src/shims/internal/work-unit-async-storage.ts` (+46 -4) 📝 `tests/__snapshots__/entry-templates.test.ts.snap` (+504 -2388) 📝 `tests/app-router.test.ts` (+317 -145) 📝 `tests/shims.test.ts` (+122 -0) </details> ### 📄 Description Fixes #972 ## Summary - unstable_io() now branches on work unit store type, matching Next.js implementation in packages/next/src/server/request/io.ts - During prerendering (prerender/prerender-client/prerender-runtime), returns a hanging promise that suspends React past the IO boundary - During requests, cache scopes, and other contexts, resolves immediately (existing behavior) - When no work unit store is present, falls back to resolved promise (client/standalone) ## What changed - **New**: make-hanging-promise.ts — promise that never resolves, rejects when AbortSignal fires (ported from Next.js dynamic-rendering-utils.ts) - **Modified**: work-unit-async-storage.ts — typed WorkUnitStore discriminated union with store types - **Modified**: cache.ts — unstable_io() checks work unit store type and delegates to makeHangingPromise during prerendering - **New**: prerender-work-unit-setup.ts — helper that sets up prerender store in workUnitAsyncStorage when VINEXT_PRERENDER=1 - **Modified**: app-rsc-entry.ts — wraps request handler in prerender work unit scope - **Updated**: entry template snapshots to reflect generated code changes ## Test plan - Added 5 new tests in tests/shims.test.ts covering: hanging promise during prerender, resolved promise with request/cache stores, rejection on abort, rejection on pre-aborted signal - All 835 existing shim tests pass - Entry template snapshot tests updated --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:11:29 +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#1000
No description provided.