Skip to content

Allow synchronous functions to use "use step" directive#1633

Merged
TooTallNate merged 11 commits into
mainfrom
allow-sync-step-functions
Apr 9, 2026
Merged

Allow synchronous functions to use "use step" directive#1633
TooTallNate merged 11 commits into
mainfrom
allow-sync-step-functions

Conversation

@TooTallNate
Copy link
Copy Markdown
Member

Summary

  • Lifts the async function restriction from "use step" in both the SWC compiler plugin and the TypeScript language service plugin
  • Synchronous functions can now use "use step" to strip their Node.js-dependent code from the workflow VM bundle
  • The async restriction is preserved for "use workflow" functions

Motivation

This enables using "use step" as a code-stripping mechanism for synchronous methods (e.g. getReadable() on the Run class) that use Node.js APIs not available in the workflow VM. Previously, "use step" required the function to be async, which was an artificial restriction for this use case.

Changed packages

  • @workflow/swc-plugin — Removed async guards from all step function code paths; updated should_transform_function; updated InvalidExport validation for module-level "use step" files; added sync-step fixture test; cleaned up error test fixtures
  • @workflow/typescript-plugin — Removed error 9002 (sync step function diagnostic); updated tests
  • Updated spec.md error documentation

Test plan

  • 196 Rust SWC plugin tests pass (22 unit + 24 error + 150 fixture, including new sync-step fixture)
  • 95 TypeScript plugin tests pass

…/private

The SWC compiler plugin no longer generates import statements. All step
function registrations and closure variable access are now self-contained
inline IIFEs with zero module dependencies:

- Step registration uses an IIFE that writes to
  globalThis[Symbol.for('@workflow/core//registeredSteps')]
- Closure variable access uses an IIFE that reads from
  globalThis[Symbol.for('WORKFLOW_STEP_CONTEXT_STORAGE')]
- Registrations are placed immediately after each function definition
  instead of being batched at the bottom of the file

This enables 3rd-party node_modules packages to define step functions
without needing the 'workflow' package available at runtime.

Also removes the @workflow/core/private and workflow/internal/private
public subpath exports since they are no longer referenced by generated
code.
Lift the async function restriction from "use step" in both the SWC
compiler plugin and the TypeScript language service plugin. This enables
using "use step" as a mechanism to strip Node.js-dependent code from
the workflow VM bundle without requiring the function to be async.

The async restriction is preserved for "use workflow" functions.

- SWC plugin: removed async guards from all step function code paths,
  updated should_transform_function, updated InvalidExport validation
- TypeScript plugin: removed error 9002 for sync step functions
- Added sync-step fixture test, cleaned up error test fixtures
- Updated spec.md error documentation
Copilot AI review requested due to automatic review settings April 7, 2026 07:45
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 7, 2026

🦋 Changeset detected

Latest commit: e4b8d29

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

This PR includes changesets to release 18 packages
Name Type
@workflow/swc-plugin Minor
@workflow/typescript-plugin Minor
@workflow/astro Patch
@workflow/builders Patch
@workflow/cli Patch
@workflow/nest Patch
@workflow/next Patch
@workflow/nitro Patch
@workflow/rollup Patch
@workflow/sveltekit Patch
workflow Patch
@workflow/vite Patch
@workflow/vitest Patch
@workflow/world-testing Patch
@workflow/nuxt Patch
@workflow/ai Patch
@workflow/core Patch
@workflow/web-shared 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

@TooTallNate TooTallNate requested a review from a team as a code owner April 7, 2026 07:45
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 7, 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 9, 2026 0:24am
example-nextjs-workflow-webpack Ready Ready Preview, Comment Apr 9, 2026 0:24am
example-workflow Ready Ready Preview, Comment Apr 9, 2026 0:24am
workbench-astro-workflow Ready Ready Preview, Comment Apr 9, 2026 0:24am
workbench-express-workflow Ready Ready Preview, Comment Apr 9, 2026 0:24am
workbench-fastify-workflow Ready Ready Preview, Comment Apr 9, 2026 0:24am
workbench-hono-workflow Ready Ready Preview, Comment Apr 9, 2026 0:24am
workbench-nitro-workflow Ready Ready Preview, Comment Apr 9, 2026 0:24am
workbench-nuxt-workflow Ready Ready Preview, Comment Apr 9, 2026 0:24am
workbench-sveltekit-workflow Ready Ready Preview, Comment Apr 9, 2026 0:24am
workbench-vite-workflow Ready Ready Preview, Comment Apr 9, 2026 0:24am
workflow-docs Ready Ready Preview, Comment, Open in v0 Apr 9, 2026 0:24am
workflow-swc-playground Ready Ready Preview, Comment Apr 9, 2026 0:24am

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 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 🥇 Nitro 0.043s (-1.4%) 1.005s (~) 0.963s 10 1.00x
🌐 Redis Next.js (Turbopack) 0.054s 1.005s 0.951s 10 1.27x
🐘 Postgres Nitro 0.058s (-8.7% 🟢) 1.010s (~) 0.952s 10 1.34x
🐘 Postgres Next.js (Turbopack) 0.059s 1.010s 0.951s 10 1.37x
🐘 Postgres Express 0.059s (+1.0%) 1.010s (~) 0.950s 10 1.38x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 0.228s (+9.1% 🔺) 2.214s (+1.9%) 1.986s 10 1.00x
▲ Vercel Nitro 0.249s (+6.6% 🔺) 2.227s (+6.5% 🔺) 1.979s 10 1.09x
▲ Vercel Next.js (Turbopack) 0.747s (+225.1% 🔺) 2.542s (+12.1% 🔺) 1.794s 10 3.27x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 1.124s 2.007s 0.883s 10 1.00x
💻 Local Nitro 1.127s (~) 2.006s (~) 0.879s 10 1.00x
🐘 Postgres Express 1.132s (-1.0%) 2.011s (~) 0.878s 10 1.01x
🐘 Postgres Next.js (Turbopack) 1.139s 2.009s 0.870s 10 1.01x
🐘 Postgres Nitro 1.147s (~) 2.010s (~) 0.862s 10 1.02x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 1.873s (-1.7%) 4.167s (+10.3% 🔺) 2.294s 10 1.00x
▲ Vercel Next.js (Turbopack) 1.940s (-5.8% 🟢) 3.298s (-13.7% 🟢) 1.358s 10 1.04x
▲ Vercel Express 2.322s (+25.7% 🔺) 4.020s (+9.9% 🔺) 1.697s 10 1.24x

