edge-sentry-cli
Catch Node-only APIs in Edge/serverless code before they hit prod.
Not affiliated with Sentry (the error tracker).
Why
Edge runtimes (Vercel Edge Runtime, Cloudflare Workers, Deno Deploy, etc.) don’t ship Node’s standard library. A single fs import or process.env access hidden deep in a transitive dependency can:
- Crash requests at runtime
- Force your app to fall back to slower Node regions
- Break SSR/ISR paths that claim
runtime: 'edge'
edge-sentry-cli statically scans your code and dependency graph, flags Node-only usage, and suggests Edge-safe fixes.
What it catches (rules)
- node-builtins → importing Node core modules (e.g.
fs, path, crypto, stream, net, tls, http, https, zlib, child_process, cluster, worker_threads, dgram, dns, readline, repl, vm, perf_hooks, os, tty, module, url via node:url, etc.)
- node-globals → usage of
process, Buffer (Node polyfill), global, global.require, __dirname, __filename in Edge-targeted code
- dynamic-require →
require(expr) with non-literal, or CommonJS interop that forces Node resolution
- cjs-in-edge → importing CJS-only packages from Edge entrypoints (many Edge bundlers require ESM)
- transitive-builtins → dependencies that import Node builtins anywhere in their tree
- polyfill-leak (warn) → reliance on bundler polyfills not available at runtime (e.g.
events, buffer)
Targets tune rule strictness. Example: global crypto.subtle is OK (Web Crypto) but import 'crypto' (Node) is not.
Quick start
npm i -D edge-sentry-cli
yarn add -D edge-sentry-cli
pnpm add -D edge-sentry-cli
edge-sentry-cli scan
edge-sentry-cli scan -c edgesentry.config.json
Recommended npm script
{
"scripts": {
"edge:scan": "edge-sentry-cli scan --ci"
}
}
Example output
edge-sentry-cli v0.1.0 • target(s): vercel-edge, cloudflare-workers
Scanning 231 files, 417 modules…
✖ 3 violations (2 error, 1 warn)
src/lib/upload.ts:12 node-builtins(fs)
Importing "fs" is not allowed in Edge targets (vercel-edge, cloudflare-workers).
10 | import fs from 'fs'
| ^^^^^^^
11 | import { put } from '@vercel/blob'
12 | // …
src/auth/session.ts:4 node-globals(process.env) (warn)
`process.env` is not available in Edge. Use platform secrets / runtime bindings.
node_modules/somepkg/index.js → transitive-builtins(crypto)
Dependency "somepkg@2.1.0" imports Node's "crypto". Replace or move usage off Edge.
Exit code: 1 (error violations found)
Configuration
Create edgesentry.config.json (or .yaml).
{
"$schema": "https://unpkg.com/edge-sentry-cli/schema.json",
"targets": ["vercel-edge", "cloudflare-workers"],
"entry": [
"app/**",
"src/**",
"middleware.{ts,tsx,js}"
],
"ignore": ["**/*.test.*", "**/*.stories.*", "scripts/**"],
"rules": {
"node-builtins": "error",
"node-globals": "error",
"dynamic-require": "warn",
"cjs-in-edge": "warn",
"polyfill-leak": "warn"
},
"allow": {
"modules": [],
"globals": []
},
"resolve": {
"tsconfig": "./tsconfig.json",
"alias": {
"@": "./src"
}
},
"output": {
"format": "pretty",
"sarifFile": "./reports/edge-sentry.sarif"
},
"ci": {
"failOn": "warn"
}
}
Targets
vercel-edge – forbids all Node core modules/globals
cloudflare-workers – forbids all Node core modules/globals
deno-deploy – forbids Node core modules (unless using Deno's Node-compat, which is discouraged for Edge paths)
generic-edge – strict superset (default when unknown)
You can list multiple targets. The strictest rule wins.
CLI
Usage: edge-sentry [command] [options]
Commands:
scan Scan files & deps for Edge-incompatible code
explain <id> Explain a rule or violation id with suggestions
list-targets Print built-in targets and their constraints
print-config Show the resolved config
Options:
-c, --config <file> Path to config file
-p, --paths <globs> Glob(s) to scan (overrides config.entry)
--format <fmt> pretty | json | sarif (default: pretty)
--fail-on <level> none | warn | error (CI-friendly)
--cache Enable disk cache (default on in CI)
--max-warnings <n> Fail if warnings exceed n
-q, --quiet Only print violations
-v, --verbose Debug logging
-h, --help Show help
Exit codes
0 – no violations
1 – error-level violations found
2 – internal error (crash, bad config)
How it works
- Graph walker (ESM + CJS) using a fast parser to resolve imports from your entrypoints
- Heuristic target model that encodes what each Edge runtime exposes
- Rule engine that matches imports, globals, and call sites
- Transitive analysis: dives into
node_modules (respects exports/conditions)
- Cache keyed by file content + lockfile to make re-runs cheap
No bundling; purely static analysis. Zero runtime hooks.
Framework recipes
Next.js (App Router)
- Add
"edge:scan": "edge-sentry-cli scan --paths app/** middleware.ts" to your scripts
- Edge hints auto-detected in:
export const runtime = 'edge' in route handlers
middleware.ts
- Common fixes:
- Replace
import crypto from 'crypto' with globalThis.crypto.subtle
- Move Node-only code to
route.ts with export const runtime = 'nodejs' or to server actions executed on Node
Cloudflare Workers
- Use platform bindings for secrets; avoid
process.env
- Use
Web Crypto / TextEncoder / ReadableStream
Suggested fixes (cheatsheet)
import fs from 'fs' | No filesystem | Use KV/blob/HTTP, or move to Node runtime | Use platform storage (Blob, KV) |
import crypto from 'crypto' | Node module missing | globalThis.crypto.subtle | await crypto.subtle.digest('SHA-256', data) |
process.env.X | process undefined | Platform secrets / env bindings | Inject via runtime env/bindings |
Buffer.from() | Node Buffer polyfill missing | TextEncoder / Uint8Array | new TextEncoder().encode(str) |
path.join() | Node path module missing | URL/strings | new URL('./file', import.meta.url) |
| CJS-only deps | Edge bundlers prefer ESM | Choose ESM build or alt package | import { ... } from 'pkg/esm' |
CI integration
GitLab CI (recommended)
edge_scan:
stage: test
image: node:20-alpine
script:
- corepack enable
- yarn install --immutable --prefer-offline
- yarn edge:scan --format sarif --fail-on error
artifacts:
when: always
paths:
- reports/edge-sentry.sarif
expire_in: 1 week
allow_failure: false
GitHub Actions
- name: Edge safety scan
run: |
corepack enable
yarn install --immutable --prefer-offline
yarn edge:scan --format sarif --fail-on error
Programmatic API (optional)
import { scan } from 'edge-sentry-cli';
const result = await scan({
targets: ['vercel-edge'],
entry: ['app/**', 'middleware.ts'],
});
if (result.errors.length) process.exit(1);
Limitations
- Static analysis ≠ full runtime: can’t detect platform quota limits, CPU timeouts, or non-standard shims
- Some packages ship mixed ESM/CJS; detection may be conservative (warns instead of errors)
- Polyfills vary by bundler; prefer explicit configuration if you intentionally include one
Roadmap
- ✅ v0: core rules, JSON/SARIF output, GitLab-friendly exit codes
- 🔜 Codemods (auto-replace Node APIs with Edge equivalents where safe)
- 🔜 PR/MR annotations (GitLab/GitHub) with inline comments
- 🔜 Suggested replacements registry (community-maintained)
- 🔜 VS Code problem matcher + ESLint rule bridge
- 🔜 Caching by workspace graph (Turborepo/Changesets-aware)
Contributing
- Issues & PRs welcome
- Please include a failing fixture for rule changes
- Run tests with
pnpm test / yarn test
License
MIT © You and contributors