Skip to content

refactor(next): remove step file copy mechanism from deferred builder#1796

Draft
TooTallNate wants to merge 2 commits intomainfrom
tootallnate/remove-step-file-copy
Draft

refactor(next): remove step file copy mechanism from deferred builder#1796
TooTallNate wants to merge 2 commits intomainfrom
tootallnate/remove-step-file-copy

Conversation

@TooTallNate
Copy link
Copy Markdown
Member

Summary

Follow-up to #1686 (removing client transform mode). Step sources in the Next.js deferred builder are now imported directly into the generated step/route.js instead of being copied into __workflow_step_files__/ per-file with hashed names, metadata comments, and rewritten imports. This mirrors how serde files are already handled in the same builder.

Why this is now possible

The step file copy mechanism was introduced in dc2dc6a with two stated reasons:

  1. Force the loader to run on node_modules step sources — a file imported directly as a package specifier might be externalized or bypassed by the bundler.
  2. Control the SWC relativeFilename for deterministic step IDs — the copy carried metadata so the loader fed SWC the original relative filename.

Both concerns are now moot:

  • Serde files (which include package sources) already use direct imports and the workflow loader transforms them in step mode; the same works for step files.
  • The loader's getRelativeFilenameForSwc (including resolveWorkflowAliasRelativePath) already produces stable IDs from the original path.

Changes

  • packages/next/src/builder-deferred.ts:
    • Remove copyDiscoveredStepFiles, createResponseBuiltinsStepFile, rewriteRelativeImportsForCopiedStep, rewriteCopiedStepImportSpecifier, resolveBareCopiedStepSpecifier, getStepCopyFileName.
    • Rewrite buildStepsFunction so the step route imports originals via getImportPath (package specifier for workspace/node_modules, relative path for app files).
    • Resolve the workflow SDK's built-in response steps file via require.resolve('workflow/internal/builtins') at build time and emit one import.
    • Keep the legacy __workflow_step_files__/ directory rm on boot so upgrades leave no stale artifacts.
  • packages/next/src/loader.ts:
    • Remove all isDeferredStepCopyFilePath branches: metadata parsing, inline-sourcemap chaining, skip-transform bypass, discoveryFilePath indirection, isGeneratedWorkflowFile carve-out.
    • Loader now always uses filename for the SWC relative filename and module specifier.
  • packages/next/src/step-copy-utils.ts: deleted.
  • packages/next/package.json: drop enhanced-resolve dependency.
  • packages/core/e2e/dev.test.ts: rename supportsDeferredStepCopiesusesDeferredBuilder; assert step registration via manifest.json entries and import specifiers in the generated route, not copied file contents.

Diff stats

 packages/core/e2e/dev.test.ts         | 144 ++++-------
 packages/next/package.json            |   1 -
 packages/next/src/builder-deferred.ts | 451 ++++-------------------
 packages/next/src/loader.ts           | 105 +++----
 packages/next/src/step-copy-utils.ts  | 137 --------
 pnpm-lock.yaml                        |   3 -
 6 files changed, 160 insertions(+), 686 deletions(-)

Verification