🔍 Observability: Nitro | Next.js (Turbopack) | Express

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 10.794s 11.025s 0.231s 3 1.00x
🐘 Postgres Express 10.866s (~) 11.023s (~) 0.157s 3 1.01x
🐘 Postgres Next.js (Turbopack) 10.905s 11.356s 0.452s 3 1.01x
🐘 Postgres Nitro 10.925s (~) 11.024s (~) 0.099s 3 1.01x
💻 Local Nitro 10.926s (~) 11.022s (~) 0.096s 3 1.01x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 16.403s (-13.8% 🟢) 18.223s (-13.1% 🟢) 1.820s 2 1.00x
▲ Vercel Next.js (Turbopack) 16.427s (-4.6%) 17.753s (-8.6% 🟢) 1.326s 2 1.00x
▲ Vercel Nitro 17.053s (~) 18.660s (-5.5% 🟢) 1.607s 2 1.04x

🔍 Observability: Express | Next.js (Turbopack) | Nitro

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 14.456s 15.028s 0.573s 4 1.00x
🐘 Postgres Next.js (Turbopack) 14.512s 15.028s 0.515s 4 1.00x
🐘 Postgres Express 14.546s (~) 15.023s (~) 0.477s 4 1.01x
🐘 Postgres Nitro 14.685s (+0.5%) 15.027s (~) 0.342s 4 1.02x
💻 Local Nitro 14.956s (~) 15.029s (~) 0.073s 4 1.03x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 29.351s (-11.0% 🟢) 31.401s (-10.0% 🟢) 2.050s 2 1.00x
▲ Vercel Next.js (Turbopack) 29.913s (-11.6% 🟢) 31.199s (-13.0% 🟢) 1.287s 2 1.02x
▲ Vercel Express 30.786s (-4.0%) 32.534s (-2.5%) 1.748s 2 1.05x

🔍 Observability: Nitro | Next.js (Turbopack) | Express

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 13.592s 14.027s 0.435s 7 1.00x
🐘 Postgres Next.js (Turbopack) 13.825s 14.166s 0.340s 7 1.02x
🐘 Postgres Express 13.975s (-1.3%) 14.448s (-3.8%) 0.473s 7 1.03x
🐘 Postgres Nitro 14.252s (+0.8%) 15.024s (+1.9%) 0.772s 6 1.05x
💻 Local Nitro 16.674s (+0.7%) 17.031s (~) 0.357s 6 1.23x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 51.717s (-74.9% 🟢) 53.900s (-74.1% 🟢) 2.183s 2 1.00x
▲ Vercel Nitro 53.533s (-74.1% 🟢) 55.369s (-73.5% 🟢) 1.835s 2 1.04x
▲ Vercel Next.js (Turbopack) 54.318s (-10.4% 🟢) 55.810s (-10.8% 🟢) 1.492s 2 1.05x

🔍 Observability: Express | Nitro | 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.230s 2.010s 0.780s 15 1.00x
🐘 Postgres Express 1.259s (~) 2.009s (~) 0.750s 15 1.02x
🐘 Postgres Nitro 1.272s (-1.4%) 2.010s (~) 0.738s 15 1.03x
🌐 Redis Next.js (Turbopack) 1.323s 2.007s 0.684s 15 1.08x
💻 Local Nitro 1.513s (-1.6%) 2.006s (~) 0.494s 15 1.23x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.056s (-25.2% 🟢) 3.711s (-16.8% 🟢) 1.655s 9 1.00x
▲ Vercel Next.js (Turbopack) 2.542s (+3.4%) 3.815s (-15.8% 🟢) 1.273s 8 1.24x
▲ Vercel Nitro 2.624s (+0.6%) 4.290s (-0.8%) 1.666s 7 1.28x

