Skip to content

fix(elysia): polyfill enterWith for Cloudflare Workers#395

Open
HugoRCD wants to merge 6 commits into
mainfrom
fix/elysia-workers-asynclocalstorage
Open

fix(elysia): polyfill enterWith for Cloudflare Workers#395
HugoRCD wants to merge 6 commits into
mainfrom
fix/elysia-workers-asynclocalstorage

Conversation

@HugoRCD

@HugoRCD HugoRCD commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #394

Test plan

  • pnpm run lint
  • pnpm run typecheck
  • pnpm run test
  • wrangler dev with Elysia + evlog() no longer throws enterWith() is not implemented

Summary by CodeRabbit

  • Bug Fixes
    • Improved request-scoped logging compatibility in Cloudflare Workers so logger context remains available during async execution (including wrangler dev-style flows).
    • Fixed logger binding/cleanup to avoid cross-request context leakage during concurrent traffic.
  • Documentation
    • Updated useLogger() guidance to clarify availability across async boundaries.
  • Tests
    • Added coverage for Workers-style AsyncLocalStorage behavior and concurrency isolation between interleaved requests.

Cloudflare Workers omit native AsyncLocalStorage.enterWith(), which the Elysia integration relies on across lifecycle hooks.
@vercel

vercel Bot commented Jun 23, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
evlog-docs Ready Ready Preview, Comment, Open in v0 Jun 23, 2026 8:17pm
just-use-evlog Ready Ready Preview, Comment Jun 23, 2026 8:17pm

@github-actions

Copy link
Copy Markdown
Contributor

Thank you for following the naming conventions! 🙏

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@HugoRCD, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 17 minutes and 12 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5a6156ae-9250-42a9-a5c0-8ebb926e895b

📥 Commits

Reviewing files that changed from the base of the PR and between efb8eed and 8ce0538.

📒 Files selected for processing (2)
  • packages/evlog/src/shared/asyncStorageScope.ts
  • packages/evlog/test/shared/asyncStorageScope.test.ts
📝 Walkthrough

Walkthrough

Adds a patchAsyncLocalStorageEnterWith() polyfill that conditionally patches AsyncLocalStorage instances with a enterWith() emulation for runtimes (Cloudflare Workers) lacking native support. The Elysia plugin installs the polyfill at module initialization and guards the enterWith call to non-skipped requests. Tests cover polyfill behavior, store persistence across microtasks, request-scoped logger propagation, and concurrent request isolation.

Changes

AsyncLocalStorage enterWith polyfill and Elysia integration

