Skip to content

fix(swc-plugin): rewrite anonymous export default class to const declaration#1601

Merged
TooTallNate merged 9 commits into
mainfrom
fix/export-default-class-anonymous-binding
Apr 3, 2026
Merged

fix(swc-plugin): rewrite anonymous export default class to const declaration#1601
TooTallNate merged 9 commits into
mainfrom
fix/export-default-class-anonymous-binding

Conversation

@TooTallNate
Copy link
Copy Markdown
Member

Summary

  • Fixes potential ReferenceError when export default class { ... } (anonymous) has serde or step methods
  • The generated registration code needs a binding name accessible at module scope, but anonymous default class exports have none
  • Rewrites export default class { ... }const __defaultClass = class __defaultClass { ... }; export default __defaultClass;
  • Also sets current_class_binding_name for named default class exports (export default class Foo { ... }) so registration uses the class name

Stacked on #1599

Details

When an anonymous class is exported as the default export:

export default class {
  static [WORKFLOW_SERIALIZE](inst) { ... }
  static [WORKFLOW_DESERIALIZE](data) { ... }
  async process(input) { "use step"; ... }
}

The SWC plugin generates registration code at module scope that needs to reference the class by name (e.g., registerStepFunction("...", ClassName.prototype["process"])). Without a binding name, the fallback was "AnonymousClass" which doesn't exist as a variable — causing a ReferenceError at runtime.

Fix

Two changes in the SWC plugin:

  1. visit_mut_export_default_decl (DefaultDecl::Class): Detects anonymous classes with serde/step methods, generates a unique name (__defaultClass), sets current_class_binding_name, and defers the rewrite to visit_mut_module_items.

  2. visit_mut_export_default_expr (Expr::Class): Same logic for the expression form (e.g., export default (class { ... })).

  3. visit_mut_module_items: Processes the deferred rewrites — removes the original export default, inserts const __defaultClass = class __defaultClass { ... };, and re-exports with export default __defaultClass;.

Named default class exports (export default class Foo { ... }) don't need rewriting — Foo is already in scope. The fix just sets current_class_binding_name so the transformer uses Foo consistently.

Tests

6 new tests (2 fixtures × 3 modes):

  • export-default-class-anonymous: Anonymous default class with serde + step method → verifies rewrite to const __defaultClass = class __defaultClass { ... }; export default __defaultClass; with correct registration code
  • export-default-class-named: Named default class (export default class MyService { ... }) → verifies no rewrite needed, MyService used for all registrations

144 total SWC tests pass (138 existing + 6 new).

…p/serde entries

When node_modules packages include "sideEffects": false in their
package.json, esbuild drops bare imports from the virtual-entry.js
file. This is incorrect because the SWC compiler transform injects
side-effectful registration code (workflow IDs, step IDs, class
serialization) into these modules.

Fix: return the resolved path alongside sideEffects: true from the
onResolve handler so esbuild uses the plugin's resolution result
instead of re-reading the package.json.
…mlink compatibility

Extract withRealpaths() helper and use it for both normalizedEntriesToBundle
and sideEffectEntries at all three bundle sites. This ensures the
sideEffects override works correctly under pnpm/workspace symlinked
layouts where enhanced-resolve may return realpaths that differ from
the original discovered file paths.
@TooTallNate TooTallNate requested a review from a team as a code owner April 3, 2026 06:26
Copilot AI review requested due to automatic review settings April 3, 2026 06:26
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 3, 2026

🦋 Changeset detected

Latest commit: f0af6af

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

This PR includes changesets to release 17 packages
Name Type
@workflow/swc-plugin Patch
@workflow/builders Patch
@workflow/astro Patch
@workflow/cli Patch
@workflow/nest Patch
@workflow/next Patch
@workflow/nitro Patch
@workflow/rollup Patch
@workflow/sveltekit Patch
@workflow/vite Patch
@workflow/vitest Patch
workflow 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