🔍 Observability: Express | Next.js (Turbopack) | Nitro

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 2.312s (-2.8%) 3.007s (~) 0.696s 10 1.00x
🐘 Postgres Nitro 2.327s (-1.1%) 3.008s (~) 0.681s 10 1.01x
🐘 Postgres Next.js (Turbopack) 2.466s 3.010s 0.544s 10 1.07x
🌐 Redis Next.js (Turbopack) 2.582s 3.008s 0.426s 10 1.12x
💻 Local Nitro 3.000s (+2.5%) 3.565s (+14.7% 🔺) 0.565s 9 1.30x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.381s (-30.6% 🟢) 4.033s (-19.9% 🟢) 1.651s 8 1.00x
▲ Vercel Express 2.543s (-16.8% 🟢) 3.976s (-15.0% 🟢) 1.433s 8 1.07x
▲ Vercel Next.js (Turbopack) 2.993s (+5.3% 🔺) 4.436s (-7.3% 🟢) 1.443s 7 1.26x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 3.486s (~) 4.011s (~) 0.525s 8 1.00x
🐘 Postgres Nitro 3.491s (+0.8%) 4.010s (~) 0.519s 8 1.00x
🐘 Postgres Next.js (Turbopack) 3.667s 4.012s 0.345s 8 1.05x
🌐 Redis Next.js (Turbopack) 4.133s 4.869s 0.736s 7 1.19x
💻 Local Nitro 8.373s (+2.3%) 9.023s (~) 0.650s 4 2.40x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.838s (-13.4% 🟢) 4.503s (-8.4% 🟢) 1.665s 7 1.00x
▲ Vercel Express 3.249s (+6.1% 🔺) 4.654s (-3.1%) 1.404s 7 1.15x
▲ Vercel Next.js (Turbopack) 4.099s (+0.9%) 5.650s (-10.0% 🟢) 1.551s 6 1.44x

🔍 Observability: Nitro | Express | 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.219s 2.010s 0.791s 15 1.00x
🐘 Postgres Nitro 1.268s (+0.8%) 2.009s (~) 0.740s 15 1.04x
🐘 Postgres Express 1.275s (+0.8%) 2.007s (~) 0.732s 15 1.05x
🌐 Redis Next.js (Turbopack) 1.365s 2.006s 0.641s 15 1.12x
💻 Local Nitro 1.543s (+0.8%) 2.006s (~) 0.463s 15 1.27x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.086s (-8.5% 🟢) 3.837s (-3.2%) 1.751s 8 1.00x
▲ Vercel Nitro 2.154s (-6.1% 🟢) 3.859s (-2.5%) 1.705s 8 1.03x
▲ Vercel Next.js (Turbopack) 2.260s (-3.5%) 3.500s (-16.1% 🟢) 1.240s 9 1.08x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 2.337s (-0.6%) 3.009s (~) 0.672s 10 1.00x
🐘 Postgres Nitro 2.354s (-0.8%) 3.009s (~) 0.654s 10 1.01x
🐘 Postgres Next.js (Turbopack) 2.377s 3.008s 0.631s 10 1.02x
🌐 Redis Next.js (Turbopack) 2.555s 3.008s 0.453s 10 1.09x
💻 Local Nitro 3.050s (-1.6%) 4.010s (+3.2%) 0.959s 8 1.31x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.435s (-99.2% 🟢) 3.952s (-98.7% 🟢) 1.517s 8 1.00x
▲ Vercel Express 2.492s (-96.8% 🟢) 3.849s (-95.2% 🟢) 1.357s 8 1.02x
▲ Vercel Next.js (Turbopack) 2.658s (-6.0% 🟢) 4.030s (-15.1% 🟢) 1.372s 8 1.09x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 3.471s (~) 4.010s (~) 0.539s 8 1.00x
🐘 Postgres Express 3.475s (~) 4.010s (~) 0.535s 8 1.00x
🐘 Postgres Next.js (Turbopack) 3.649s 4.012s 0.363s 8 1.05x
🌐 Redis Next.js (Turbopack) 4.120s 4.582s 0.462s 7 1.19x
💻 Local Nitro 8.769s (-2.2%) 9.021s (-2.7%) 0.251s 4 2.53x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.123s (-29.9% 🟢) 4.760s (-20.8% 🟢) 1.637s 7 1.00x
▲ Vercel Nitro 3.257s (+8.6% 🔺) 4.834s (+3.1%) 1.576s 7 1.04x
▲ Vercel Next.js (Turbopack) 3.527s (-1.8%) 4.910s (-8.0% 🟢) 1.384s 7 1.13x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 0.699s 1.004s 0.305s 60 1.00x
🐘 Postgres Next.js (Turbopack) 0.769s 1.006s 0.236s 60 1.10x
🐘 Postgres Express 0.808s (-5.2% 🟢) 1.023s (+1.7%) 0.215s 59 1.16x
🐘 Postgres Nitro 0.860s (+4.4%) 1.023s (+1.7%) 0.163s 59 1.23x
💻 Local Nitro 0.978s (-0.7%) 1.057s (-10.5% 🟢) 0.079s 57 1.40x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 8.679s (-4.7%) 10.740s (-4.0%) 2.061s 6 1.00x
▲ Vercel Next.js (Turbopack) 9.102s (-12.0% 🟢) 10.751s (-13.7% 🟢) 1.649s 6 1.05x
▲ Vercel Express 9.131s (-11.4% 🟢) 10.745s (-10.6% 🟢) 1.614s 6 1.05x

