A Worker that builds and passes its tests can still fail the moment it runs. The usual cause is not your code. It is a dependency, sometimes three levels down, that calls an API the runtime does not have.
I kept hitting this, so I built edgefit: a static check that tells you, before you deploy, which APIs your project and its dependencies reach and whether Cloudflare Workers, Bun or Deno support them.
Try it
npx edgefit check
It reads the entry point and settings from your wrangler.jsonc. For Bun or Deno, pass --target bun --entry src/index.ts.
Here is what it says about a small app that pulls in a file watcher and a process spawner by accident:
error unsupported node:fs.watch (workerd)
file watching is not implemented; throws ERR_UNSUPPORTED_OPERATION
chokidar@4.0.3 node_modules/chokidar/esm/handler.js:1:34
via src/index.js > chokidar
see https://github.com/cloudflare/workerd/tree/v1.20260929.1/src/node/internal/internal_fs_callback.ts
Every finding gives the API, the package and version, the file and line, the import chain from your entry, and a link to the runtime source the answer came from. You can check the answer yourself instead of trusting a tool.
How a check works
-
Resolution. edgefit resolves imports the way the target's bundler does, with its export conditions. For Workers that means
workerd,workerandbrowser, so a library's Workers build is checked, not its Node build. - Extraction. It parses each reachable file and collects the Node and Web APIs it uses.
-
Lookup. Each API is checked against pinned data: the workers-nodejs-compat-matrix for what exists, plus a small set of curated overrides for APIs that exist but throw or do nothing, such as
fs.watchorchild_processon Workers. The overrides are read from the runtime source and link to it.
Your compatibility_date and flags matter too. Without nodejs_compat, Node built-ins are unavailable, so import process from 'node:process' is an error. edgefit reads your wrangler config and reports that.
Precision matters more than coverage
A tool that cries wolf gets ignored, so false errors cost more than missed ones. Two things keep edgefit honest:
-
Guards. When code checks that an API exists before using it, the finding is reported as guarded and does not fail the check. drizzle-orm's blob columns, for example, use
Bufferwithout a check, and edgefit reports that only whennodejs_compatis off. The guarded uses in the same file stay out. - Real apps. It is tested against small apps built on real, pinned packages: the Hono starter, Hono with zod, drizzle-orm on D1, a Postgres client and a Nitro build with oauth4webapi and jose. They must report nothing that nobody can act on. A known-bad app must report exactly what Workers lacks, so a change that makes edgefit miss an API fails too.
What it will not tell you
edgefit is static, so a clean run is not a guarantee. Code it cannot analyze, such as a computed require, is reported as unknown instead of passing silently. It does not run your code and does not see what happens at runtime. The limitations page lists the rest.
Keeping the data current
The data is vendored and pinned (workerd, Bun and Deno versions are on the status page), so a result only changes when your project or the data changes. A weekly job runs the real runtimes, compares them with the data and opens an issue when it falls behind.
In CI
The GitHub Action checks the merge base and the head of a pull request and comments only on what the pull request adds or fixes, so findings you already accepted do not drown out new ones:
- uses: hamedniroomand/edgefit@v0
with:
targets: workerd
It installs the published package and verifies its signature and provenance. JSON output is available for your own tooling.
Where I would like help
It is a 0.x release, and I would like to know where it is wrong: an API reported that works, or one that should have been reported. Open an issue with the API and the target. Those are the most useful reports, and each one becomes a test.

Top comments (0)