Skip to content

presets(cloudflare): support durable objects, workflows and bindings in local dev#4341

Open
zsilbi wants to merge 4 commits into
nitrojs:mainfrom
zsilbi:feat/cf-dev-bindings
Open

presets(cloudflare): support durable objects, workflows and bindings in local dev#4341
zsilbi wants to merge 4 commits into
nitrojs:mainfrom
zsilbi:feat/cf-dev-bindings

Conversation

@zsilbi

@zsilbi zsilbi commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #4338 — makes durable object and workflows usable in dev (miniflare runner).

🔗 Linked issue

#4339

❓ Type of change

  • 👌 Enhancement (improving an existing functionality like performance)

📚 Description

Compose a dev worker entry that statically re-exports binding-referenced classes from exports.cloudflare.ts (bundled with rolldown) and pass them to env-runner via exports — workerd requires them as static exports, while the app keeps loading through the Vite module runner (HMR intact, DO class changes need a restart - documented).

Forward env/ctx to the app in the dev worker, same as the prod module handler (globalThis.__env__ + req.runtime.cloudflare).

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@zsilbi zsilbi requested a review from pi0 as a code owner June 11, 2026 21:38
@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

@zsilbi is attempting to deploy a commit to the Nitro Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds Cloudflare Durable Objects and Workflows support to Nitro by introducing a dev-worker composition system that bundles exports from exports.cloudflare.ts, automatically discovers bound Durable Objects and Workflows from wrangler configuration, integrates them into the miniflare dev environment runner, and augments request bindings to expose the Cloudflare environment.

Changes

Cloudflare Durable Objects Integration