🔍 Observability: Nitro | Next.js (Turbopack) | Express

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 1.676s 2.006s 0.329s 45 1.00x
🐘 Postgres Next.js (Turbopack) 1.872s 2.075s 0.203s 44 1.12x
🐘 Postgres Express 1.952s (-5.1% 🟢) 2.174s (-24.6% 🟢) 0.222s 42 1.16x
🐘 Postgres Nitro 2.093s (+6.2% 🔺) 2.945s (+32.3% 🔺) 0.852s 31 1.25x
💻 Local Nitro 3.017s (~) 3.586s (+1.1%) 0.569s 26 1.80x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 27.604s (-1.7%) 29.418s (-2.2%) 1.814s 4 1.00x
▲ Vercel Nitro 28.084s (-3.0%) 30.146s (-1.7%) 2.062s 3 1.02x
▲ Vercel Next.js (Turbopack) 28.799s (-6.5% 🟢) 30.803s (-6.4% 🟢) 2.004s 3 1.04x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 3.383s 4.008s 0.625s 30 1.00x
🐘 Postgres Next.js (Turbopack) 3.737s 4.009s 0.273s 30 1.10x
🐘 Postgres Express 3.949s (-3.0%) 4.147s (-13.8% 🟢) 0.197s 29 1.17x
🐘 Postgres Nitro 4.252s (+2.8%) 4.972s (+1.7%) 0.720s 25 1.26x
💻 Local Nitro 9.279s (+1.0%) 9.942s (~) 0.663s 13 2.74x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 69.222s (-8.7% 🟢) 70.943s (-8.6% 🟢) 1.721s 2 1.00x
▲ Vercel Nitro 70.767s (-3.2%) 73.163s (-2.3%) 2.396s 2 1.02x
▲ Vercel Next.js (Turbopack) 74.888s (-10.2% 🟢) 76.096s (-11.0% 🟢) 1.208s 2 1.08x

🔍 Observability: Express | Nitro | 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.252s 1.007s 0.755s 60 1.00x
🐘 Postgres Express 0.283s (+1.4%) 1.007s (~) 0.724s 60 1.12x
🐘 Postgres Nitro 0.286s (~) 1.007s (~) 0.721s 60 1.13x
🌐 Redis Next.js (Turbopack) 0.294s 1.004s 0.710s 60 1.16x
💻 Local Nitro 0.603s (+1.6%) 1.004s (~) 0.401s 60 2.39x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 1.456s (-23.7% 🟢) 3.016s (-15.2% 🟢) 1.560s 21 1.00x
▲ Vercel Nitro 1.477s (-17.8% 🟢) 2.985s (-16.2% 🟢) 1.507s 21 1.01x
▲ Vercel Next.js (Turbopack) 1.621s (-2.4%) 2.966s (-8.3% 🟢) 1.345s 21 1.11x

🔍 Observability: Express | Nitro | 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.467s 1.006s 0.539s 90 1.00x
🐘 Postgres Express 0.488s (-1.3%) 1.007s (~) 0.519s 90 1.04x
🐘 Postgres Nitro 0.501s (~) 1.007s (~) 0.506s 90 1.07x
🌐 Redis Next.js (Turbopack) 1.234s 2.006s 0.772s 45 2.64x
💻 Local Nitro 2.785s (+8.7% 🔺) 3.183s (+5.8% 🔺) 0.397s 29 5.96x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.526s (-3.3%) 4.332s (~) 1.807s 21 1.00x
▲ Vercel Nitro 2.686s (+8.3% 🔺) 4.386s (+5.1% 🔺) 1.701s 21 1.06x
▲ Vercel Next.js (Turbopack) 3.397s (-4.3%) 4.947s (-3.9%) 1.550s 19 1.34x

🔍 Observability: Express | Nitro | 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.760s 1.014s 0.255s 119 1.00x
🐘 Postgres Express 0.771s (-2.4%) 1.007s (~) 0.236s 120 1.01x
🐘 Postgres Nitro 0.803s (-6.9% 🟢) 1.008s (-5.2% 🟢) 0.205s 120 1.06x
🌐 Redis Next.js (Turbopack) 2.816s 3.032s 0.217s 40 3.71x
💻 Local Nitro 11.426s (+3.3%) 12.031s (+3.1%) 0.605s 10 15.04x
💻 Local Express ⚠️ missing - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 6.156s (-13.3% 🟢) 7.581s (-14.2% 🟢) 1.425s 16 1.00x
▲ Vercel Nitro 6.524s (-5.3% 🟢) 8.084s (-5.9% 🟢) 1.560s 15 1.06x
▲ Vercel Next.js (Turbopack) 32.897s (+425.9% 🔺) 34.290s (+334.1% 🔺) 1.393s 12 5.34x