Layer / File(s) Summary
AsyncLocalStorage.enterWith() polyfill implementation
packages/evlog/src/shared/asyncStorageScope.ts
Exports helper functions for async-local storage operations: supportsAsyncLocalStorageEnterWith() detects native enterWith support, bindAsyncLocalStorage() and clearAsyncLocalStorage() bind/clear values via enterWith. patchAsyncLocalStorageEnterWith() conditionally patches a single ALS instance—returns early if enterWith already exists, otherwise installs an enterWith that stores values in a fallbackStore property, wraps run() to maintain a runDepth counter via try/finally, and overrides getStore() to return the native store inside run() frames or the fallbackStore when no run() frame is active.
Elysia plugin: polyfill installation and request-scoped binding
packages/evlog/src/elysia/index.ts
Imports async-local storage helpers and patches the module's AsyncLocalStorage instance at initialization. onRequest conditionally calls bindAsyncLocalStorage(storage, logger) only when the request is not marked skipped. onAfterResponse and onError use clearAsyncLocalStorage(storage) for cleanup. Updates useLogger() JSDoc to document logger availability across async boundaries via enterWith() (including the Cloudflare Workers polyfill) and removes prior notes about activeLoggers persistence beyond the request lifecycle.
Unit and integration tests for polyfill behavior and request-scope semantics
packages/evlog/test/shared/asyncStorageScope.test.ts
Sets up a createWorkersLikeStorage() helper that simulates Cloudflare Workers by removing native enterWith and applying the patch. Test cases verify native enterWith support detection, patching is a no-op when enterWith already exists, patching only affects the provided storage instance, patched enterWith persists across resolved microtasks and correctly clears with undefined, Elysia-style request-scoped logger propagation works including useLogger() error handling after clearing, and concurrent interleaved requests maintain isolated derived context and drained events.
Release documentation
.changeset/fix-elysia-workers-asynclocalstorage.md
Adds a Changeset file documenting the Cloudflare Workers fix as a patch release for evlog, describing the load-time AsyncLocalStorage.enterWith() polyfill installation to support useLogger()/logging in wrangler dev flows and referencing issue #394.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main fix: adding a polyfill for enterWith in the Elysia integration to support Cloudflare Workers.
Description check ✅ Passed The PR description includes a clear summary, mentions the linked issue (#394), explains the test plan with completed items, and follows the expected format with summary and test sections.
Linked Issues check ✅ Passed The PR addresses all coding requirements from issue #394: it implements an AsyncLocalStorage polyfill for enterWith, updates the Elysia integration to use it, and includes unit and integration tests for the reported bug.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing issue #394: polyfill implementation, Elysia integration updates, and comprehensive test coverage with no unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/elysia-workers-asynclocalstorage

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

HugoRCD added 2 commits June 23, 2026 20:38
The Workers regression test mutated AsyncLocalStorage.prototype and flaked under CI sharding; cover the pattern via a subclass-based unit test instead.
Use object-typed WeakMap keys and explicit run callback signatures so the polyfill passes strict TypeScript and eslint checks.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/evlog/src/elysia/index.ts`:
- Around line 116-118: The `storage.enterWith(logger)` call in the Elysia
request handler and corresponding `storage.enterWith(undefined)` cleanup in
`onAfterResponse` use a module-level singleton storage implementation that does
not properly isolate logger context for concurrent requests on Cloudflare
Workers. The polyfill stores context in a process-global WeakMap instead of
binding to async execution context, causing interleaved requests to leak and
contaminate each other's logger state. Replace the `enterWith()` pattern with
`storage.run()` to wrap the relevant request handling logic, since the polyfill
properly delegates `run()` to the native implementation which Workers supports.
Additionally, add a concurrency regression test that simulates multiple
simultaneous interleaved requests to verify logger context isolation and prevent
this contamination issue from reoccurring.

In `@packages/evlog/src/shared/asyncStorageScope.ts`:
- Around line 11-42: The fallbackStores WeakMap in
installAsyncLocalStorageEnterWithPolyfill() is keyed only by the ALS instance,
causing all concurrent async contexts to share the same store value instead of
isolating per execution context as native enterWith() does. This results in
cross-request context bleed when multiple requests call enterWith() without
run() wrappers. Fix this by either modifying fallbackStores to be keyed by
execution context (e.g., using a Map that tracks execution state alongside the
ALS instance in enterWith() and getStore()), or by wrapping all Elysia
integration calls to storage.enterWith() in run() frames per request.
Additionally, correct the JSDoc comment on lines 11-12 to accurately reflect
that this polyfill does not provide the same concurrency semantics as native
enterWith() and only guarantees isolation within a single run() invocation.

In `@packages/evlog/test/frameworks/elysia.test.ts`:
- Around line 394-419: The test captures and restores only the `enterWith`
method from AsyncLocalStorage.prototype, but the
`installAsyncLocalStorageEnterWithPolyfill()` function patches three methods:
`enterWith`, `getStore`, and `run`. To prevent state leakage between tests,
capture all three methods at the beginning (before calling
installAsyncLocalStorageEnterWithPolyfill) by destructuring `getStore` and `run`
alongside `enterWith` from AsyncLocalStorage.prototype, then restore all three
methods in the finally block using Object.defineProperty calls for each method,
following the pattern demonstrated in asyncStorageScope.test.ts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0c933f74-e654-4a34-9897-18776f20fa0f

📥 Commits

Reviewing files that changed from the base of the PR and between 32ea435 and d49c16a.

📒 Files selected for processing (5)
  • .changeset/fix-elysia-workers-asynclocalstorage.md
  • packages/evlog/src/elysia/index.ts
  • packages/evlog/src/shared/asyncStorageScope.ts
  • packages/evlog/test/frameworks/elysia.test.ts
  • packages/evlog/test/shared/asyncStorageScope.test.ts

Comment thread packages/evlog/src/elysia/index.ts
Comment thread packages/evlog/src/shared/asyncStorageScope.ts Outdated
Comment thread packages/evlog/test/frameworks/elysia.test.ts Outdated
@pkg-pr-new

pkg-pr-new Bot commented Jun 23, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/evlog@395
npm i https://pkg.pr.new/@evlog/nuxthub@395

commit: 8ce0538

Use native AsyncLocalStorage helpers, document Workers concurrency limits honestly, and add a derive-based concurrency regression test.
@HugoRCD

HugoRCD commented Jun 23, 2026

Copy link
Copy Markdown
Owner Author

@zAlweNy26 Can you try the pkg.pr.new to see if this fixes your issue

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/evlog/src/shared/asyncStorageScope.ts`:
- Around line 107-109: The function installAsyncLocalStorageEnterWithPolyfill is
patching AsyncLocalStorage.prototype globally, which affects all
AsyncLocalStorage instances in the isolate and can break other dependencies.
Instead of calling this function to patch the global prototype, locate the
module-level storage instance used by evlog and patch that specific instance
directly using patchAsyncLocalStorageEnterWith. Remove the global prototype
patching approach and apply the patch only to the evlog storage object (likely
created in asyncStorageScope.ts or passed from elysia/index.ts) right after it
is instantiated. This way, only evlog's storage instance gets the custom
enterWith behavior without affecting other ALS instances.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3d0d6730-307a-4a16-b3cf-9ebcccbbbc31

📥 Commits

Reviewing files that changed from the base of the PR and between 700415c and bfc29f1.

📒 Files selected for processing (4)
  • .changeset/fix-elysia-workers-asynclocalstorage.md
  • packages/evlog/src/elysia/index.ts
  • packages/evlog/src/shared/asyncStorageScope.ts
  • packages/evlog/test/shared/asyncStorageScope.test.ts

Comment thread packages/evlog/src/shared/asyncStorageScope.ts Outdated
Scope the enterWith polyfill to evlog's storage object instead of mutating AsyncLocalStorage.prototype globally.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/evlog/test/shared/asyncStorageScope.test.ts (1)

16-24: 🎯 Functional Correctness | 🔵 Trivial

Add a test case for a Workers-style throwing enterWith shim to improve defensive coverage.

The current test helper only models an absent enterWith (set to undefined). The polyfill's check on line 10 of the source (typeof storage.enterWith === 'function') means that if enterWith exists as a function—even one that throws—the patch will skip early and not provide a fallback. Adding a test for the throwing case ensures the patch handles that scenario defensively, matching potential edge cases in runtime implementations.

Regression test shape
+  it('patches a Workers-style throwing enterWith shim', () => {
+    class LocalAsyncLocalStorage extends AsyncLocalStorage<string> {}
+    Object.defineProperty(LocalAsyncLocalStorage.prototype, 'enterWith', {
+      configurable: true,
+      writable: true,
+      value() {
+        throw new Error('asyncLocalStorage.enterWith() is not implemented')
+      },
+    })
+
+    const storage = new LocalAsyncLocalStorage()
+    patchAsyncLocalStorageEnterWith(storage)
+
+    expect(() => storage.enterWith('evlog')).not.toThrow()
+    expect(storage.getStore()).toBe('evlog')
+  })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/evlog/test/shared/asyncStorageScope.test.ts` around lines 16 - 24,
The current test helper createWorkersLikeStorage only models an absent enterWith
(set to undefined), but the polyfill will skip patching if enterWith exists as
any function (per the typeof check). Add a new test case that creates an
AsyncLocalStorage where enterWith is defined as a function that throws an error
instead of being undefined, then verify that the patchAsyncLocalStorageEnterWith
function handles this throwing scenario defensively without crashing. This
ensures coverage of the edge case where enterWith exists but fails at runtime.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/evlog/src/shared/asyncStorageScope.ts`:
- Line 46: The early-return check for
supportsAsyncLocalStorageEnterWith(storage) at line 46 does not account for
environments like Cloudflare Workers where enterWith may be a callable that
throws an error rather than being undefined. Wrap the early-return condition in
a try-catch block: if supportsAsyncLocalStorageEnterWith(storage) executes
successfully, return early as before; if it throws an exception, catch the error
and skip the return so the code falls through to the polyfill fallback instead.
This ensures that mock or non-functional implementations of enterWith will not
bypass the polyfill and cause bindAsyncLocalStorage() to fail.

---

Outside diff comments:
In `@packages/evlog/test/shared/asyncStorageScope.test.ts`:
- Around line 16-24: The current test helper createWorkersLikeStorage only
models an absent enterWith (set to undefined), but the polyfill will skip
patching if enterWith exists as any function (per the typeof check). Add a new
test case that creates an AsyncLocalStorage where enterWith is defined as a
function that throws an error instead of being undefined, then verify that the
patchAsyncLocalStorageEnterWith function handles this throwing scenario
defensively without crashing. This ensures coverage of the edge case where
enterWith exists but fails at runtime.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7d4b1078-8208-4449-a1a8-70dc351cb342

📥 Commits

Reviewing files that changed from the base of the PR and between bfc29f1 and efb8eed.

📒 Files selected for processing (3)
  • packages/evlog/src/elysia/index.ts
  • packages/evlog/src/shared/asyncStorageScope.ts
  • packages/evlog/test/shared/asyncStorageScope.test.ts

Comment thread packages/evlog/src/shared/asyncStorageScope.ts
Cloudflare Workers expose enterWith but throw when called; detect that
at probe time so the polyfill still applies on those runtimes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] Elysia + Cloudflare worker error with AsyncLocalStorage

1 participant