[PR #410] [MERGED] feat: AsyncLocalStorage for ExecutionContext (ctx) propagation #553

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

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/410
Author: @james-elicx
Created: 3/10/2026
Status: Merged
Merged: 3/10/2026
Merged by: @james-elicx

Base: mainHead: feat/execution-context-als


📝 Commits (1)

  • 7a3d9a3 feat: AsyncLocalStorage for ExecutionContext (ctx) propagation

📊 Changes

8 files changed (+276 additions, -28 deletions)

View changed files

📝 packages/vinext/src/cloudflare/kv-cache-handler.ts (+23 -13)
📝 packages/vinext/src/deploy.ts (+10 -3)
📝 packages/vinext/src/entries/pages-server-entry.ts (+19 -3)
📝 packages/vinext/src/server/app-router-entry.ts (+8 -4)
packages/vinext/src/shims/request-context.ts (+81 -0)
📝 tests/__snapshots__/entry-templates.test.ts.snap (+16 -3)
📝 tests/deploy.test.ts (+8 -2)
tests/request-context.test.ts (+111 -0)

📄 Description

Summary

Extracts ExecutionContext (ctx) propagation from #405 into a standalone feature. Adds an AsyncLocalStorage-based mechanism so Cloudflare Workers' ctx (used for waitUntil) is accessible anywhere in the call stack without manual threading.

What changed

  • shims/request-context.ts — New ALS module keyed on Symbol.for("vinext.requestContext.als"), following the same pattern as headers.ts, cache.ts, and navigation-state.ts. Exports runWithExecutionContext(ctx, fn) and getRequestExecutionContext().
  • server/app-router-entry.tsfetch(request, _env?, ctx?) now wraps the RSC handler in runWithExecutionContext(ctx, ...) when ctx is present.
  • entries/app-rsc-entry.ts — Generated RSC entry imports runWithExecutionContext and getRequestExecutionContext from the shim for downstream use.
  • deploy.ts — Generated Cloudflare worker entry updated to fetch(request, env, ctx) and forwards ctx to handler.fetch(request, env, ctx).
  • cloudflare/kv-cache-handler.ts — Uses getRequestExecutionContext() ?? this.ctx so the ALS-provided ctx takes priority over the constructor-injected one.

Design

getRequestExecutionContext() returns null when called outside a request scope (e.g. Node.js dev server), making it safe to call anywhere. The ALS scope is established by app-router-entry.ts before any RSC/SSR/routing logic runs, so it's available throughout the entire request lifecycle.

Tests

  • 8 new tests in tests/request-context.test.ts covering: null outside scope, correct value inside scope, async propagation, concurrent request isolation, nested scope override/restore, and waitUntil callable from inside scope.
  • Updated tests/deploy.test.ts assertions for the new worker entry signature.
  • Regenerated 6 snapshots in tests/__snapshots__/entry-templates.test.ts.snap.

Extracted from #405 (ISR caching) — the ctx plumbing is a prerequisite for waitUntil-based background cache writes but is generally useful on its own.


🔄 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/410 **Author:** [@james-elicx](https://github.com/james-elicx) **Created:** 3/10/2026 **Status:** ✅ Merged **Merged:** 3/10/2026 **Merged by:** [@james-elicx](https://github.com/james-elicx) **Base:** `main` ← **Head:** `feat/execution-context-als` --- ### 📝 Commits (1) - [`7a3d9a3`](https://github.com/cloudflare/vinext/commit/7a3d9a355a905b45d22791223a946f0e1405c515) feat: AsyncLocalStorage for ExecutionContext (ctx) propagation ### 📊 Changes **8 files changed** (+276 additions, -28 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/cloudflare/kv-cache-handler.ts` (+23 -13) 📝 `packages/vinext/src/deploy.ts` (+10 -3) 📝 `packages/vinext/src/entries/pages-server-entry.ts` (+19 -3) 📝 `packages/vinext/src/server/app-router-entry.ts` (+8 -4) ➕ `packages/vinext/src/shims/request-context.ts` (+81 -0) 📝 `tests/__snapshots__/entry-templates.test.ts.snap` (+16 -3) 📝 `tests/deploy.test.ts` (+8 -2) ➕ `tests/request-context.test.ts` (+111 -0) </details> ### 📄 Description ## Summary Extracts `ExecutionContext` (`ctx`) propagation from #405 into a standalone feature. Adds an `AsyncLocalStorage`-based mechanism so Cloudflare Workers' `ctx` (used for `waitUntil`) is accessible anywhere in the call stack without manual threading. ## What changed - **`shims/request-context.ts`** — New ALS module keyed on `Symbol.for("vinext.requestContext.als")`, following the same pattern as `headers.ts`, `cache.ts`, and `navigation-state.ts`. Exports `runWithExecutionContext(ctx, fn)` and `getRequestExecutionContext()`. - **`server/app-router-entry.ts`** — `fetch(request, _env?, ctx?)` now wraps the RSC handler in `runWithExecutionContext(ctx, ...)` when `ctx` is present. - **`entries/app-rsc-entry.ts`** — Generated RSC entry imports `runWithExecutionContext` and `getRequestExecutionContext` from the shim for downstream use. - **`deploy.ts`** — Generated Cloudflare worker entry updated to `fetch(request, env, ctx)` and forwards `ctx` to `handler.fetch(request, env, ctx)`. - **`cloudflare/kv-cache-handler.ts`** — Uses `getRequestExecutionContext() ?? this.ctx` so the ALS-provided ctx takes priority over the constructor-injected one. ## Design `getRequestExecutionContext()` returns `null` when called outside a request scope (e.g. Node.js dev server), making it safe to call anywhere. The ALS scope is established by `app-router-entry.ts` before any RSC/SSR/routing logic runs, so it's available throughout the entire request lifecycle. ## Tests - 8 new tests in `tests/request-context.test.ts` covering: null outside scope, correct value inside scope, async propagation, concurrent request isolation, nested scope override/restore, and `waitUntil` callable from inside scope. - Updated `tests/deploy.test.ts` assertions for the new worker entry signature. - Regenerated 6 snapshots in `tests/__snapshots__/entry-templates.test.ts.snap`. ## Related Extracted from #405 (ISR caching) — the ctx plumbing is a prerequisite for `waitUntil`-based background cache writes but is generally useful on its own. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:08:43 +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#553
No description provided.