Skip to content

feat(cloudflare-runtime): local send_email stub that logs under alchemy dev - #76

Merged
john-royal merged 3 commits into
alchemy-run:mainfrom
zeyuri:feat/send-email-local-stub
Jul 22, 2026
Merged

feat(cloudflare-runtime): local send_email stub that logs under alchemy dev#76
john-royal merged 3 commits into
alchemy-run:mainfrom
zeyuri:feat/send-email-local-stub

Conversation

@zeyuri

@zeyuri zeyuri commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Adds SendEmail.local alongside the existing SendEmail.remote, plus a send-email.worker.ts wrapped-binding stub. Under a local runtime (e.g. alchemy dev), env.<binding>.send(...) logs the message metadata (from / to / subject) and resolves, instead of proxying to the deployed binding and delivering real mail.

Why

Today the only send_email variant is remote(...), which proxies to the deployed binding — so a Worker run locally sends real email from the production domain on every send() in the dev loop, with no local stub to fall back to. This surfaced downstream in alchemy: see alchemy-run/alchemy#865, where the first fix was just a docs warning that "send_email is live under alchemy dev." @john-royal noted that Miniflare/Wrangler instead console.log the message unless you opt into the real remote binding, and suggested implementing that. This PR is the runtime half.

How

  • send-email.worker.ts: a minimal stub whose send() logs from/to/subject (when present) and resolves. Mirrors the no-op analytics-engine.worker.ts stub, but logs rather than discards so the dev loop still shows what would have been sent. Handles both the builder-form message and a pre-built EmailMessage.
  • SendEmail.ts: registers the stub as a second internal extension module in SendEmailLive (alongside the existing cloudflare-internal:email EmailMessage shim, reused unchanged), and adds export const local(props) that points a wrapped binding at it — same shape as remote(props).

Consumer side

The alchemy side (separate PR, ready to open once this lands and a cloudflare-runtime version bump exists) defaults send_email to .local under alchemy dev and adds the dev: { remote: true } opt-in that selects .remote — the flag shape agreed in alchemy-run/alchemy#865.

Verification

bunx --bun tsdown builds clean (worker + node outputs, dts includes declare const local), tsc -b passes, oxlint/oxfmt clean.

sam-goodwin and others added 2 commits July 17, 2026 13:34
…s typed error/requirement channels

The dts bundler cannot name the inferred Credentials requirement from
@distilled.cloud/cloudflare and degraded make/layer (and everything
composed from them, e.g. RuntimeServices.layerRuntime) to any/any.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…my dev

Add SendEmail.local alongside SendEmail.remote: a wrapped-binding stub
(send-email.worker.ts) whose send() logs the message metadata (from/to/
subject) and resolves instead of delivering, mirroring analytics-engine's
local no-op. This lets a consumer default send_email to log-in-dev and opt
into the real remote binding explicitly, matching Miniflare/Wrangler
behavior. Reuses the cloudflare-internal:email EmailMessage shim.
@zeyuri

zeyuri commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@sam-goodwin I think this is in good shape, how about you?

@sam-goodwin

Copy link
Copy Markdown
Contributor

@john-royal please review this

@john-royal
john-royal merged commit 99f9952 into alchemy-run:main Jul 22, 2026
3 of 9 checks passed
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.

3 participants