Layer / File(s) Summary
Documentation and User Example
docs/2.deploy/20.providers/cloudflare.md, examples/cloudflare-durable/*
Docs clarify that exports.cloudflare.ts exports are included in builds and dev-mode Durable Objects/Workflows are exported from the local dev worker. Complete cloudflare-durable example demonstrates exporting a CounterDO class, declaring bindings in wrangler.jsonc, and accessing via event.req.runtime.cloudflare.env in a route handler with HTML UI and Vite/Nitro config.
Public API Utilities
src/presets/cloudflare/entry-exports.ts, src/presets/cloudflare/utils.ts
resolveExportsEntry and readWranglerConfig are exported to enable dev-worker composition discovery of exports entry and wrangler configuration.
Dev Worker Composition
src/presets/cloudflare/dev.ts
New composeCloudflareDevWorker function discovers bound Durable Objects and Workflows from wrangler config, bundles exports.cloudflare.ts with rolldown into cloudflare-dev/exports.mjs, and generates cloudflare-dev/worker.mjs wrapper that dynamically imports the entry via unsafe eval to forward fetch/upgrade/ipc handlers. Includes helpers for extracting bound classes, selecting env-specific config, bundling exports, and generating the wrapper source.
Miniflare Runner Integration
src/build/vite/env.ts
When preset is cloudflare-dev, the miniflare runner conditionally composes the dev worker, wires its exports and entry into MiniflareEnvRunner, and configures miniflare with durableObjects: {}.
Vite Dev Worker Binding Augmentation
src/runtime/internal/vite/dev-worker.mjs
fetch export now accepts (req, env, ctx) signature. New augmentReq helper sets globalThis.__env__, populates req.ip, initializes req.runtime.cloudflare with passed env/context, and binds req.waitUntil.
Test Fixture and Integration Tests
test/vite/cloudflare-do-fixture/, test/vite/cloudflare-do.test.ts, test/examples.test.ts
Test fixture defines CounterDO and EchoWorkflow classes, route handler reading bindings from req.runtime.cloudflare.env, and Vite/Nitro config. Integration test starts dev server, verifies DO state persistence across requests, checks globalThis.__env__ binding exposure, and validates workflow export/binding. cloudflare-durable example is skipped in dev-mode test run.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • nitrojs/nitro#3834: Implements the underlying resolveExportsEntry/setupEntryExports mechanism for virtualizing exports.cloudflare.ts, which this PR builds on by bundling and re-exporting those entries through the Cloudflare dev worker.
  • nitrojs/nitro#4064: Both PRs update examples and docs to use event.req.runtime.cloudflare.env for accessing Cloudflare bindings in Nitro v3.
  • nitrojs/nitro#4338: Modifies the same Cloudflare local-dev infrastructure (src/build/vite/env.ts and miniflare runner wiring) to support the dev-worker composition and environment-runner integration.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 follows conventional commits format with a scope (cloudflare), type (feat via 'presets'), and descriptive subject aligned with the core changeset of adding durable objects/workflows support to local dev.
Description check ✅ Passed The description is directly related to the changeset, explaining the dev worker composition, static re-exports strategy, env/ctx forwarding, and linking to the relevant issue with proper context.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedgeist@​1.7.21001001009290
Addedmotion-v@​2.2.110010010093100

View full report

@socket-security

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm @internationalized/date is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: docs/pnpm-lock.yamlnpm/@internationalized/date@3.12.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@internationalized/date@3.12.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm @tanstack/table-core is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: docs/pnpm-lock.yamlnpm/@tanstack/table-core@8.21.3

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@tanstack/table-core@8.21.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm brace-expansion is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: docs/pnpm-lock.yamlnpm/brace-expansion@5.0.4

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/brace-expansion@5.0.4. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm css-tree is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: docs/pnpm-lock.yamlnpm/css-tree@3.2.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/css-tree@3.2.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm embla-carousel is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: docs/pnpm-lock.yamlnpm/embla-carousel@8.6.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/embla-carousel@8.6.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm es-module-lexer is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: docs/pnpm-lock.yamlnpm/es-module-lexer@2.0.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/es-module-lexer@2.0.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm es-module-lexer is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: docs/pnpm-lock.yamlnpm/es-module-lexer@2.0.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/es-module-lexer@2.0.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm fuse.js is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: docs/pnpm-lock.yamlnpm/fuse.js@7.1.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/fuse.js@7.1.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm ioredis is 96.0% likely obfuscated

Confidence: 0.96

Location: Package overview

From: docs/pnpm-lock.yamlnpm/ioredis@5.10.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/ioredis@5.10.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm jiti is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: docs/pnpm-lock.yamlnpm/automd@0.4.3npm/jiti@2.6.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/jiti@2.6.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm next is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: docs/pnpm-lock.yamlnpm/geist@1.7.2npm/next@16.1.6

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/next@16.1.6. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm node-fetch-native is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: docs/pnpm-lock.yamlnpm/automd@0.4.3npm/node-fetch-native@1.6.7

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/node-fetch-native@1.6.7. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm node-forge is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: docs/pnpm-lock.yamlnpm/node-forge@1.3.3

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/node-forge@1.3.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@pkg-pr-new

pkg-pr-new Bot commented Jun 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/nitro@4341

commit: b9506c6

@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 `@examples/cloudflare-durable/routes/counter.ts`:
- Around line 4-8: The code assumes event.req.runtime?.cloudflare?.env exists
and directly casts it, which will throw a TypeError if missing; update the
handler (defineHandler) to validate that event.req.runtime?.cloudflare?.env is
defined and that env.COUNTER exists before calling
env.COUNTER.getByName("global"), and throw a clear, descriptive error (e.g.,
"Missing Cloudflare Durable Object binding: COUNTER") so callers get an
actionable message instead of a runtime TypeError; reference the env variable
and the COUNTER DurableObjectNamespace/CounterDO binding when adding the checks.

In `@src/presets/cloudflare/dev.ts`:
- Around line 149-153: IPC can call loadDevWorker() before
globalThis.__ENV_RUNNER_UNSAFE_EVAL__ is set, so guard and lazily initialize
that binding instead of dereferencing it; create a small helper (e.g.,
ensureUnsafeEval or createUnsafeEvalBinder) that if
globalThis.__ENV_RUNNER_UNSAFE_EVAL__ is undefined will initialize and assign a
safe fallback object exposing newAsyncFunction (built via the Function
constructor to return import(id) so it behaves like the real binder), then use
that helper when computing devWorkerPromise and in the other block (the similar
166-173 code) so loadDevWorker() never calls undefined.newAsyncFunction;
reference globalThis.__ENV_RUNNER_UNSAFE_EVAL__, devWorkerPromise,
loadDevWorker, newAsyncFunction, devWorkerId, and the ipc.onOpen / ipc.onMessage
pre-load paths when locating where to add the helper and replace direct
dereferences.
- Around line 152-154: The memoized devWorkerPromise (devWorkerPromise) is set
with ??= which leaves a permanently rejected promise on transient import
failures; update the promise chain returned by
newAsyncFunction(...)(devWorkerId) to attach a .catch handler that clears
devWorkerPromise (set it to undefined/null) before rethrowing the error so
subsequent attempts will retry, while keeping the successful .then branch that
sets devWorker; reference devWorkerPromise, devWorker, devWorkerId and the
newAsyncFunction(...).then(...) chain when making the change.
🪄 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: CHILL

Plan: Pro

Run ID: c8e33f58-1f23-4169-8454-e9ebef5545cd

📥 Commits

Reviewing files that changed from the base of the PR and between 7765bcb and b9506c6.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (23)
  • docs/2.deploy/20.providers/cloudflare.md
  • examples/cloudflare-durable/README.md
  • examples/cloudflare-durable/exports.cloudflare.ts
  • examples/cloudflare-durable/index.html
  • examples/cloudflare-durable/nitro.config.ts
  • examples/cloudflare-durable/package.json
  • examples/cloudflare-durable/routes/counter.ts
  • examples/cloudflare-durable/server/durable/counter.ts
  • examples/cloudflare-durable/tsconfig.json
  • examples/cloudflare-durable/vite.config.ts
  • examples/cloudflare-durable/wrangler.jsonc
  • src/build/vite/env.ts
  • src/presets/cloudflare/dev.ts
  • src/presets/cloudflare/entry-exports.ts
  • src/presets/cloudflare/utils.ts
  • src/runtime/internal/vite/dev-worker.mjs
  • test/examples.test.ts
  • test/vite/cloudflare-do-fixture/durable/counter.ts
  • test/vite/cloudflare-do-fixture/durable/entrypoint.ts
  • test/vite/cloudflare-do-fixture/exports.cloudflare.ts
  • test/vite/cloudflare-do-fixture/routes/counter.ts
  • test/vite/cloudflare-do-fixture/vite.config.ts
  • test/vite/cloudflare-do.test.ts

Comment on lines +4 to +8
export default defineHandler(async (event) => {
const env = event.req.runtime?.cloudflare?.env as {
COUNTER: DurableObjectNamespace<CounterDO>;
};
const counter = env.COUNTER.getByName("global");

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add validation for missing Cloudflare bindings.

If event.req.runtime?.cloudflare?.env is undefined, the type assertion on line 5 combined with property access on line 8 will throw a TypeError rather than a clear, actionable error. As per coding guidelines, prefer explicit errors over silent failures.

🛡️ Proposed fix to add explicit validation
 export default defineHandler(async (event) => {
+  if (!event.req.runtime?.cloudflare?.env) {
+    throw new Error("Cloudflare runtime environment not available");
+  }
   const env = event.req.runtime?.cloudflare?.env as {
     COUNTER: DurableObjectNamespace<CounterDO>;
   };
🤖 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 `@examples/cloudflare-durable/routes/counter.ts` around lines 4 - 8, The code
assumes event.req.runtime?.cloudflare?.env exists and directly casts it, which
will throw a TypeError if missing; update the handler (defineHandler) to
validate that event.req.runtime?.cloudflare?.env is defined and that env.COUNTER
exists before calling env.COUNTER.getByName("global"), and throw a clear,
descriptive error (e.g., "Missing Cloudflare Durable Object binding: COUNTER")
so callers get an actionable message instead of a runtime TypeError; reference
the env variable and the COUNTER DurableObjectNamespace/CounterDO binding when
adding the checks.

Source: Coding guidelines

Comment on lines +149 to +153
if (env && !globalThis.__ENV_RUNNER_UNSAFE_EVAL__) {
globalThis.__ENV_RUNNER_UNSAFE_EVAL__ = env.__ENV_RUNNER_UNSAFE_EVAL__;
}
return (devWorkerPromise ??= globalThis.__ENV_RUNNER_UNSAFE_EVAL__
.newAsyncFunction("return import(id)", "loadDevWorker", "id")(devWorkerId)

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

IPC can hit loadDevWorker() before the unsafe-eval binding is initialized.

fetch() seeds globalThis.__ENV_RUNNER_UNSAFE_EVAL__ from env, but ipc.onOpen() and the pre-load ipc.onMessage() path call loadDevWorker() with no env. On a fresh worker that can dereference undefined.newAsyncFunction(...) before the first request, which breaks the dev-worker/HMR bootstrap.

Also applies to: 166-173

🤖 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 `@src/presets/cloudflare/dev.ts` around lines 149 - 153, IPC can call
loadDevWorker() before globalThis.__ENV_RUNNER_UNSAFE_EVAL__ is set, so guard
and lazily initialize that binding instead of dereferencing it; create a small
helper (e.g., ensureUnsafeEval or createUnsafeEvalBinder) that if
globalThis.__ENV_RUNNER_UNSAFE_EVAL__ is undefined will initialize and assign a
safe fallback object exposing newAsyncFunction (built via the Function
constructor to return import(id) so it behaves like the real binder), then use
that helper when computing devWorkerPromise and in the other block (the similar
166-173 code) so loadDevWorker() never calls undefined.newAsyncFunction;
reference globalThis.__ENV_RUNNER_UNSAFE_EVAL__, devWorkerPromise,
loadDevWorker, newAsyncFunction, devWorkerId, and the ipc.onOpen / ipc.onMessage
pre-load paths when locating where to add the helper and replace direct
dereferences.

Comment on lines +152 to +154
return (devWorkerPromise ??= globalThis.__ENV_RUNNER_UNSAFE_EVAL__
.newAsyncFunction("return import(id)", "loadDevWorker", "id")(devWorkerId)
.then((mod) => (devWorker = mod)));

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reset the memoized import promise on failure.

With ??=, one transient transform/import failure leaves devWorkerPromise permanently rejected, so every later request keeps failing until the whole runner restarts.

Suggested fix
 function loadDevWorker(env) {
   if (env && !globalThis.__ENV_RUNNER_UNSAFE_EVAL__) {
     globalThis.__ENV_RUNNER_UNSAFE_EVAL__ = env.__ENV_RUNNER_UNSAFE_EVAL__;
   }
   return (devWorkerPromise ??= globalThis.__ENV_RUNNER_UNSAFE_EVAL__
     .newAsyncFunction("return import(id)", "loadDevWorker", "id")(devWorkerId)
-    .then((mod) => (devWorker = mod)));
+    .then((mod) => (devWorker = mod))
+    .catch((error) => {
+      devWorkerPromise = undefined;
+      throw error;
+    }));
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (devWorkerPromise ??= globalThis.__ENV_RUNNER_UNSAFE_EVAL__
.newAsyncFunction("return import(id)", "loadDevWorker", "id")(devWorkerId)
.then((mod) => (devWorker = mod)));
return (devWorkerPromise ??= globalThis.__ENV_RUNNER_UNSAFE_EVAL__
.newAsyncFunction("return import(id)", "loadDevWorker", "id")(devWorkerId)
.then((mod) => (devWorker = mod))
.catch((error) => {
devWorkerPromise = undefined;
throw error;
}));
🤖 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 `@src/presets/cloudflare/dev.ts` around lines 152 - 154, The memoized
devWorkerPromise (devWorkerPromise) is set with ??= which leaves a permanently
rejected promise on transient import failures; update the promise chain returned
by newAsyncFunction(...)(devWorkerId) to attach a .catch handler that clears
devWorkerPromise (set it to undefined/null) before rethrowing the error so
subsequent attempts will retry, while keeping the successful .then branch that
sets devWorker; reference devWorkerPromise, devWorker, devWorkerId and the
newAsyncFunction(...).then(...) chain when making the change.

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