@vercel
Copy link
Copy Markdown
Contributor

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 3, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 857 0 67 924
✅ 💻 Local Development 830 0 178 1008
✅ 📦 Local Production 830 0 178 1008
✅ 🐘 Local Postgres 830 0 178 1008
✅ 🪟 Windows 76 0 8 84
❌ 🌍 Community Worlds 133 59 24 216
✅ 📋 Other 210 0 42 252
Total 3766 59 675 4500

❌ Failed Tests

🌍 Community Worlds (59 failed)

mongodb (2 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KNAAKPPJ8K5YCEEYPTZY0SSE
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KNAAWB5WH7C4VBMHPRFMNFZ4

redis (2 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KNAAKPPJ8K5YCEEYPTZY0SSE
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KNAAWB5WH7C4VBMHPRFMNFZ4

turso (55 failed):

  • addTenWorkflow | wrun_01KNAAJHK7DNC4AAKEDQ9143KK
  • addTenWorkflow | wrun_01KNAAJHK7DNC4AAKEDQ9143KK
  • wellKnownAgentWorkflow (.well-known/agent) | wrun_01KNAANAJJ387DC9ZH0K90BCJP
  • should work with react rendering in step
  • promiseAllWorkflow | wrun_01KNAAJR5Y4K3XD7V2R9NPZ5HY
  • promiseRaceWorkflow | wrun_01KNAAJWWSWBTBQ2KNSHXTTB9J
  • promiseAnyWorkflow | wrun_01KNAAJZ40GY1YS059F04W9BGW
  • importedStepOnlyWorkflow | wrun_01KNAANQFZB04Q0MJ0CSZ0PHFC
  • hookWorkflow | wrun_01KNAAKB4EP8QP9RVG50T3RC8B
  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KNAAKPPJ8K5YCEEYPTZY0SSE
  • webhookWorkflow | wrun_01KNAAKZE1DFNVBH9YWAVKABV7
  • sleepingWorkflow | wrun_01KNAAM99P8T4M3FJWJN89Z0R5
  • parallelSleepWorkflow | wrun_01KNAAMP6DCKKWG7HBXR81BH0G
  • nullByteWorkflow | wrun_01KNAAMTZXY610E6GV4Y3ZS6Q7
  • workflowAndStepMetadataWorkflow | wrun_01KNAAMXAFJ83Z6HBJ7RVGDHVD
  • fetchWorkflow | wrun_01KNAAR1BPNKMXM4AF7069C4C9
  • promiseRaceStressTestWorkflow | wrun_01KNAAR4RM5GNKXQQRQGRPXNFF
  • 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_01KNAAVP5KZTB5FMHFV92Y4MT9
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KNAAWB5WH7C4VBMHPRFMNFZ4
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_01KNAAX0QJY55D4FKMVYBZKGE9
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_01KNAAXMFTNC3W8JR7Q9CZQDTP
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_01KNAAXX2B365BMQJDSX9MA57H
  • closureVariableWorkflow - nested step functions with closure variables | wrun_01KNAAY2GN8DMKJQKR10C3YECD
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_01KNAAY4PS2SYWYG98ZTNVW354
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_01KNAAYM0PQDWND2RJMM84FQK6
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_01KNAAYSEZSBN4VT0945EG8B2G
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_01KNAAZ017N5XBT9PRBYVMV4KC
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_01KNAAZ6XMWMFHR5KZAARF1FN1
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_01KNAAZE4QGRRXBQMNMN0A163G
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_01KNAAZMN27XKKB8Q7ERR02E0C
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_01KNAAZV5KX3FK5SYN023VJWQD
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_01KNAB0522ZRFYK8VC20BSJT8M
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_01KNAB0EJ0TFFCTNRMF8QM456A
  • cancelRun - cancelling a running workflow | wrun_01KNAB0NB6KHTECAS7XN6W2285
  • cancelRun via CLI - cancelling a running workflow | wrun_01KNAB0YGQFM8X2QMF4N3NCN0J
  • 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_01KNAB1B6P1FEEQWT0SMMJTPFG
  • sleepInLoopWorkflow - sleep inside loop with steps actually delays each iteration | wrun_01KNAB201KPJTC0Z7MK1XEPKHT
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_01KNAB2AE84HE9AGP4JG22PMZ0
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KNAB2GZK9QT2HD96HG52P5HV

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 77 0 7
✅ example 77 0 7
✅ express 77 0 7
✅ fastify 77 0 7
✅ hono 77 0 7
✅ nextjs-turbopack 82 0 2
✅ nextjs-webpack 82 0 2
✅ nitro 77 0 7
✅ nuxt 77 0 7
✅ sveltekit 77 0 7
✅ vite 77 0 7
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 70 0 14
✅ express-stable 70 0 14
✅ fastify-stable 70 0 14
✅ hono-stable 70 0 14
✅ nextjs-turbopack-canary 59 0 25
✅ nextjs-turbopack-stable 76 0 8
✅ nextjs-webpack-canary 59 0 25
✅ nextjs-webpack-stable 76 0 8
✅ nitro-stable 70 0 14
✅ nuxt-stable 70 0 14
✅ sveltekit-stable 70 0 14
✅ vite-stable 70 0 14
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 70 0 14
✅ express-stable 70 0 14
✅ fastify-stable 70 0 14
✅ hono-stable 70 0 14
✅ nextjs-turbopack-canary 59 0 25
✅ nextjs-turbopack-stable 76 0 8
✅ nextjs-webpack-canary 59 0 25
✅ nextjs-webpack-stable 76 0 8
✅ nitro-stable 70 0 14
✅ nuxt-stable 70 0 14
✅ sveltekit-stable 70 0 14
✅ vite-stable 70 0 14
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 70 0 14
✅ express-stable 70 0 14
✅ fastify-stable 70 0 14
✅ hono-stable 70 0 14
✅ nextjs-turbopack-canary 59 0 25
✅ nextjs-turbopack-stable 76 0 8
✅ nextjs-webpack-canary 59 0 25
✅ nextjs-webpack-stable 76 0 8
✅ nitro-stable 70 0 14
✅ nuxt-stable 70 0 14
✅ sveltekit-stable 70 0 14
✅ vite-stable 70 0 14
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 76 0 8
❌ 🌍 Community Worlds
App Passed Failed Skipped
✅ mongodb-dev 5 0 0
❌ mongodb 57 2 8
✅ redis-dev 5 0 0
❌ redis 57 2 8
✅ turso-dev 5 0 0
❌ turso 4 55 8
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 70 0 14
✅ e2e-local-postgres-nest-stable 70 0 14
✅ e2e-local-prod-nest-stable 70 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

Updates the SWC workflow transform to ensure default-exported classes have a module-scope binding name when the plugin needs to emit registration code (serde and/or step/workflow method registrations), avoiding runtime ReferenceError for anonymous export default class { ... } patterns.

Changes:

  • Add transformer logic to defer and rewrite anonymous default class exports into const <name> = class <name> { ... }; export default <name>;.
  • Ensure named default class exports set the binding name so generated registration code consistently references the in-scope identifier.
  • Add/extend fixtures and spec documentation describing the new rewriting behavior.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/swc-plugin-workflow/transform/src/lib.rs Implements deferred rewrite for anonymous default class exports and binding-name tracking for default-exported classes.
packages/swc-plugin-workflow/spec.md Documents anonymous default class export rewriting behavior and expected output.
.changeset/tall-owls-glow.md Publishes a patch changeset describing the fix.
packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-named/input.js Adds fixture input for named export default class MyService { ... } with serde + step.
packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-named/output-step.js Expected step-mode output for named default class export.
packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-named/output-workflow.js Expected workflow-mode output for named default class export.
packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-named/output-client.js Expected client-mode output for named default class export.
packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-anonymous/input.js Adds fixture input for anonymous export default class { ... } with serde + step.
packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-anonymous/output-step.js Expected step-mode output showing const-decl rewrite and correct registrations.
packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-anonymous/output-workflow.js Expected workflow-mode output showing const-decl rewrite and correct registrations.
packages/swc-plugin-workflow/transform/tests/fixture/export-default-class-anonymous/output-client.js Expected client-mode output showing const-decl rewrite and correct registrations.

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

Comment thread packages/swc-plugin-workflow/transform/src/lib.rs
Comment thread packages/swc-plugin-workflow/transform/src/lib.rs
Comment thread packages/swc-plugin-workflow/spec.md Outdated
Comment thread packages/swc-plugin-workflow/transform/src/lib.rs Outdated
…s handling

- Remove dead Expr::Class handler in visit_mut_export_default_expr
  (SWC wraps parenthesized form in Expr::Paren, so it never fires)
- Extract class_needs_binding_rewrite() helper, eliminating duplicated
  detection logic and unnecessary body clones
- Add debug_assert for mutual exclusivity of default_workflow_exports
  and default_class_exports
- Clarify spec.md on self-name behavior difference between serde and
  step-only classes
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 3, 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.038s (-26.6% 🟢) 1.005s (~) 0.967s 10 1.00x
💻 Local Next.js (Turbopack) 0.047s 1.005s 0.958s 10 1.24x
🐘 Postgres Express 0.048s (-28.1% 🟢) 1.010s (~) 0.962s 10 1.26x
🌐 MongoDB Next.js (Turbopack) 0.058s 1.007s 0.949s 10 1.52x
🐘 Postgres Next.js (Turbopack) 0.058s 1.011s 0.954s 10 1.53x
🌐 Redis Next.js (Turbopack) 0.059s 1.005s 0.947s 10 1.54x
🐘 Postgres Nitro 0.065s (+13.0% 🔺) 1.013s (~) 0.948s 10 1.72x
💻 Local Express ⚠️ missing - - - -
workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 1.106s (-3.2%) 2.006s (~) 0.900s 10 1.00x
💻 Local Next.js (Turbopack) 1.118s 2.006s 0.888s 10 1.01x
🐘 Postgres Express 1.120s (-2.0%) 2.011s (~) 0.892s 10 1.01x
🌐 Redis Next.js (Turbopack) 1.129s 2.007s 0.878s 10 1.02x
🐘 Postgres Next.js (Turbopack) 1.146s 2.009s 0.863s 10 1.04x
🐘 Postgres Nitro 1.150s (~) 2.012s (~) 0.862s 10 1.04x
🌐 MongoDB Next.js (Turbopack) 1.309s 2.009s 0.700s 10 1.18x
💻 Local Express ⚠️ missing - - - -
workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 10.651s (-3.3%) 11.025s (-2.9%) 0.374s 3 1.00x
🐘 Postgres Express 10.735s (-1.2%) 11.025s (~) 0.289s 3 1.01x
💻 Local Next.js (Turbopack) 10.769s 11.024s 0.254s 3 1.01x
🐘 Postgres Next.js (Turbopack) 10.805s 11.021s 0.216s 3 1.01x
🌐 Redis Next.js (Turbopack) 10.843s 11.024s 0.181s 3 1.02x
🐘 Postgres Nitro 10.867s (~) 11.022s (~) 0.155s 3 1.02x
🌐 MongoDB Next.js (Turbopack) 12.229s 13.021s 0.792s 3 1.15x
💻 Local Express ⚠️ missing - - - -
workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 14.007s (-4.2%) 14.421s (-4.0%) 0.415s 5 1.00x
💻 Local Nitro 14.239s (-6.4% 🟢) 15.030s (-6.2% 🟢) 0.791s 4 1.02x
🌐 Redis Next.js (Turbopack) 14.308s 15.031s 0.723s 4 1.02x
🐘 Postgres Next.js (Turbopack) 14.365s 15.028s 0.663s 4 1.03x
💻 Local Next.js (Turbopack) 14.512s 15.028s 0.516s 4 1.04x
🐘 Postgres Nitro 14.581s (~) 15.029s (~) 0.449s 4 1.04x
🌐 MongoDB Next.js (Turbopack) 17.791s 18.022s 0.231s 4 1.27x
💻 Local Express ⚠️ missing - - - -
workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 12.950s (-7.7% 🟢) 13.024s (-9.9% 🟢) 0.073s 7 1.00x
🌐 Redis Next.js (Turbopack) 13.499s 14.027s 0.528s 7 1.04x
🐘 Postgres Next.js (Turbopack) 13.952s 14.165s 0.213s 7 1.08x
🐘 Postgres Nitro 14.030s (~) 14.598s (~) 0.567s 7 1.08x
💻 Local Nitro 14.931s (-13.0% 🟢) 15.028s (-16.7% 🟢) 0.097s 6 1.15x
💻 Local Next.js (Turbopack) 15.844s 16.028s 0.184s 6 1.22x
🌐 MongoDB Next.js (Turbopack) 20.425s 21.024s 0.599s 5 1.58x
💻 Local Express ⚠️ missing - - - -
Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.209s (-4.7%) 2.010s (~) 0.802s 15 1.00x
🐘 Postgres Next.js (Turbopack) 1.218s 2.011s 0.793s 15 1.01x
🐘 Postgres Nitro 1.267s (+0.5%) 2.011s (~) 0.743s 15 1.05x
🌐 Redis Next.js (Turbopack) 1.318s 2.007s 0.688s 15 1.09x
💻 Local Nitro 1.467s (-6.3% 🟢) 2.006s (~) 0.539s 15 1.21x
💻 Local Next.js (Turbopack) 1.485s 2.005s 0.521s 15 1.23x
🌐 MongoDB Next.js (Turbopack) 2.138s 3.008s 0.871s 10 1.77x
💻 Local Express ⚠️ missing - - - -
Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 2.308s (-1.5%) 3.010s (~) 0.702s 10 1.00x
🐘 Postgres Nitro 2.335s (~) 3.011s (~) 0.677s 10 1.01x
🐘 Postgres Next.js (Turbopack) 2.385s 3.010s 0.625s 10 1.03x
🌐 Redis Next.js (Turbopack) 2.633s 3.108s 0.476s 10 1.14x
💻 Local Nitro 2.736s (-12.3% 🟢) 3.108s (-20.0% 🟢) 0.372s 10 1.19x
💻 Local Next.js (Turbopack) 2.770s 3.107s 0.337s 10 1.20x
🌐 MongoDB Next.js (Turbopack) 8.470s 9.014s 0.544s 4 3.67x
💻 Local Express ⚠️ missing - - - -
Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 3.408s (-1.8%) 4.013s (~) 0.605s 8 1.00x
🐘 Postgres Nitro 3.452s (-0.6%) 4.012s (~) 0.560s 8 1.01x
🐘 Postgres Next.js (Turbopack) 3.628s 4.012s 0.384s 8 1.06x
🌐 Redis Next.js (Turbopack) 4.197s 5.011s 0.814s 6 1.23x
💻 Local Nitro 6.989s (-16.6% 🟢) 7.416s (-17.9% 🟢) 0.427s 5 2.05x
💻 Local Next.js (Turbopack) 7.048s 7.621s 0.574s 5 2.07x
🌐 MongoDB Next.js (Turbopack) 19.805s 20.026s 0.221s 2 5.81x
💻 Local Express ⚠️ missing - - - -
Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.192s (-5.7% 🟢) 2.010s (~) 0.818s 15 1.00x
🐘 Postgres Next.js (Turbopack) 1.223s 2.010s 0.786s 15 1.03x
🐘 Postgres Nitro 1.253s (~) 2.009s (~) 0.757s 15 1.05x
🌐 Redis Next.js (Turbopack) 1.356s 2.007s 0.651s 15 1.14x
💻 Local Next.js (Turbopack) 1.473s 2.006s 0.533s 15 1.24x
💻 Local Nitro 1.486s (-5.1% 🟢) 2.007s (~) 0.521s 15 1.25x
🌐 MongoDB Next.js (Turbopack) 3.395s 3.758s 0.362s 8 2.85x
💻 Local Express ⚠️ missing - - - -
Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 2.292s (-2.1%) 3.011s (~) 0.719s 10 1.00x
🐘 Postgres Nitro 2.315s (~) 3.012s (~) 0.697s 10 1.01x
🐘 Postgres Next.js (Turbopack) 2.369s 3.009s 0.641s 10 1.03x
🌐 Redis Next.js (Turbopack) 2.581s 3.009s 0.428s 10 1.13x
💻 Local Next.js (Turbopack) 2.872s 3.208s 0.336s 10 1.25x
💻 Local Nitro 2.888s (-10.1% 🟢) 3.108s (-20.0% 🟢) 0.220s 10 1.26x
🌐 MongoDB Next.js (Turbopack) 9.383s 10.012s 0.629s 3 4.09x
💻 Local Express ⚠️ missing - - - -
Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 3.385s (-2.4%) 4.012s (~) 0.627s 8 1.00x
🐘 Postgres Nitro 3.471s (~) 4.013s (~) 0.542s 8 1.03x
🐘 Postgres Next.js (Turbopack) 3.628s 4.015s 0.387s 8 1.07x
🌐 Redis Next.js (Turbopack) 4.189s 4.725s 0.535s 7 1.24x
💻 Local Nitro 7.565s (-15.4% 🟢) 8.017s (-13.6% 🟢) 0.452s 4 2.23x
💻 Local Next.js (Turbopack) 7.734s 8.271s 0.536s 4 2.28x
🌐 MongoDB Next.js (Turbopack) 20.138s 20.527s 0.389s 2 5.95x
💻 Local Express ⚠️ missing - - - -
workflow with 10 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.609s (-28.5% 🟢) 1.007s (~) 0.399s 60 1.00x
💻 Local Nitro 0.692s (-32.5% 🟢) 1.005s (-47.5% 🟢) 0.312s 60 1.14x
🌐 Redis Next.js (Turbopack) 0.733s 1.022s 0.289s 59 1.20x
🐘 Postgres Next.js (Turbopack) 0.766s 1.007s 0.241s 60 1.26x
🐘 Postgres Nitro 0.831s (+0.6%) 1.007s (~) 0.176s 60 1.37x
💻 Local Next.js (Turbopack) 0.833s 1.039s 0.205s 58 1.37x
🌐 MongoDB Next.js (Turbopack) 2.140s 3.008s 0.867s 20 3.52x
💻 Local Express ⚠️ missing - - - -
workflow with 25 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.523s (-24.8% 🟢) 2.031s (-24.4% 🟢) 0.508s 45 1.00x
🌐 Redis Next.js (Turbopack) 1.733s 2.028s 0.296s 45 1.14x
🐘 Postgres Next.js (Turbopack) 1.839s 2.007s 0.168s 45 1.21x
🐘 Postgres Nitro 1.956s (~) 2.228s (+3.5%) 0.272s 41 1.28x
💻 Local Nitro 2.246s (-27.3% 🟢) 3.008s (-25.0% 🟢) 0.762s 30 1.48x
💻 Local Next.js (Turbopack) 2.586s 3.007s 0.422s 30 1.70x
🌐 MongoDB Next.js (Turbopack) 5.292s 6.012s 0.720s 15 3.48x
💻 Local Express ⚠️ missing - - - -
workflow with 50 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 3.071s (-24.3% 🟢) 3.882s (-18.8% 🟢) 0.811s 31 1.00x
🌐 Redis Next.js (Turbopack) 3.447s 4.009s 0.562s 30 1.12x
🐘 Postgres Next.js (Turbopack) 3.777s 4.045s 0.268s 30 1.23x
🐘 Postgres Nitro 4.035s (~) 4.495s (-0.8%) 0.461s 27 1.31x
💻 Local Nitro 7.328s (-22.2% 🟢) 8.016s (-20.0% 🟢) 0.688s 15 2.39x
💻 Local Next.js (Turbopack) 8.460s 9.017s 0.557s 14 2.75x
🌐 MongoDB Next.js (Turbopack) 10.968s 11.659s 0.691s 11 3.57x
💻 Local Express ⚠️ missing - - - -
workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.229s (-17.6% 🟢) 1.008s (~) 0.779s 60 1.00x
🐘 Postgres Next.js (Turbopack) 0.249s 1.008s 0.759s 60 1.09x
🐘 Postgres Nitro 0.280s (+2.6%) 1.008s (~) 0.728s 60 1.22x
🌐 Redis Next.js (Turbopack) 0.313s 1.004s 0.691s 60 1.37x
💻 Local Next.js (Turbopack) 0.548s 1.004s 0.457s 60 2.39x
💻 Local Nitro 0.580s (-15.1% 🟢) 1.004s (-3.4%) 0.425s 60 2.53x
🌐 MongoDB Next.js (Turbopack) 3.163s 3.948s 0.785s 16 13.80x
💻 Local Express ⚠️ missing - - - -
workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.392s (-21.2% 🟢) 1.019s (+1.1%) 0.627s 89 1.00x
🐘 Postgres Next.js (Turbopack) 0.471s 1.007s 0.536s 90 1.20x
🐘 Postgres Nitro 0.496s (+1.5%) 1.008s (~) 0.511s 90 1.27x
🌐 Redis Next.js (Turbopack) 1.180s 2.006s 0.826s 45 3.01x
💻 Local Next.js (Turbopack) 2.467s 2.977s 0.510s 31 6.29x
💻 Local Nitro 2.493s (-1.5%) 3.008s (~) 0.516s 30 6.36x
🌐 MongoDB Next.js (Turbopack) 9.454s 10.016s 0.562s 9 24.10x
💻 Local Express ⚠️ missing - - - -
workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.584s (-26.3% 🟢) 1.008s (~) 0.424s 120 1.00x
🐘 Postgres Next.js (Turbopack) 0.754s 1.016s 0.262s 119 1.29x
🐘 Postgres Nitro 0.811s (+4.8%) 1.019s (+1.0%) 0.208s 118 1.39x
🌐 Redis Next.js (Turbopack) 2.805s 3.085s 0.280s 39 4.80x
💻 Local Next.js (Turbopack) 10.212s 10.942s 0.730s 12 17.48x
💻 Local Nitro 10.642s (-6.2% 🟢) 11.027s (-7.6% 🟢) 0.384s 11 18.22x
🌐 MongoDB Next.js (Turbopack) 19.870s 20.531s 0.661s 6 34.02x
💻 Local Express ⚠️ missing - - - -
Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 0.149s (-31.6% 🟢) 1.003s (~) 0.010s (-13.5% 🟢) 1.016s (~) 0.867s 10 1.00x
🐘 Postgres Express 0.152s (-25.6% 🟢) 0.999s (~) 0.001s (-28.6% 🟢) 1.010s (~) 0.858s 10 1.02x
💻 Local Next.js (Turbopack) 0.172s 1.002s 0.012s 1.018s 0.846s 10 1.15x
🌐 Redis Next.js (Turbopack) 0.182s 1.001s 0.002s 1.008s 0.826s 10 1.22x
🐘 Postgres Next.js (Turbopack) 0.193s 1.002s 0.001s 1.012s 0.820s 10 1.29x
🐘 Postgres Nitro 0.209s (~) 0.997s (~) 0.001s (-13.3% 🟢) 1.013s (~) 0.803s 10 1.41x
🌐 MongoDB Next.js (Turbopack) 0.493s 0.958s 0.001s 1.009s 0.516s 10 3.31x
💻 Local Express ⚠️ missing - - - - -
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.508s 1.001s 0.003s 1.012s 0.504s 60 1.00x
🐘 Postgres Express 0.526s (-14.6% 🟢) 1.005s (~) 0.004s (-9.1% 🟢) 1.023s (~) 0.497s 59 1.04x
💻 Local Nitro 0.583s (-23.3% 🟢) 1.009s (~) 0.008s (-15.2% 🟢) 1.022s (~) 0.439s 59 1.15x
🐘 Postgres Next.js (Turbopack) 0.597s 1.009s 0.004s 1.024s 0.427s 59 1.18x
🐘 Postgres Nitro 0.633s (+4.8%) 1.003s (~) 0.005s (+37.4% 🔺) 1.026s (~) 0.393s 59 1.25x
💻 Local Next.js (Turbopack) 0.638s 1.009s 0.010s 1.023s 0.385s 59 1.26x
🌐 MongoDB Next.js (Turbopack) 1.326s 1.954s 0.003s 2.016s 0.690s 30 2.61x
💻 Local Express ⚠️ missing - - - - -
10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.912s (-6.7% 🟢) 1.069s (-10.8% 🟢) 0.000s (+Infinity% 🔺) 1.089s (-12.0% 🟢) 0.177s 56 1.00x
🐘 Postgres Next.js (Turbopack) 0.913s 1.092s 0.000s 1.102s 0.190s 55 1.00x
🌐 Redis Next.js (Turbopack) 0.919s 1.034s 0.000s 1.039s 0.120s 58 1.01x
🐘 Postgres Nitro 1.022s (+4.8%) 1.356s (+16.5% 🔺) 0.000s (-100.0% 🟢) 1.374s (+15.7% 🔺) 0.352s 44 1.12x
💻 Local Nitro 1.175s (-9.3% 🟢) 2.018s (~) 0.000s (-72.7% 🟢) 2.021s (~) 0.846s 30 1.29x
💻 Local Next.js (Turbopack) 1.217s 2.018s 0.000s 2.020s 0.804s 30 1.33x
🌐 MongoDB Next.js (Turbopack) 2.356s 2.954s 0.000s 3.008s 0.652s 20 2.58x
💻 Local Express ⚠️ missing - - - - -
fan-out fan-in 10 streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.703s (-2.7%) 2.060s (~) 0.000s (+Infinity% 🔺) 2.090s (-0.6%) 0.386s 29 1.00x
🌐 Redis Next.js (Turbopack) 1.732s 2.034s 0.000s 2.040s 0.308s 30 1.02x
🐘 Postgres Next.js (Turbopack) 1.835s 2.145s 0.000s 2.197s 0.362s 28 1.08x
🐘 Postgres Nitro 1.852s (+8.0% 🔺) 2.219s (+5.7% 🔺) 0.000s (+7.4% 🔺) 2.234s (+5.7% 🔺) 0.382s 27 1.09x
💻 Local Next.js (Turbopack) 3.461s 4.030s 0.000s 4.034s 0.573s 15 2.03x
💻 Local Nitro 3.497s (-6.4% 🟢) 4.098s (-3.3%) 0.000s (-63.6% 🟢) 4.103s (-3.2%) 0.606s 15 2.05x
🌐 MongoDB Next.js (Turbopack) 4.421s 4.963s 0.000s 5.012s 0.591s 12 2.60x
💻 Local Express ⚠️ missing - - - - -

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World 🥇 Fastest Framework Wins
💻 Local Nitro 15/21
🐘 Postgres Express 21/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 🐘 Postgres 21/21
Next.js (Turbopack) 🐘 Postgres 10/21
Nitro 🐘 Postgres 14/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

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