[builders] Add opt-out for discovering workflows in node_modules#3054
Conversation
Add a `discoverWorkflowsInNodeModules` config option (default true) and a `WORKFLOW_DISCOVER_NODE_MODULES` env var to disable discovery of `"use workflow"`/`"use step"` files inside `node_modules`. When disabled, third-party workflow/step files are no longer registered, so they are neither transformed nor bundled. Serde discovery and import traversal are unaffected, preserving the SDK's own runtime serde entry point. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: ec8a3ca The changes in this PR will be included in the next version bump. This PR includes changesets to release 16 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
🧪 E2E Test Results✅ All tests passed Summary
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
✅ 📋 Other
✅ vercel-multi-region
❌ Some E2E test jobs failed:
Check the workflow run for details. |
📊 Workflow Benchmarkscommit Backend:
📜 Previous results (1)19b4978Wed, 22 Jul 2026 16:42:59 GMT · run logs
Best/P75/P90/P99 deltas compare against the most recent benchmark run on Metrics — TTFS: time to first step body (in-deployment start() → first step body, deployment clocks) · STSO: step-to-step overhead (gap between consecutive step bodies) · WO: workflow overhead (whole-run time outside step bodies, in-deployment anchored) · SL: stream latency (in-deployment write → read propagation, readAt - writtenAt) Scenarios — step: one trivial no-op step, no stream; no hooks, so the run stays in turbo mode (in-process fast path) · stream: one streaming step; no hooks, so the run stays in turbo mode (in-process fast path) · hook + stream: registers a hook before one step, which exits turbo mode (dispatch path) · 1020 steps: 1020 trivial sequential steps; STSO is measured between consecutive steps in the given step ranges, and WO is the whole-run overhead outside step bodies · stream latency: parallel reader/writer steps on a dedicated stream; SL is the in-deployment write->read propagation (readAt - writtenAt) 🔴 marks a percentile over its target (within target is left unmarked). Targets (p75/p90/p99, ms) — TTFS 200/300/600 · SL 50/60/125 · STSO (1-20) 20/30/60 · STSO (101-120) 30/45/90 · STSO (1001-1020) 40/60/120 All metrics are measured from deployment-side timestamps only. Runs are triggered by an in-deployment route that stamps the anchor ( Cold starts are kept in the numbers on purpose — they are part of real bursty-workload latency. The workbench deployment cold-starts the |
Refine the discoverWorkflowsInNodeModules opt-out: instead of reading and scanning every dependency file and only skipping registration, stop following imports from application code that resolve into node_modules altogether. The build no longer reads, scans, or descends into third-party dependency graphs, skipping that cost entirely. Imports within node_modules are still followed, so the SDK's seeded runtime serde entry point (which lives under node_modules in installed apps) keeps discovering its transitive classes such as Run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
|
Backport PR opened against |
Summary
Adds an opt-out that stops workflow discovery from descending into
node_modules.Today,
getInputFiles()excludesnode_modules, but the fast-discovery import-graph walk follows imports into any dependency that declares aworkflow/@workflow/*dependency and reads/scans its files (registering the workflow/step/serde files it finds). There was no way to turn that off. This PR adds one.What's added
discoverWorkflowsInNodeModules?: booleanonBaseWorkflowConfig(defaulttrue, current behavior).WORKFLOW_DISCOVER_NODE_MODULES(0/falseto disable). Precedence mirrorssourcemap: config > env var > default.node_modulesare not followed, so the build never reads, scans, or descends into dependency file graphs. This skips the scanning cost and stops third-party workflow/step/serde code from being discovered.Implementation
The gate is a single stateless rule in
processImportSpecifier: when opted out, skip an import whose resolved target is undernode_modulesif the importer is not itself undernode_modules. Application code therefore can't pull third-party dependencies into discovery, but imports withinnode_modulesare still followed.That last part is deliberate and load-bearing: in an installed app
@workflow/corelives undernode_modules, and the SDK seeds@workflow/core/runtime/runas an entry point so built-in serde classes (Run, plus its transitiveserialization.js/runs.js) get registered. Because the seeded entry is itself undernode_modules, it keeps traversing its own subtree — so the SDK's own serde discovery is preserved; only third-partynode_modulesdescent is dropped.Tests
fast-discovery.test.ts: a dependency's files are never read/scanned/traversed when the option isfalse(asserts they're absent fromdiscoveredFilesand no parent edge into the package is recorded). Mutation-verified: fails without the gate, passes with it.node_modulespackage as an entry point (mirroring the SDK runtime serde entry) and asserts its subtree is still discovered — proving the SDK-serde carve-out.srcsuite green (17/17 in the file);pnpm typecheckclean.Docs
Documented
WORKFLOW_DISCOVER_NODE_MODULESindocs/content/docs/v5/configuration/build-and-diagnostics.mdx.Docs Preview
/v5/docs/configuration/build-and-diagnostics#discovery(Preview is behind Vercel deployment protection; requires team access.)
Follow-up (not in this PR)
The option is exposed as a builders config field + env var only. Surfacing it as a first-class framework option (e.g.
withWorkflow({ discoverWorkflowsInNodeModules })) is a possible follow-up; the env var already works across all integrations today.🤖 Generated with Claude Code