🔍 Observability: Express | Nitro | 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
🌐 Redis 🥇 Next.js (Turbopack) 0.186s 1.001s 0.002s 1.008s 0.822s 10 1.00x
🐘 Postgres Express 0.202s (-2.1%) 0.994s (-0.5%) 0.002s (+23.1% 🔺) 1.010s (~) 0.809s 10 1.08x
🐘 Postgres Next.js (Turbopack) 0.206s 1.001s 0.028s 1.037s 0.832s 10 1.10x
💻 Local Nitro 0.206s (+1.2%) 1.004s (~) 0.013s (+2.5%) 1.019s (~) 0.812s 10 1.11x
🐘 Postgres Nitro 0.209s (+3.5%) 1.000s (~) 0.001s (-7.7% 🟢) 1.010s (~) 0.801s 10 1.12x
💻 Local Express ⚠️ missing - - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 1.415s (-40.6% 🟢) 2.521s (-33.7% 🟢) 0.694s (+87.6% 🔺) 3.696s (-20.5% 🟢) 2.281s 10 1.00x
▲ Vercel Next.js (Turbopack) 1.443s (-10.1% 🟢) 2.663s (-15.5% 🟢) 0.571s (+82.2% 🔺) 3.613s (-8.6% 🟢) 2.170s 10 1.02x
▲ Vercel Express 1.509s (-13.0% 🟢) 2.868s (-11.8% 🟢) 0.552s (+59.6% 🔺) 3.833s (-4.8%) 2.324s 10 1.07x

🔍 Observability: Nitro | Next.js (Turbopack) | Express

stream pipeline with 5 transform steps (1MB)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 0.481s 1.001s 0.003s 1.011s 0.530s 60 1.00x
🐘 Postgres Next.js (Turbopack) 0.589s 1.009s 0.005s 1.023s 0.434s 59 1.22x
🐘 Postgres Express 0.596s (-4.6%) 1.005s (~) 0.004s (-8.6% 🟢) 1.021s (~) 0.425s 59 1.24x
🐘 Postgres Nitro 0.647s (+4.0%) 1.005s (~) 0.005s (+14.5% 🔺) 1.024s (~) 0.377s 59 1.35x
💻 Local Nitro 0.828s (+13.5% 🔺) 1.012s (~) 0.010s (+1.1%) 1.116s (+9.1% 🔺) 0.288s 54 1.72x
💻 Local Express ⚠️ missing - - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.990s (+5.4% 🔺) 5.204s (-1.5%) 0.315s (-18.0% 🟢) 5.965s (-1.8%) 1.974s 11 1.00x
▲ Vercel Next.js (Turbopack) 4.125s (-2.8%) 5.578s (-3.6%) 0.185s (-52.3% 🟢) 6.124s (-7.7% 🟢) 2.000s 10 1.03x
▲ Vercel Express 5.305s 6.929s 0.393s 7.734s 2.429s 8 1.33x

🔍 Observability: Nitro | Next.js (Turbopack) | Express

10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 0.912s 1.035s 0.000s 1.039s 0.127s 58 1.00x
🐘 Postgres Next.js (Turbopack) 0.946s 1.154s 0.000s 1.171s 0.225s 52 1.04x
🐘 Postgres Express 0.977s (+0.6%) 1.243s (+6.1% 🔺) 0.000s (+59.4% 🔺) 1.257s (+5.1% 🔺) 0.281s 48 1.07x
🐘 Postgres Nitro 0.991s (+3.6%) 1.214s (+5.7% 🔺) 0.000s (-65.3% 🟢) 1.228s (+5.7% 🔺) 0.237s 50 1.09x
💻 Local Nitro 1.244s (~) 2.021s (~) 0.000s (+50.0% 🔺) 2.023s (~) 0.779s 30 1.36x
💻 Local Express ⚠️ missing - - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.787s (+7.0% 🔺) 4.205s (+13.8% 🔺) 0.000s (-85.6% 🟢) 4.643s (+12.6% 🔺) 1.855s 13 1.00x
▲ Vercel Nitro 2.823s (-90.9% 🟢) 4.110s (-87.2% 🟢) 0.001s (+Infinity% 🔺) 4.619s (-85.9% 🟢) 1.796s 13 1.01x
▲ Vercel Next.js (Turbopack) 3.007s (-9.8% 🟢) 4.399s (-5.4% 🟢) 0.000s (-100.0% 🟢) 4.764s (-6.5% 🟢) 1.757s 13 1.08x

🔍 Observability: Express | Nitro | 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
🌐 Redis 🥇 Next.js (Turbopack) 1.666s 2.036s 0.000s 2.039s 0.373s 30 1.00x
🐘 Postgres Express 1.747s (-7.0% 🟢) 2.140s (-6.7% 🟢) 0.000s (+Infinity% 🔺) 2.152s (-6.7% 🟢) 0.405s 28 1.05x
🐘 Postgres Nitro 1.791s (~) 2.134s (~) 0.000s (-3.4%) 2.147s (~) 0.356s 29 1.07x
🐘 Postgres Next.js (Turbopack) 1.805s 2.146s 0.000s 2.192s 0.387s 28 1.08x
💻 Local Nitro 3.632s (+2.5%) 4.232s (+4.9%) 0.001s (~) 4.235s (+4.9%) 0.604s 15 2.18x
💻 Local Express ⚠️ missing - - - - -
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.867s (+2.6%) 5.104s (+4.7%) 0.001s (+900.0% 🔺) 5.538s (+3.8%) 1.671s 12 1.00x
▲ Vercel Nitro 5.456s (+32.2% 🔺) 6.612s (+23.8% 🔺) 0.000s (+144.4% 🔺) 7.089s (+22.2% 🔺) 1.633s 9 1.41x
▲ Vercel Next.js (Turbopack) 5.462s (+36.0% 🔺) 6.548s (+22.8% 🔺) 0.000s (NaN%) 6.966s (+19.3% 🔺) 1.504s 9 1.41x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

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

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 🐘 Postgres 19/21
Next.js (Turbopack) 🌐 Redis 12/21
Nitro 🐘 Postgres 16/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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 890 0 67 957
✅ 💻 Local Development 866 0 178 1044
✅ 📦 Local Production 866 0 178 1044
✅ 🐘 Local Postgres 866 0 178 1044
✅ 🪟 Windows 79 0 8 87
❌ 🌍 Community Worlds 136 65 24 225
✅ 📋 Other 219 0 42 261
Total 3922 65 675 4662

