Skip to content

refactor: retire lib/effect-cloudflare - #760

Merged
Makisuo merged 2 commits into
mainfrom
refactor/retire-effect-cloudflare
Sep 4, 2026
Merged

refactor: retire lib/effect-cloudflare#760
Makisuo merged 2 commits into
mainfrom
refactor/retire-effect-cloudflare

Conversation

@Makisuo

@Makisuo Makisuo commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

lib/effect-cloudflare was a hand-vendored subset of alchemy-effect's Cloudflare/Workers/* runtime modules, copied in while that package was unpublished — nearly every file carried a provenance header saying exactly that, and promising a mechanical find-and-replace once upstream shipped. The repo now runs alchemy@2.0.0-beta.74, which ships that design as first-party source. This deletes the package.

What changed

Deleted ~1600 LOC with zero production consumers. The DO / Workflow / RPC / KV / fetcher / websocket / http / scheduled-events cluster was imported by nothing outside the library itself — apps/api's two Workflows and its ChatSession DO extend cloudflare:workers directly and bypassed it entirely. The only importer was effect-cloudflare-rpc.test.ts, a test for the dead rpc.ts; it goes too. Every exported symbol was grepped repo-wide first (the apparent hits for namespaceOf, WorkflowEvent, DurableObjectState, HttpEffect are all false positives — a local helper in packages/ui, and CF's / Effect's own types of the same name).

Moved the ~250 LOC that does have consumers into packages/infra, behind runtime-only subpaths so a worker bundle never reaches the deploy graph through the existing ./cloudflare barrel:

Subpath What
@maple/infra/worker-runtime the per-invocation runtime bootstrap (runScheduledEffect, withRequestRuntime, the drainScheduler span-flush fix) plus the env/config surface
@maple/infra/workers-cache the Cache-API service — alchemy exposes only cache purge
@maple/infra/r2 the binding-name-keyed R2 client, incl. bindOptional for self-hosted installs with no R2
@maple/infra/config-helpers the blank-collapsing Config helpers

48 import sites repointed; WorkerEnvironment.layer renamed to workerEnvironmentLayer. No other call-site changes.

What a reviewer should know

Alchemy's runtime services are not importable from a hand-written Worker entry, which is why the env/config tag is still defined in-repo rather than re-exported. Its exports map has no entry finer than a directory (./Cloudflare/**/index.ts), and the Cloudflare/Workers barrel re-exports Source.ts / WorkerProvider.ts / LocalWorkerProvider.ts next to the two runtime services. Bundling it pulls fdir, rolldown glue and Node builtins — 426 KB minified for an entry importing just WorkerEnvironment, against 14 KB with the tag defined locally — and node:module does not exist in workerd. R2/KV/service-binding clients are blocked twice over: makeBucketBinding yields the Worker service and indexes env[bucket.LogicalId], so it needs the deploy-side resource value, and apps/api/src/worker.ts imports alchemy.run.ts type-only.

So packages/infra/src/cloudflare/worker-env.ts defines the service under alchemy's exact key ("Cloudflare.Workers.WorkerEnvironment"). Effect resolves services by that string, so this and alchemy's are the same service in both directions — the whole runtime surface becomes free the day api/alerting move to the class-form Cloudflare.Worker and let alchemy's bundler generate the entry. That migration is deliberately out of scope here; it's the only way worker-runtime.ts itself could go. All of this is written up under Alchemy v1 → v2 notes in docs/infra.md.

The one behavioural difference: alchemy's WorkerConfigProvider also reifies the {"_tag":"Redacted"} markers its deploy-time Config interceptor writes into the env. Maple binds plain vars and secrets, and the vendored copy never reified either, so nothing changes — noted in the file.

Verification

  • tsc --noEmit clean for apps/api, apps/alerting, apps/electric-sync, packages/infra, and tsconfig.alchemy.json
  • oxlint clean over the touched workspaces
  • packages/infra (57 tests) and 9 targeted apps/api suites — ReplayBlobStore, pg-connection-scope, DatabasePgLive, both rate limiters, both queues, slack-integration, internal chat (71 tests) — all pass
  • bundle size re-measured after the correction: 30 KB

Not yet run locally: the full test suite, knip, an apps/api alchemy build, and a bun dev check that a request still produces a root server span with children (the drainScheduler contract — parentless spans would mean the flush regressed). Worth eyeballing the stg deploy for the R2 / Cache / config paths before prd.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Makisuo and others added 2 commits September 4, 2026 01:29
The package was a hand-vendored subset of alchemy-effect's
Cloudflare/Workers/* runtime modules, carried while that package was
unpublished — nearly every file said so in a provenance header. The repo now
runs alchemy@2.0.0-beta.74, which ships that design as first-party source.

Two things drove the shape of this change:

- ~1600 of the package's 2520 lines had no production consumer at all: the
  DO / Workflow / RPC / KV / fetcher / websocket / http / scheduled-events
  cluster. apps/api's own Workflows and its ChatSession DO extend
  cloudflare:workers directly and bypassed the library entirely. Deleted,
  along with the one test that exercised the dead rpc.ts.
- The ~250 lines that do have consumers move to packages/infra behind
  runtime-only subpaths (/worker-runtime, /workers-cache, /r2,
  /config-helpers), kept out of the ./cloudflare barrel so a worker bundle
  never reaches the deploy graph.

Alchemy's runtime services turned out not to be importable from a
hand-written Worker entry, so the env/config tag stays local. Its exports map
has no entry finer than a directory (./Cloudflare/* -> */index.ts), and the
Cloudflare/Workers barrel re-exports Source.ts / WorkerProvider.ts /
LocalWorkerProvider.ts next to the two runtime services: bundling it pulls
fdir, rolldown glue and node:module — 426 KB minified against 14 KB for the
tag alone, and node:module does not exist in workerd. R2/KV/service-binding
clients are blocked twice over, since makeBucketBinding wants the deploy-side
resource value and the Worker service. So worker-env.ts defines the service
under alchemy's exact key ("Cloudflare.Workers.WorkerEnvironment") — Effect
resolves by string, so it is the same service, and the whole surface becomes
importable the day api/alerting move to the class-form Cloudflare.Worker.

Call sites are otherwise unchanged: 48 imports repointed and
WorkerEnvironment.layer renamed to workerEnvironmentLayer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`packages/infra` now holds the `import("cloudflare:workers")` that
lib/effect-cloudflare used to, so it needs that workspace's
`ignoreDependencies: ["cloudflare"]` — the module is a Workers runtime
module, not a package.

The other two knip reports were latent and only surface now that the run
reaches these workspaces: `wrangler` no longer needs an apps/api ignore, and
`BucketAxis` in apps/web has had no consumer since it was introduced
(`makeBucketAxis` is what the charts import).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Makisuo
Makisuo merged commit 9fb50d0 into main Sep 4, 2026
41 checks passed
@Makisuo
Makisuo deleted the refactor/retire-effect-cloudflare branch September 4, 2026 10:39
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

🍁 Maple PR preview

Warning

Preview cleanup could not be confirmed. The Alchemy teardown outcome was skipped.

Final commit 0f7be4c · View workflow run

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