Local verification against workbench/nextjs-turbopack:

  • pnpm build (all packages): pass
  • pnpm typecheck: pass
  • pnpm -C workbench/nextjs-turbopack build: pass; generated step/route.js contains direct imports (incl. workflow/internal/builtins, @workflow/ai/agent, workspace and app files); __workflow_step_files__/ directory not created; manifest has 27 steps / 16 workflows / 3 classes.
  • E2E (packages/core/e2e/e2e.test.ts against http://localhost:3000): 71/71 pass.
  • Dev tests (packages/core/e2e/dev.test.ts): 6/6 pass against a fresh dev server.

Step sources are now imported directly into the generated `step/route.js`
using the same `getImportPath`-based logic already used for serde files.
This is possible because the SWC plugin's client mode was merged into
step mode (#1686), so the workflow loader always runs in step mode and
transforms every file it sees, including those from packages.

Removed:
- `__workflow_step_files__/` per-file copies with hashed names, metadata
  comments, inline source maps, and bare-specifier rewriting via
  `enhanced-resolve`
- `createResponseBuiltinsStepFile`; builtins are now imported via
  `require.resolve('workflow/internal/builtins')` at build time
- `step-copy-utils.ts` and all copy-specific branches in `loader.ts`
- `enhanced-resolve` dependency

The deferred builder still removes the legacy `__workflow_step_files__/`
directory on boot so upgrades leave no stale artifacts behind.

Dev tests that inspected copied step file contents now inspect the
manifest.json entries (which list every discovered step keyed by source
path).
@TooTallNate TooTallNate requested review from a team and ijjk as code owners April 17, 2026 07:30
Copilot AI review requested due to automatic review settings April 17, 2026 07:30
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 17, 2026

🦋 Changeset detected

Latest commit: c44020e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 17 packages
Name Type
@workflow/next Patch
workflow Patch
@workflow/ai Patch
@workflow/world-testing Patch
@workflow/core Patch
@workflow/builders Patch
@workflow/cli Patch
@workflow/nitro Patch
@workflow/vitest Patch
@workflow/web-shared Patch
@workflow/web Patch
@workflow/astro Patch
@workflow/nest Patch
@workflow/rollup Patch
@workflow/sveltekit Patch
@workflow/vite Patch
@workflow/nuxt Patch

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

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Apr 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview, Comment Apr 17, 2026 8:20am
example-nextjs-workflow-webpack Ready Ready Preview, Comment Apr 17, 2026 8:20am
example-workflow Ready Ready Preview, Comment Apr 17, 2026 8:20am
workbench-astro-workflow Ready Ready Preview, Comment Apr 17, 2026 8:20am
workbench-express-workflow Ready Ready Preview, Comment Apr 17, 2026 8:20am
workbench-fastify-workflow Ready Ready Preview, Comment Apr 17, 2026 8:20am
workbench-hono-workflow Ready Ready Preview, Comment Apr 17, 2026 8:20am
workbench-nitro-workflow Ready Ready Preview, Comment Apr 17, 2026 8:20am
workbench-nuxt-workflow Ready Ready Preview, Comment Apr 17, 2026 8:20am
workbench-sveltekit-workflow Ready Ready Preview, Comment Apr 17, 2026 8:20am
workbench-vite-workflow Ready Ready Preview, Comment Apr 17, 2026 8:20am
workflow-docs Ready Ready Preview, Comment, Open in v0 Apr 17, 2026 8:20am
workflow-swc-playground Ready Ready Preview, Comment Apr 17, 2026 8:20am
workflow-web Ready Ready Preview, Comment Apr 17, 2026 8:20am

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 17, 2026

📊 Benchmark Results

📈 Comparing against baseline from main branch. Green 🟢 = faster, Red 🔺 = slower.

workflow with no steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 0.043s (-4.1%) 1.005s (~) 0.962s 10 1.00x
💻 Local Nitro 0.043s (~) 1.005s (~) 0.962s 10 1.02x
🐘 Postgres Next.js (Turbopack) 0.051s 1.009s 0.957s 10 1.21x
🐘 Postgres Nitro 0.060s (-37.2% 🟢) 1.011s (-3.1%) 0.951s 10 1.41x
🐘 Postgres Express 0.120s (+106.4% 🔺) 1.064s (+5.3% 🔺) 0.944s 10 2.82x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 0.383s (+52.3% 🔺) 2.554s (+9.5% 🔺) 2.171s 10 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.115s 2.010s 0.895s 10 1.00x
💻 Local Nitro 1.130s (~) 2.005s (~) 0.876s 10 1.01x
💻 Local Express 1.139s (+1.2%) 2.006s (~) 0.867s 10 1.02x
🐘 Postgres Express 1.139s (-0.6%) 2.018s (~) 0.879s 10 1.02x
🐘 Postgres Nitro 1.154s (+1.2%) 2.009s (~) 0.855s 10 1.04x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.107s (+3.5%) 3.747s (-2.2%) 1.640s 10 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 10.718s 11.017s 0.299s 3 1.00x
💻 Local Express 10.951s (~) 11.024s (~) 0.072s 3 1.02x
🐘 Postgres Nitro 10.961s (+0.8%) 11.029s (~) 0.068s 3 1.02x
💻 Local Nitro 10.988s (~) 11.025s (~) 0.037s 3 1.03x
🐘 Postgres Express 11.075s (+1.0%) 11.685s (+6.0% 🔺) 0.610s 3 1.03x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 18.263s (+5.5% 🔺) 19.967s (+2.9%) 1.704s 2 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 14.236s 15.024s 0.789s 4 1.00x
🐘 Postgres Nitro 14.622s (~) 15.027s (~) 0.405s 4 1.03x
💻 Local Nitro 15.069s (~) 16.032s (~) 0.962s 4 1.06x
💻 Local Express 15.099s (+0.9%) 16.033s (+6.7% 🔺) 0.934s 4 1.06x
🐘 Postgres Express 15.949s (+9.4% 🔺) 16.570s (+10.3% 🔺) 0.621s 4 1.12x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 33.295s (-36.7% 🟢) 35.026s (-35.9% 🟢) 1.731s 2 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 13.494s 14.026s 0.532s 7 1.00x
🐘 Postgres Nitro 14.027s (~) 14.594s (+2.0%) 0.568s 7 1.04x
💻 Local Nitro 16.573s (-1.3%) 17.033s (~) 0.460s 6 1.23x
🐘 Postgres Express 16.673s (+19.0% 🔺) 17.218s (+18.0% 🔺) 0.545s 6 1.24x
💻 Local Express 16.951s (+2.1%) 17.199s (+1.0%) 0.249s 6 1.26x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 59.656s (-84.8% 🟢) 61.714s (-84.4% 🟢) 2.058s 2 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.213s 2.011s 0.798s 15 1.00x
🐘 Postgres Nitro 1.279s (~) 2.011s (~) 0.732s 15 1.05x
🐘 Postgres Express 1.430s (+13.4% 🔺) 2.025s (+0.7%) 0.595s 15 1.18x
💻 Local Nitro 1.484s (-9.1% 🟢) 2.005s (-3.3%) 0.522s 15 1.22x
💻 Local Express 1.571s (+5.5% 🔺) 2.006s (~) 0.435s 15 1.29x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.374s (-30.1% 🟢) 4.351s (-11.8% 🟢) 1.977s 7 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 2.373s (+0.9%) 3.010s (~) 0.637s 10 1.00x
🐘 Postgres Next.js (Turbopack) 2.407s 3.009s 0.602s 10 1.01x
🐘 Postgres Express 2.534s (+7.3% 🔺) 3.144s (+4.5%) 0.610s 10 1.07x
💻 Local Nitro 2.867s (-8.8% 🟢) 3.108s (-20.0% 🟢) 0.240s 10 1.21x
💻 Local Express 3.088s (+4.6%) 3.885s (+12.5% 🔺) 0.797s 8 1.30x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 3.003s (-57.7% 🟢) 4.619s (-48.1% 🟢) 1.616s 7 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 3.486s (~) 4.012s (~) 0.526s 8 1.00x
🐘 Postgres Next.js (Turbopack) 3.653s 4.014s 0.362s 8 1.05x
🐘 Postgres Express 3.716s (+6.6% 🔺) 4.301s (+7.2% 🔺) 0.585s 7 1.07x
💻 Local Nitro 8.302s (-0.6%) 8.776s (-2.7%) 0.473s 4 2.38x
💻 Local Express 9.542s (+14.4% 🔺) 10.355s (+14.7% 🔺) 0.813s 3 2.74x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 3.505s (-60.7% 🟢) 5.086s (-53.6% 🟢) 1.580s 6 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.217s 2.009s 0.791s 15 1.00x
🐘 Postgres Nitro 1.272s (+1.2%) 2.009s (~) 0.736s 15 1.05x
🐘 Postgres Express 1.319s (+4.9%) 2.011s (~) 0.692s 15 1.08x
💻 Local Nitro 1.532s (-17.9% 🟢) 2.006s (-14.3% 🟢) 0.475s 15 1.26x
💻 Local Express 1.553s (-18.0% 🟢) 2.006s (-15.1% 🟢) 0.453s 15 1.28x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.774s (-5.4% 🟢) 4.374s (-5.8% 🟢) 1.601s 7 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 2.349s (~) 3.010s (~) 0.660s 10 1.00x
🐘 Postgres Next.js (Turbopack) 2.382s 3.010s 0.628s 10 1.01x
🐘 Postgres Express 2.492s (+6.4% 🔺) 3.027s (+0.5%) 0.535s 10 1.06x
💻 Local Nitro 2.934s (-4.3%) 3.309s (-14.9% 🟢) 0.375s 10 1.25x
💻 Local Express 3.909s (+24.8% 🔺) 4.728s (+25.7% 🔺) 0.819s 7 1.66x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.716s (-13.6% 🟢) 4.434s (-1.9%) 1.718s 7 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 3.472s (~) 4.011s (~) 0.538s 8 1.00x
🐘 Postgres Next.js (Turbopack) 3.619s 4.011s 0.392s 8 1.04x
🐘 Postgres Express 3.910s (+11.7% 🔺) 4.301s (+7.2% 🔺) 0.391s 8 1.13x
💻 Local Nitro 8.184s (-10.5% 🟢) 9.027s (-9.9% 🟢) 0.843s 4 2.36x
💻 Local Express 9.209s (+4.6%) 10.027s (+8.1% 🔺) 0.818s 3 2.65x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 3.585s (-46.9% 🟢) 5.462s (-36.1% 🟢) 1.877s 6 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.702s 1.007s 0.305s 60 1.00x
🐘 Postgres Nitro 0.832s (+1.5%) 1.023s (+1.7%) 0.191s 59 1.19x
💻 Local Express 1.016s (+3.3%) 1.824s (+69.6% 🔺) 0.808s 33 1.45x
🐘 Postgres Express 1.061s (+26.4% 🔺) 1.552s (+51.6% 🔺) 0.491s 39 1.51x
💻 Local Nitro 1.206s (+23.0% 🔺) 2.149s (+96.5% 🔺) 0.943s 28 1.72x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 10.029s (-30.9% 🟢) 12.217s (-24.0% 🟢) 2.188s 5 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.677s 2.030s 0.353s 45 1.00x
🐘 Postgres Nitro 1.977s (+2.6%) 2.377s (+13.1% 🔺) 0.400s 38 1.18x
🐘 Postgres Express 2.500s (+26.5% 🔺) 2.960s (+31.1% 🔺) 0.460s 31 1.49x
💻 Local Express 3.028s (~) 3.843s (+7.2% 🔺) 0.815s 24 1.81x
💻 Local Nitro 3.087s (+1.7%) 3.922s (+4.4%) 0.835s 23 1.84x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 29.840s (-40.1% 🟢) 31.828s (-38.5% 🟢) 1.989s 3 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 3.533s 4.044s 0.511s 30 1.00x
🐘 Postgres Nitro 4.043s (-1.5%) 4.669s (+1.4%) 0.626s 26 1.14x
🐘 Postgres Express 4.995s (+25.2% 🔺) 5.572s (+27.5% 🔺) 0.577s 22 1.41x
💻 Local Express 9.059s (-1.6%) 9.479s (-5.4% 🟢) 0.420s 13 2.56x
💻 Local Nitro 9.109s (-2.0%) 9.788s (-2.3%) 0.679s 13 2.58x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 82.648s (-22.9% 🟢) 84.593s (-22.3% 🟢) 1.945s 2 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.214s 1.008s 0.793s 60 1.00x
🐘 Postgres Nitro 0.288s (+1.5%) 1.007s (~) 0.719s 60 1.34x
🐘 Postgres Express 0.362s (+28.3% 🔺) 1.018s (+1.0%) 0.655s 59 1.69x
💻 Local Express 0.585s (+4.3%) 1.005s (~) 0.420s 60 2.73x
💻 Local Nitro 0.589s (-2.5%) 1.004s (-1.7%) 0.415s 60 2.75x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 1.512s (-25.2% 🟢) 3.080s (-18.8% 🟢) 1.568s 20 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.468s 1.007s 0.539s 90 1.00x
🐘 Postgres Nitro 0.508s (+2.4%) 1.007s (~) 0.498s 90 1.09x
🐘 Postgres Express 0.907s (+78.0% 🔺) 1.459s (+45.0% 🔺) 0.551s 62 1.94x
💻 Local Nitro 2.456s (-3.2%) 3.009s (~) 0.553s 30 5.25x
💻 Local Express 2.474s (-1.6%) 3.009s (~) 0.535s 30 5.28x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 3.293s (-6.9% 🟢) 4.763s (-8.3% 🟢) 1.470s 20 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.714s 1.007s 0.293s 120 1.00x
🐘 Postgres Nitro 0.822s (+4.0%) 1.017s (+1.0%) 0.195s 118 1.15x
🐘 Postgres Express 1.147s (+40.1% 🔺) 1.603s (+57.5% 🔺) 0.456s 75 1.61x
💻 Local Nitro 10.660s (-4.7%) 11.120s (-4.7%) 0.459s 11 14.94x
💻 Local Express 11.011s (-1.6%) 11.754s (-1.6%) 0.743s 11 15.43x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 6.449s (-37.6% 🟢) 8.239s (-32.9% 🟢) 1.790s 15 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.172s 1.001s 0.001s 1.010s 0.838s 10 1.00x
🐘 Postgres Express 0.193s (-6.0% 🟢) 0.998s (~) 0.027s (+1593.8% 🔺) 1.036s (+2.4%) 0.843s 10 1.12x
💻 Local Express 0.209s (+4.8%) 1.004s (~) 0.013s (+3.3%) 1.019s (~) 0.810s 10 1.21x
💻 Local Nitro 0.211s (-1.2%) 1.004s (~) 0.010s (-19.2% 🟢) 1.016s (~) 0.805s 10 1.22x
🐘 Postgres Nitro 0.212s (+3.6%) 0.998s (~) 0.001s (-13.3% 🟢) 1.010s (~) 0.798s 10 1.23x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 1.732s (-74.7% 🟢) 3.167s (-63.4% 🟢) 0.852s (+34.8% 🔺) 4.454s (-54.5% 🟢) 2.722s 10 1.00x
▲ Vercel Express ⚠️ missing - - - - -
▲ Vercel Nitro ⚠️ missing - - - - -

🔍 Observability: Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.623s 1.026s 0.004s 1.040s 0.417s 58 1.00x
🐘 Postgres Nitro 0.626s (~) 1.005s (~) 0.004s (-2.9%) 1.023s (~) 0.396s 59 1.01x
💻 Local Nitro 0.774s (-7.7% 🟢) 1.013s (~) 0.010s (+2.7%) 1.024s (-8.2% 🟢) 0.250s 59 1.24x
💻 Local Express 0.844s (+11.5% 🔺) 1.013s (-1.6%) 0.010s (+1.1%) 1.117s (+7.4% 🔺) 0.273s 54 1.35x
🐘 Postgres Express 0.853s (+35.4% 🔺) 1.241s (+23.3% 🔺) 0.065s (+1587.0% 🔺) 1.349s (+31.8% 🔺) 0.495s 45 1.37x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 6.524s (-61.4% 🟢) 8.054s (-55.8% 🟢) 0.204s (-3.2%) 8.768s (-53.7% 🟢) 2.244s 7 1.00x
▲ Vercel Express ⚠️ missing - - - - -
▲ Vercel Nitro ⚠️ missing - - - - -

🔍 Observability: Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.942s 1.251s 0.000s 1.260s 0.318s 48 1.00x
🐘 Postgres Nitro 0.972s (~) 1.300s (+4.2%) 0.000s (-47.8% 🟢) 1.317s (+4.7%) 0.345s 46 1.03x
💻 Local Express 1.251s (+2.1%) 2.023s (~) 0.000s (~) 2.025s (~) 0.773s 30 1.33x
💻 Local Nitro 1.264s (+3.4%) 2.024s (~) 0.000s (+266.7% 🔺) 2.026s (~) 0.762s 30 1.34x
🐘 Postgres Express 1.391s (+44.7% 🔺) 1.781s (+39.4% 🔺) 0.000s (-28.1% 🟢) 1.905s (+45.8% 🔺) 0.514s 32 1.48x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 4.671s (-54.1% 🟢) 6.204s (-46.1% 🟢) 0.000s (+Infinity% 🔺) 6.671s (-44.6% 🟢) 2.000s 9 1.00x
▲ Vercel Express ⚠️ missing - - - - -
▲ Vercel Nitro ⚠️ missing - - - - -

🔍 Observability: Next.js (Turbopack)

fan-out fan-in 10 streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.824s (+1.8%) 2.141s (~) 0.000s (-100.0% 🟢) 2.157s (-0.8%) 0.333s 28 1.00x
🐘 Postgres Next.js (Turbopack) 1.890s 2.145s 0.000s 2.153s 0.262s 28 1.04x
🐘 Postgres Express 2.362s (+33.3% 🔺) 2.821s (+29.5% 🔺) 0.000s (NaN%) 2.902s (+32.0% 🔺) 0.539s 22 1.30x
💻 Local Express 3.493s (+0.7%) 4.101s (+1.7%) 0.000s (-41.7% 🟢) 4.104s (+1.7%) 0.611s 15 1.92x
💻 Local Nitro 3.678s (+8.6% 🔺) 4.170s (+3.4%) 0.001s (+100.0% 🔺) 4.173s (+3.4%) 0.495s 15 2.02x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 4.109s (-26.8% 🟢) 5.191s (-25.6% 🟢) 0.000s (-100.0% 🟢) 5.668s (-24.8% 🟢) 1.560s 11 1.00x
▲ Vercel Express ⚠️ missing - - - - -
▲ Vercel Nitro ⚠️ missing - - - - -

🔍 Observability: Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World 🥇 Fastest Framework Wins
💻 Local Nitro 12/21
🐘 Postgres Next.js (Turbopack) 16/21
▲ Vercel Next.js (Turbopack) 21/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 🐘 Postgres 14/21
Next.js (Turbopack) 🐘 Postgres 19/21
Nitro 🐘 Postgres 18/21
Column Definitions
  • Workflow Time: Runtime reported by workflow (completedAt - createdAt) - primary metric
  • TTFB: Time to First Byte - time from workflow start until first stream byte received (stream benchmarks only)
  • Slurp: Time from first byte to complete stream consumption (stream benchmarks only)
  • Wall Time: Total testbench time (trigger workflow + poll for result)
  • Overhead: Testbench overhead (Wall Time - Workflow Time)
  • Samples: Number of benchmark iterations run
  • vs Fastest: How much slower compared to the fastest configuration for this benchmark

Worlds:

  • 💻 Local: In-memory filesystem world (local development)
  • 🐘 Postgres: PostgreSQL database world (local development)
  • ▲ Vercel: Vercel production/preview deployment
  • 🌐 Turso: Community world (local development)
  • 🌐 MongoDB: Community world (local development)
  • 🌐 Redis: Community world (local development)
  • 🌐 Jazz: Community world (local development)

📋 View full workflow run


Some benchmark jobs failed:

  • Local: failure
  • Postgres: success
  • Vercel: cancelled

Check the workflow run for details.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 17, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
❌ ▲ Vercel Production 922 1 67 990
❌ 💻 Local Development 990 4 86 1080
❌ 📦 Local Production 992 2 86 1080
❌ 🐘 Local Postgres 992 2 86 1080
✅ 📋 Other 252 0 18 270
Total 4148 9 343 4500

❌ Failed Tests

▲ Vercel Production (1 failed)

nextjs-turbopack (1 failed):

  • error handling error propagation step errors cross-file step error preserves message and function names in stack
💻 Local Development (4 failed)

nextjs-webpack-canary (2 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
  • error handling error propagation step errors cross-file step error preserves message and function names in stack

nextjs-webpack-stable (2 failed):

  • error handling error propagation step errors basic step error preserves message and stack trace
  • error handling error propagation step errors cross-file step error preserves message and function names in stack
📦 Local Production (2 failed)

nextjs-turbopack-canary (1 failed):

  • error handling error propagation step errors cross-file step error preserves message and function names in stack

nextjs-turbopack-stable (1 failed):

  • error handling error propagation step errors cross-file step error preserves message and function names in stack
🐘 Local Postgres (2 failed)

nextjs-turbopack-canary (1 failed):

  • error handling error propagation step errors cross-file step error preserves message and function names in stack

nextjs-turbopack-stable (1 failed):

  • error handling error propagation step errors cross-file step error preserves message and function names in stack

Details by Category

❌ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 83 0 7
✅ example 83 0 7
✅ express 83 0 7
✅ fastify 83 0 7
✅ hono 83 0 7
❌ nextjs-turbopack 87 1 2
✅ nextjs-webpack 88 0 2
✅ nitro 83 0 7
✅ nuxt 83 0 7
✅ sveltekit 83 0 7
✅ vite 83 0 7
❌ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 84 0 6
✅ express-stable 84 0 6
✅ fastify-stable 84 0 6
✅ hono-stable 84 0 6
✅ nextjs-turbopack-canary 71 0 19
✅ nextjs-turbopack-stable 90 0 0
❌ nextjs-webpack-canary 69 2 19
❌ nextjs-webpack-stable 88 2 0
✅ nitro-stable 84 0 6
✅ nuxt-stable 84 0 6
✅ sveltekit-stable 84 0 6
✅ vite-stable 84 0 6
❌ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 84 0 6
✅ express-stable 84 0 6
✅ fastify-stable 84 0 6
✅ hono-stable 84 0 6
❌ nextjs-turbopack-canary 70 1 19
❌ nextjs-turbopack-stable 89 1 0
✅ nextjs-webpack-canary 71 0 19
✅ nextjs-webpack-stable 90 0 0
✅ nitro-stable 84 0 6
✅ nuxt-stable 84 0 6
✅ sveltekit-stable 84 0 6
✅ vite-stable 84 0 6
❌ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 84 0 6
✅ express-stable 84 0 6
✅ fastify-stable 84 0 6
✅ hono-stable 84 0 6
❌ nextjs-turbopack-canary 70 1 19
❌ nextjs-turbopack-stable 89 1 0
✅ nextjs-webpack-canary 71 0 19
✅ nextjs-webpack-stable 90 0 0
✅ nitro-stable 84 0 6
✅ nuxt-stable 84 0 6
✅ sveltekit-stable 84 0 6
✅ vite-stable 84 0 6
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 84 0 6
✅ e2e-local-postgres-nest-stable 84 0 6
✅ e2e-local-prod-nest-stable 84 0 6

📋 View full workflow run


Some E2E test jobs failed:

  • Vercel Prod: failure
  • Local Dev: failure
  • Local Prod: failure
  • Local Postgres: failure
  • Windows: cancelled

Check the workflow run for details.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors the Next.js deferred builder to stop copying step source files into a generated directory, instead importing step/serde sources directly from their original locations (mirroring existing serde handling) and simplifying the loader accordingly.

Changes:

  • Remove deferred step copy utilities and copy/rewrite logic; generate step/route.js with direct imports (including workflow response builtins).
  • Simplify the Next loader by removing step-copy-file special casing and always using the actual filename/module specifier inputs.
  • Update dev E2E assertions to validate manifest/route imports (rather than copied file contents) and drop the unused dependency.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/next/src/builder-deferred.ts Removes step-copy pipeline and switches step route generation to direct imports + legacy cleanup.
packages/next/src/loader.ts Deletes deferred step-copy branches and unifies transform/discovery behavior.
packages/next/src/step-copy-utils.ts Deleted (no longer needed).
packages/next/package.json Drops enhanced-resolve dependency.
pnpm-lock.yaml Removes enhanced-resolve from @workflow/next importer.
packages/core/e2e/dev.test.ts Updates dev E2E checks to assert via manifest + route import specifiers.
.changeset/remove-step-file-copy.md Adds release note for the behavioral change.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1768 to +1775
} | null {
let resolved: string;
try {
resolved = require.resolve('workflow/internal/builtins', {
paths: [this.config.workingDir],
});
} catch {
return null;
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolveResponseBuiltinsStepSource() silently returns null when workflow/internal/builtins cannot be resolved. If the app has an incompatible workflow version (or resolution differs under pnpm), this will build a step route without registering the builtin response steps, likely causing runtime failures when Request/Response builtins are invoked. Consider failing fast with a clear error (e.g., instruct to upgrade workflow) or at least logging a warning and keeping the previous in-repo fallback behavior so builtins are always present.

Suggested change
} | null {
let resolved: string;
try {
resolved = require.resolve('workflow/internal/builtins', {
paths: [this.config.workingDir],
});
} catch {
return null;
} {
let resolved: string;
try {
resolved = require.resolve('workflow/internal/builtins', {
paths: [this.config.workingDir],
});
} catch (error) {
const message =
error instanceof Error ? ` ${error.message}` : '';
throw new Error(
`Unable to resolve "workflow/internal/builtins" from working directory ` +
`"${this.config.workingDir}". Builtin Request/Response workflow steps ` +
`require a compatible "workflow" package to be installed and resolvable ` +
`from the application. Please ensure the app depends on a compatible ` +
`"workflow" version and upgrade it if necessary.${message}`
);

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

@VaguelySerious VaguelySerious left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review: no blocking issues

inputSourceMap: isDeferredStepCopyFile
? deferredSourceMapResult.sourceMap || sourceMap
: sourceMap,
inputSourceMap: sourceMap,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: Note

Directly transforming compiled package sources brings back SWC's external sourcemap-resolution path. Built @workflow/ai files (and others) ship with //# sourceMappingURL=foo.js.map trailers that point at sibling .map files. SWC tries to resolve the URL against the loader's relative filename (e.g. packages/ai/dist/agent/durable-agent.js) — that path doesn't exist on disk, so SWC logs failed to read input source map: failed to find input source map file … once per file per build/dev request.

Verified locally on workbench/nextjs-turbopack:

  • main: only packages/serde/dist/index.js errors (already imported directly).
  • This PR: adds errors for packages/ai/dist/agent/{do-stream-step,durable-agent,stream-text-iterator}.js and packages/ai/dist/providers/mock.js.

The build still succeeds and the workflows run, so this isn't blocking, but it adds noisy red ERROR lines to every workflow request in dev and to every production build that uses such packages. The previous copy mechanism avoided this by attaching an inline base64 source map via createDeferredStepCopyInlineSourceMapComment.

Options:

  • Strip //# sourceMappingURL= trailers from sourceForTransform (or pass inputSourceMap: false) when the file has no inbound sourceMap from a prior loader.
  • Read the sibling .map from disk and pass it as inputSourceMap so SWC can chain it.
  • Set filename to the absolute path here (the SWC plugin can still receive the relative path via a different option) so the sibling .map is resolvable.

await this.createResponseBuiltinsStepFile({
stepsRouteDir,
});
const responseBuiltins = this.resolveResponseBuiltinsStepSource();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review: Nit

The manifest key for built-in response steps changes from app/.well-known/workflow/v1/step/__workflow_step_files__/workflow-response-builtins.ts to packages/workflow/dist/internal/builtins.js. The actual stepId values (__builtin_response_array_buffer, __builtin_response_json, __builtin_response_text) are unchanged, so runtime resolution is unaffected — but anything keying off the manifest path (tooling, tests that grep manifest keys, observability dashboards that surface step source paths) will see the path move. Probably fine, just worth noting in the changeset since the diff isn't immediately obvious.

@TooTallNate TooTallNate marked this pull request as draft April 17, 2026 16:30
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