❌ Failed Tests

🌍 Community Worlds (65 failed)

mongodb (4 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KNQT12HK8X4N86DEASFAYTC3
  • webhookWorkflow | wrun_01KNQT1B8CE6EZ1PEED6RR64J3
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KNQT90QM3AQHKNFTTC3T4RSX
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KNQTFBW8QY7AK7XNWV2MJH0N

redis (3 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KNQT12HK8X4N86DEASFAYTC3
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KNQT90QM3AQHKNFTTC3T4RSX
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KNQTFBW8QY7AK7XNWV2MJH0N

turso (58 failed):

  • addTenWorkflow | wrun_01KNQSZWK13GCCQH5P3FH07BKA
  • addTenWorkflow | wrun_01KNQSZWK13GCCQH5P3FH07BKA
  • wellKnownAgentWorkflow (.well-known/agent) | wrun_01KNQT0FT2RNF0HGMMDGWWFG78
  • should work with react rendering in step
  • promiseAllWorkflow | wrun_01KNQT04E56N7KQ56RT376TDRY
  • promiseRaceWorkflow | wrun_01KNQT08Y6VPWJ6HA9XB3ACDVM
  • promiseAnyWorkflow | wrun_01KNQT0B3AWW74S02RSY96DFDY
  • importedStepOnlyWorkflow | wrun_01KNQT0VCPFWJEJR9NRCGH1YE1
  • hookWorkflow | wrun_01KNQT0Q25ZF0230NTFW8TT8FV
  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KNQT12HK8X4N86DEASFAYTC3
  • webhookWorkflow | wrun_01KNQT1B8CE6EZ1PEED6RR64J3
  • sleepingWorkflow | wrun_01KNQT1H85476RXPSMD074B2X0
  • parallelSleepWorkflow | wrun_01KNQT1X4V7GJPN458S4DTNQHZ
  • nullByteWorkflow | wrun_01KNQT20D17XGS9YA969RSNBCX
  • workflowAndStepMetadataWorkflow | wrun_01KNQT22GYCEM9QQ63EYRWYNYJ
  • fetchWorkflow | wrun_01KNQT4QEY4E6RG3M8NABTD1WT
  • promiseRaceStressTestWorkflow | wrun_01KNQT4ST3XHH8WE6DE83AQQ2C
  • error handling error propagation workflow errors nested function calls preserve message and stack trace
  • error handling error propagation workflow errors cross-file imports preserve message and stack trace
  • 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
  • error handling retry behavior regular Error retries until success
  • error handling retry behavior FatalError fails immediately without retries
  • error handling retry behavior RetryableError respects custom retryAfter delay
  • error handling retry behavior maxRetries=0 disables retries
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • error handling not registered WorkflowNotRegisteredError fails the run when workflow does not exist
  • error handling not registered StepNotRegisteredError fails the step but workflow can catch it
  • error handling not registered StepNotRegisteredError fails the run when not caught in workflow
  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_01KNQT8C7D0W9WVRY9DB6M4R6F
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KNQT90QM3AQHKNFTTC3T4RSX
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_01KNQT9N3Q09Z1RWEW56XMCHA3
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_01KNQTA95C42ANFAA89B5TYKMB
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_01KNQTAJX9XKZVZ5CE2TFDDGRD
  • closureVariableWorkflow - nested step functions with closure variables | wrun_01KNQTARF9TMA086QP6MMNFMPD
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_01KNQTATM6MNKY1EC8MNQ9Z5RQ
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_01KNQTBAFJVXA1W3K58JC3MHE3
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_01KNQTBG2A2R586XK4ANBP75KQ
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_01KNQTBPRJW3W53CRJ9A43SX2T
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_01KNQTBXD7ED0HM67PD7H4ER2X
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_01KNQTC2Z4MT98F21MRWEJ9DSB
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_01KNQTC9NDWM216M5JCAQBM90C
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_01KNQTCH1WM9Z1WVTTBGMPMBRN
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_01KNQTCXME3Y7DX14MBYE93H4D
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_01KNQTD5CV0BR9DZAG478KNZWX
  • cancelRun - cancelling a running workflow | wrun_01KNQTDC6Y4PD7004JSH4NQBYC
  • cancelRun via CLI - cancelling a running workflow | wrun_01KNQTDNJ0Z5XAKHDKSY4EEN56
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router
  • hookWithSleepWorkflow - hook payloads delivered correctly with concurrent sleep | wrun_01KNQTE1PJA6ZM5HN105P89GME
  • sleepInLoopWorkflow - sleep inside loop with steps actually delays each iteration | wrun_01KNQTEPC3ZQSTTXA0PZS0GAP7
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_01KNQTF0MXCVDK1YB1PQDYC8YY
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KNQTF7DQSQERG48JV5RK0R6S
  • metadataFromHelperWorkflow - getWorkflowMetadata/getStepMetadata work from module-level helper (#1577) | wrun_01KNQTF9SAC38S1HRGBERBMHNE
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KNQTFBW8QY7AK7XNWV2MJH0N
  • getterStepWorkflow - getter functions with "use step" directive | wrun_01KNQTFF4388KKRERJQVTNB56C

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 80 0 7
✅ example 80 0 7
✅ express 80 0 7
✅ fastify 80 0 7
✅ hono 80 0 7
✅ nextjs-turbopack 85 0 2
✅ nextjs-webpack 85 0 2
✅ nitro 80 0 7
✅ nuxt 80 0 7
✅ sveltekit 80 0 7
✅ vite 80 0 7
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 73 0 14
✅ express-stable 73 0 14
✅ fastify-stable 73 0 14
✅ hono-stable 73 0 14
✅ nextjs-turbopack-canary 62 0 25
✅ nextjs-turbopack-stable 79 0 8
✅ nextjs-webpack-canary 62 0 25
✅ nextjs-webpack-stable 79 0 8
✅ nitro-stable 73 0 14
✅ nuxt-stable 73 0 14
✅ sveltekit-stable 73 0 14
✅ vite-stable 73 0 14
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 73 0 14
✅ express-stable 73 0 14
✅ fastify-stable 73 0 14
✅ hono-stable 73 0 14
✅ nextjs-turbopack-canary 62 0 25
✅ nextjs-turbopack-stable 79 0 8
✅ nextjs-webpack-canary 62 0 25
✅ nextjs-webpack-stable 79 0 8
✅ nitro-stable 73 0 14
✅ nuxt-stable 73 0 14
✅ sveltekit-stable 73 0 14
✅ vite-stable 73 0 14
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 73 0 14
✅ express-stable 73 0 14
✅ fastify-stable 73 0 14
✅ hono-stable 73 0 14
✅ nextjs-turbopack-canary 62 0 25
✅ nextjs-turbopack-stable 79 0 8
✅ nextjs-webpack-canary 62 0 25
✅ nextjs-webpack-stable 79 0 8
✅ nitro-stable 73 0 14
✅ nuxt-stable 73 0 14
✅ sveltekit-stable 73 0 14
✅ vite-stable 73 0 14
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 79 0 8
❌ 🌍 Community Worlds
App Passed Failed Skipped
✅ mongodb-dev 5 0 0
❌ mongodb 58 4 8
✅ redis-dev 5 0 0
❌ redis 59 3 8
✅ turso-dev 5 0 0
❌ turso 4 58 8
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 73 0 14
✅ e2e-local-postgres-nest-stable 73 0 14
✅ e2e-local-prod-nest-stable 73 0 14

📋 View full workflow run

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

This PR removes the “step functions must be async” restriction across the SWC transform and the TypeScript language service plugin, allowing synchronous functions to use the "use step" directive as a code-stripping mechanism while preserving the async requirement for "use workflow".

Changes:

  • TypeScript plugin: removes diagnostic error 9002 for synchronous step functions and updates tests accordingly.
  • SWC plugin: lifts async-only checks from "use step" transformation paths and adds a new sync-step fixture to verify output across workflow/step/client modes.
  • Documentation + release: updates SWC plugin spec validation table and adds a changeset for patch releases.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/typescript-plugin/src/diagnostics.ts Removes async validation for "use step" functions in custom diagnostics.
packages/typescript-plugin/src/diagnostics.test.ts Updates tests to assert no error 9002 for sync step functions.
packages/swc-plugin-workflow/transform/tests/fixture/sync-step/input.js Adds fixture input covering sync function/arrow/object-method step directives.
packages/swc-plugin-workflow/transform/tests/fixture/sync-step/output-workflow.js Expected workflow-mode output for sync-step fixture (initializer exports).
packages/swc-plugin-workflow/transform/tests/fixture/sync-step/output-step.js Expected step-mode output for sync-step fixture (registration + preserved bodies).
packages/swc-plugin-workflow/transform/tests/fixture/sync-step/output-client.js Expected client-mode output for sync-step fixture (stepId annotations).
packages/swc-plugin-workflow/transform/tests/errors/non-async-functions/input.js Updates error fixture to only cover non-async "use workflow" cases.
packages/swc-plugin-workflow/transform/tests/errors/non-async-functions/output-workflow.js Updates expected workflow-mode output after removing step async restriction.
packages/swc-plugin-workflow/transform/tests/errors/non-async-functions/output-workflow.stderr Updates expected stderr to reflect only "use workflow" async errors.
packages/swc-plugin-workflow/transform/tests/errors/non-async-functions/output-step.js Updates expected step-mode output after removing step async restriction.
packages/swc-plugin-workflow/transform/tests/errors/non-async-functions/output-step.stderr Updates expected stderr to reflect only "use workflow" async errors.
packages/swc-plugin-workflow/transform/tests/errors/non-async-functions/output-client.js Updates expected client-mode output after removing step async restriction.
packages/swc-plugin-workflow/transform/tests/errors/non-async-functions/output-client.stderr Updates expected stderr to reflect only "use workflow" async errors.
packages/swc-plugin-workflow/transform/tests/errors/invalid-exports/input.js Updates invalid-export fixture to allow sync function exports in "use step" files.
packages/swc-plugin-workflow/transform/tests/errors/invalid-exports/output-workflow.js Expected workflow-mode output reflecting sync function export allowance.
packages/swc-plugin-workflow/transform/tests/errors/invalid-exports/output-workflow.stderr Expected stderr after invalid-export fixture adjustments.
packages/swc-plugin-workflow/transform/tests/errors/invalid-exports/output-step.js Expected step-mode output registering sync exported steps.
packages/swc-plugin-workflow/transform/tests/errors/invalid-exports/output-step.stderr Expected stderr after invalid-export fixture adjustments.
packages/swc-plugin-workflow/transform/tests/errors/invalid-exports/output-client.js Expected client-mode output with stepId for sync exported steps.
packages/swc-plugin-workflow/transform/tests/errors/invalid-exports/output-client.stderr Expected stderr after invalid-export fixture adjustments.
packages/swc-plugin-workflow/transform/src/lib.rs Removes async guards for "use step" transformations; keeps "use workflow" async enforcement in relevant paths.
packages/swc-plugin-workflow/spec.md Updates validation error documentation to reflect sync "use step" allowance.
.changeset/allow-sync-step-functions.md Adds patch changeset entries for SWC + TypeScript plugins.

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

Comment thread packages/swc-plugin-workflow/transform/src/lib.rs Outdated
Comment thread packages/swc-plugin-workflow/transform/src/lib.rs Outdated
…row behavior

- Remove stale 'Collect registration calls' comment from StepTransform
- Fix 'everyhwere' typo in e2e test comments
- Closure vars IIFE now throws when called outside a step function
  context, matching the original __private_getClosureVars behavior
…xport message

- Remove the no-op validate_async_function method and unwrap all callers
- InvalidExport message now says 'Only functions can be exported' for
  'use step' files, vs 'Only async functions' for 'use workflow' files
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: blocking issues found

Comment thread packages/swc-plugin-workflow/transform/src/lib.rs Outdated
Comment thread packages/swc-plugin-workflow/transform/src/lib.rs
Comment thread packages/typescript-plugin/src/diagnostics.ts Outdated
Comment thread packages/swc-plugin-workflow/spec.md Outdated
Base automatically changed from inline-step-registration to main April 8, 2026 23:58
…ethod async check

- Remove validate_async_function() entirely and unwrap all 8 call sites
  (the function was a no-op that always returned true)
- Fix static class method handler to only check async for 'use workflow'
  (was still blocking sync step functions on static methods)
- Remove no-op checkStepFunction from TypeScript plugin
- Regenerate test fixture outputs
Also bump changeset from patch to minor (new feature, not bug fix)
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

Copilot reviewed 22 out of 22 changed files in this pull request and generated 4 comments.


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

Comment on lines +4057 to 4058
// Previously validated that step functions must be async. The restriction

Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

There’s a dangling/incomplete comment here (ends mid-sentence) left over from removing the async-only validation. It should either be completed to explain the new behavior or removed to avoid confusion when maintaining this transform logic.

Suggested change
// Previously validated that step functions must be async. The restriction

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

Choose a reason for hiding this comment

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

Fixed in #1664 — removed the dangling incomplete comment entirely.

Comment on lines +6054 to +6057
// Validate exports if we have a file-level workflow directive.
// Step files allow any exports (sync or async), but workflow files
// require exported functions to be async.
if self.has_file_workflow_directive {
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

visit_mut_module_items no longer validates exports for module-level 'use step' files, which means non-function exports (constants/classes/re-exports) are now allowed even though they won’t be code-stripped and can easily pull Node-only code into the workflow/client bundles. Consider adding a separate validation branch for has_file_step_directive that allows sync or async functions but rejects non-function exports/re-exports, with an error message that matches that rule.

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

Choose a reason for hiding this comment

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

Fixed in #1664 — added export validation for has_file_step_directive that allows sync/async function exports but rejects non-function exports (constants, classes, re-exports). Error message is "Only functions can be exported from a \"use step\" file".

Comment on lines +3 to 8
// These should all error - not functions
export const value = 42;
export function syncFunc() {
return 'not allowed';
}
export class MyClass {
method() {}
}
export * from './other';
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The header comment says these exports “should all error”, but the expected stderr fixtures for this test case were removed and the outputs keep these exports. Please update the comment (and potentially relocate/rename this fixture since it no longer asserts an error) so the test intent matches the actual expected behavior.

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

Choose a reason for hiding this comment

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

Fixed in #1664 — the comment is now correct again since export validation for step files has been restored. The const value, class MyClass, and export * all produce errors; the sync and async function exports pass.

"@workflow/typescript-plugin": minor
---

Allow synchronous functions to use `"use step"` directive. This enables using `"use step"` as a mechanism to strip Node.js-dependent code from the workflow VM bundle without requiring the function to be async.
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The PR description mentions that packages/swc-plugin-workflow/spec.md error documentation was updated, but this PR diff doesn’t include any changes to that spec file. Please ensure the spec is updated to reflect that "use step" can be used on sync functions while "use workflow" remains async-only (and adjust any validation/export rules accordingly).

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

Choose a reason for hiding this comment

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

Fixed in #1664 — updated spec.md error table (split Non-async into workflow-only, added step export validation row) and supported function forms (added sync variants for all step function forms).

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