Skip to content

fix(swc-plugin): count destructuring-default references in DCE usage analysis#2398

Open
pranaygp wants to merge 2 commits into
mainfrom
pgp/swc-plugin-fix
Open

fix(swc-plugin): count destructuring-default references in DCE usage analysis#2398
pranaygp wants to merge 2 commits into
mainfrom
pgp/swc-plugin-fix

Conversation

@pranaygp

Copy link
Copy Markdown
Contributor

Fixes #2396.

Problem

The SWC plugin's dead-code-elimination (DCE) usage analysis did not count identifier references that appear as default values inside a destructuring pattern (e.g. const { ttl = TTL } = options;). When a module-scope const was referenced only through such a destructuring default, the DCE pass treated it as unused and stripped it from the emitted bundle — while keeping the code that referenced it. The result was a runtime ReferenceError: <const> is not defined when the default fired.

This is mode-independent (reproduces in both step and workflow mode) and class-independent (a plain top-level function exhibits it too). It shipped as a worked-around bug in the kill-switch pattern (#1858) and remains latent in workbench/vitest/workflows/cookbook/distributed-abort-controller.ts.

Root cause

In ComprehensiveUsageCollector::visit_mut_var_declarator, the collector visited only the initializer and deliberately skipped the entire var_decl.name pattern (to avoid marking the binding name as "used"). For destructuring patterns, the default-value initializer expressions live inside var_decl.name — so references like the TTL in { ttl = TTL } were never added to used_identifiers, and remove_dead_code then pruned the still-referenced declaration.

Fix

Added visit_pat_default_initializers, which walks a binding pattern and visits only the default-value initializer expressions (and computed keys) — ObjectPatProp::KeyValue values, ObjectPatProp::Assign.value, AssignPat.right, array element defaults, rest args — while still not marking the binding names themselves as used. visit_mut_var_declarator now calls it on var_decl.name.

Function-parameter destructuring defaults were already covered: visit_mut_fn_decl visits params in full (confirmed by the existing default-parameter-usage fixture), so no change was needed there.

Tests

  • New fixture destructuring-default-references-module-const/ asserts that two module-scope consts referenced only via destructuring defaults — one inside a class static method, one inside a plain exported function — survive in both step and workflow mode. A third, genuinely-unused const is still stripped, confirming DCE is not over-broadened.
  • Verified the fixture fails on main (consts stripped) and passes with the fix.
  • Full cargo test -p swc_workflow suite green (128 fixture tests + error tests).

Spec

Updated packages/swc-plugin-workflow/spec.md (both the step-mode and workflow-mode DCE sections) to note that a reference counts even when it appears only inside a destructuring-default initializer.

Relation to prior fixes

Same symptom class as #1944 (DCE removing a declaration that surviving code references) but a distinct root cause: #1944 was about DCE ordering relative to step hoisting; this is about the usage collector never traversing destructuring-default initializers at all, and reproduces with no nested/hoisted steps involved.

🤖 Generated with Claude Code

…analysis

The DCE usage collector skipped the entire variable name pattern when
visiting a `VarDeclarator` (to avoid marking the binding name as "used").
But default-value initializers inside destructuring patterns live in that
pattern — e.g. the `TTL` in `const { ttl = TTL } = options;` — so those
references were invisible to the collector. A module-scope `const`
referenced only through such a default was treated as unused and stripped,
while the surviving code kept reading it, producing a runtime
`ReferenceError` when the default fired.

Traverse the default-value initializer expressions (and computed keys)
within destructuring patterns while still not marking the binding names
themselves, so the referenced declaration is preserved. Function-parameter
defaults were already covered (params are visited in full).

Fixes #2396.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 13, 2026 06:29
@pranaygp pranaygp requested a review from a team as a code owner June 13, 2026 06:29
@changeset-bot

changeset-bot Bot commented Jun 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5f05ea6

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/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/core Patch
@workflow/web-shared Patch
@workflow/web 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

vercel Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

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 Jun 14, 2026 8:14am
example-nextjs-workflow-webpack Ready Ready Preview, Comment Jun 14, 2026 8:14am
example-workflow Ready Ready Preview, Comment Jun 14, 2026 8:14am
workbench-astro-workflow Ready Ready Preview, Comment Jun 14, 2026 8:14am
workbench-express-workflow Ready Ready Preview, Comment Jun 14, 2026 8:14am
workbench-fastify-workflow Ready Ready Preview, Comment Jun 14, 2026 8:14am
workbench-hono-workflow Ready Ready Preview, Comment Jun 14, 2026 8:14am
workbench-nitro-workflow Ready Ready Preview, Comment Jun 14, 2026 8:14am
workbench-nuxt-workflow Ready Ready Preview, Comment Jun 14, 2026 8:14am
workbench-sveltekit-workflow Ready Ready Preview, Comment Jun 14, 2026 8:14am
workbench-tanstack-start-workflow Ready Ready Preview, Comment Jun 14, 2026 8:14am
workbench-vite-workflow Ready Ready Preview, Comment Jun 14, 2026 8:14am
workflow-docs Ready Ready Preview, Comment, Open in v0 Jun 14, 2026 8:14am
workflow-swc-playground Ready Ready Preview, Comment Jun 14, 2026 8:14am
workflow-tarballs Ready Ready Preview, Comment Jun 14, 2026 8:14am
workflow-web Ready Ready Preview, Comment Jun 14, 2026 8:14am

@github-actions

github-actions Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

📊 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.044s (-7.9% 🟢) 1.006s (~) 0.962s 10 1.00x
💻 Local Express 0.044s (+4.5%) 1.006s (~) 0.962s 10 1.01x
💻 Local Next.js (Turbopack) 0.059s (-4.4%) 1.006s (~) 0.947s 10 1.34x
🐘 Postgres Express 0.062s (+1.8%) 1.013s (~) 0.951s 10 1.40x
🐘 Postgres Nitro 0.065s (~) 1.013s (~) 0.948s 10 1.47x
🐘 Postgres Next.js (Turbopack) 0.072s (+8.5% 🔺) 1.011s (~) 0.939s 10 1.65x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 0.297s (+13.1% 🔺) 2.155s (-8.6% 🟢) 1.858s 10 1.00x
▲ Vercel Express 0.310s (-3.2%) 2.213s (-3.3%) 1.903s 10 1.04x
▲ Vercel Next.js (Turbopack) 0.328s (-18.4% 🟢) 2.508s (-1.6%) 2.180s 10 1.10x

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

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 1.094s (-0.6%) 2.006s (~) 0.912s 10 1.00x
💻 Local Express 1.095s (-0.6%) 2.007s (~) 0.912s 10 1.00x
🐘 Postgres Nitro 1.110s (~) 2.009s (~) 0.898s 10 1.01x
🐘 Postgres Express 1.113s (+0.6%) 2.010s (~) 0.897s 10 1.02x
💻 Local Next.js (Turbopack) 1.129s (-1.6%) 2.006s (~) 0.877s 10 1.03x
🐘 Postgres Next.js (Turbopack) 1.157s (~) 2.009s (~) 0.851s 10 1.06x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 1.779s (+6.4% 🔺) 3.870s (+5.4% 🔺) 2.091s 10 1.00x
▲ Vercel Next.js (Turbopack) 2.018s (+22.4% 🔺) 4.615s (+20.5% 🔺) 2.598s 10 1.13x
▲ Vercel Nitro 2.057s (+25.6% 🔺) 3.607s (-5.0% 🟢) 1.550s 10 1.16x

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

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 10.543s (~) 11.023s (~) 0.481s 3 1.00x
🐘 Postgres Nitro 10.565s (~) 11.015s (~) 0.450s 3 1.00x
💻 Local Express 10.574s (~) 11.021s (~) 0.447s 3 1.00x
🐘 Postgres Express 10.592s (~) 11.022s (~) 0.430s 3 1.00x
🐘 Postgres Next.js (Turbopack) 10.849s (-0.9%) 11.018s (-3.0%) 0.169s 3 1.03x
💻 Local Next.js (Turbopack) 10.889s (+1.0%) 11.357s (+3.0%) 0.468s 3 1.03x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 14.130s (-7.6% 🟢) 15.562s (-10.4% 🟢) 1.432s 2 1.00x
▲ Vercel Express 14.940s (+5.6% 🔺) 17.298s (+11.1% 🔺) 2.358s 2 1.06x
▲ Vercel Nitro 15.024s (-39.4% 🟢) 16.261s (-37.8% 🟢) 1.237s 2 1.06x

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

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 13.768s (~) 14.028s (~) 0.260s 5 1.00x
💻 Local Express 13.774s (~) 14.027s (~) 0.253s 5 1.00x
🐘 Postgres Nitro 13.810s (-0.5%) 14.025s (-1.4%) 0.215s 5 1.00x
🐘 Postgres Express 13.853s (+0.6%) 14.020s (~) 0.168s 5 1.01x
🐘 Postgres Next.js (Turbopack) 14.425s (-2.3%) 15.015s (-1.7%) 0.590s 4 1.05x
💻 Local Next.js (Turbopack) 14.493s (~) 15.029s (~) 0.536s 4 1.05x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 23.274s (+11.6% 🔺) 25.616s (+12.6% 🔺) 2.342s 3 1.00x
▲ Vercel Nitro 23.759s (+12.7% 🔺) 25.782s (+12.3% 🔺) 2.023s 3 1.02x
▲ Vercel Express 24.217s (+7.5% 🔺) 26.362s (+8.0% 🔺) 2.145s 3 1.04x

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

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 12.509s (-0.6%) 13.025s (~) 0.515s 7 1.00x
🐘 Postgres Express 12.520s (+0.5%) 13.022s (~) 0.502s 7 1.00x
💻 Local Express 12.547s (+0.5%) 13.026s (~) 0.479s 7 1.00x
🐘 Postgres Nitro 12.707s (+1.5%) 13.018s (~) 0.310s 7 1.02x
💻 Local Next.js (Turbopack) 13.659s (~) 14.027s (~) 0.367s 7 1.09x
🐘 Postgres Next.js (Turbopack) 13.736s (-1.2%) 14.015s (-2.0%) 0.279s 7 1.10x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 34.846s (-8.6% 🟢) 37.134s (-6.2% 🟢) 2.288s 3 1.00x
▲ Vercel Next.js (Turbopack) 35.119s (+15.5% 🔺) 37.720s (+15.9% 🔺) 2.602s 3 1.01x
▲ Vercel Nitro 35.701s (+18.9% 🔺) 37.525s (+16.2% 🔺) 1.824s 3 1.02x

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

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 1.200s (+0.8%) 2.007s (~) 0.807s 15 1.00x
💻 Local Express 1.200s (-2.6%) 2.006s (~) 0.806s 15 1.00x
🐘 Postgres Nitro 1.206s (~) 2.009s (~) 0.803s 15 1.00x
🐘 Postgres Express 1.221s (+1.1%) 2.076s (+3.4%) 0.855s 15 1.02x
🐘 Postgres Next.js (Turbopack) 1.289s (+1.3%) 2.007s (~) 0.718s 15 1.07x
💻 Local Next.js (Turbopack) 1.316s (+0.5%) 2.006s (~) 0.690s 15 1.10x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.769s (+13.7% 🔺) 3.945s (-2.5%) 1.176s 8 1.00x
▲ Vercel Express 3.013s (-34.0% 🟢) 4.945s (-17.7% 🟢) 1.931s 7 1.09x
▲ Vercel Next.js (Turbopack) 4.070s (+53.1% 🔺) 5.958s (+36.1% 🔺) 1.888s 6 1.47x

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

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.380s (+1.5%) 2.509s (+4.8%) 1.129s 12 1.00x
🐘 Postgres Nitro 1.413s (+2.1%) 2.315s (-3.3%) 0.902s 13 1.02x
🐘 Postgres Next.js (Turbopack) 1.608s (+2.2%) 2.223s (-3.1%) 0.614s 14 1.17x
💻 Local Nitro 1.755s (-10.6% 🟢) 2.006s (-13.4% 🟢) 0.252s 15 1.27x
💻 Local Next.js (Turbopack) 1.805s (+1.9%) 2.150s (+7.1% 🔺) 0.345s 14 1.31x
💻 Local Express 2.013s (+24.0% 🔺) 2.392s (+19.3% 🔺) 0.379s 13 1.46x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 3.446s (+10.9% 🔺) 5.800s (+11.9% 🔺) 2.354s 6 1.00x
▲ Vercel Nitro 3.514s (-2.4%) 5.187s (-4.8%) 1.673s 6 1.02x
▲ Vercel Express 3.873s (+7.7% 🔺) 5.547s (+9.0% 🔺) 1.674s 6 1.12x

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

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.642s (+3.7%) 3.888s (-3.1%) 2.246s 8 1.00x
🐘 Postgres Nitro 1.969s (+22.3% 🔺) 4.143s (~) 2.175s 8 1.20x
🐘 Postgres Next.js (Turbopack) 2.738s (-16.4% 🟢) 3.762s (-6.3% 🟢) 1.023s 8 1.67x
💻 Local Nitro 4.291s (-22.0% 🟢) 5.014s (-16.6% 🟢) 0.723s 6 2.61x
💻 Local Next.js (Turbopack) 4.951s (+2.2%) 5.512s (+3.1%) 0.561s 6 3.02x
💻 Local Express 5.465s (+29.0% 🔺) 5.848s (+23.8% 🔺) 0.383s 6 3.33x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.858s (-15.0% 🟢) 5.636s (-10.5% 🟢) 1.778s 6 1.00x
▲ Vercel Express 5.822s (+38.5% 🔺) 8.082s (+33.4% 🔺) 2.260s 4 1.51x
▲ Vercel Next.js (Turbopack) 6.206s (+19.4% 🔺) 8.102s (+15.9% 🔺) 1.896s 4 1.61x

🔍 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 🥇 Express 1.213s (+1.1%) 2.008s (~) 0.794s 15 1.00x
🐘 Postgres Nitro 1.227s (+0.9%) 2.007s (~) 0.780s 15 1.01x
🐘 Postgres Next.js (Turbopack) 1.282s (~) 2.007s (~) 0.725s 15 1.06x
💻 Local Next.js (Turbopack) 1.374s (-0.6%) 2.006s (~) 0.632s 15 1.13x
💻 Local Express 1.574s (+4.2%) 2.007s (~) 0.433s 15 1.30x
💻 Local Nitro 1.623s (-2.0%) 2.007s (~) 0.384s 15 1.34x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.954s (-14.9% 🟢) 5.035s (-0.8%) 2.081s 6 1.00x
▲ Vercel Express 3.148s (+30.1% 🔺) 5.166s (+24.4% 🔺) 2.018s 6 1.07x
▲ Vercel Nitro 3.756s (+27.6% 🔺) 5.562s (+14.3% 🔺) 1.806s 6 1.27x

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

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.360s (+0.6%) 2.738s (+5.6% 🔺) 1.378s 11 1.00x
🐘 Postgres Express 1.379s (-2.4%) 2.225s (-4.0%) 0.846s 14 1.01x
🐘 Postgres Next.js (Turbopack) 1.491s (-8.1% 🟢) 2.074s (-9.6% 🟢) 0.583s 15 1.10x
💻 Local Nitro 1.946s (-8.8% 🟢) 2.392s (-7.7% 🟢) 0.446s 13 1.43x
💻 Local Next.js (Turbopack) 2.083s (+0.6%) 2.735s (-3.2%) 0.652s 11 1.53x
💻 Local Express 2.265s (+29.7% 🔺) 2.737s (+27.3% 🔺) 0.472s 11 1.67x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.154s (+20.1% 🔺) 4.555s (+12.4% 🔺) 1.402s 7 1.00x
▲ Vercel Next.js (Turbopack) 3.517s (+34.1% 🔺) 5.413s (+25.6% 🔺) 1.896s 6 1.12x
▲ Vercel Express 3.635s (+20.5% 🔺) 5.553s (+24.6% 🔺) 1.917s 6 1.15x

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

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.610s (+2.6%) 4.011s (~) 2.400s 8 1.00x
🐘 Postgres Nitro 1.638s (+1.7%) 4.297s (+14.2% 🔺) 2.659s 7 1.02x
🐘 Postgres Next.js (Turbopack) 3.050s (+11.6% 🔺) 3.763s (+8.8% 🔺) 0.713s 8 1.89x
💻 Local Next.js (Turbopack) 4.777s (-2.8%) 5.346s (+3.1%) 0.569s 6 2.97x
💻 Local Express 5.845s (+24.7% 🔺) 6.414s (+23.9% 🔺) 0.569s 5 3.63x
💻 Local Nitro 5.885s (+0.6%) 6.617s (+3.2%) 0.732s 5 3.65x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 4.463s (~) 5.952s (-1.2%) 1.489s 6 1.00x
▲ Vercel Next.js (Turbopack) 4.823s (+36.5% 🔺) 6.520s (+20.0% 🔺) 1.697s 5 1.08x
▲ Vercel Express 4.902s (+41.8% 🔺) 6.892s (+34.8% 🔺) 1.990s 5 1.10x

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

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.563s (~) 1.006s (~) 0.443s 60 1.00x
🐘 Postgres Nitro 0.614s (+6.9% 🔺) 1.023s (~) 0.409s 59 1.09x
💻 Local Express 0.626s (~) 1.022s (~) 0.396s 59 1.11x
💻 Local Nitro 0.715s (+13.8% 🔺) 1.058s (+5.2% 🔺) 0.343s 57 1.27x
🐘 Postgres Next.js (Turbopack) 0.832s (-1.9%) 1.023s (-3.4%) 0.191s 59 1.48x
💻 Local Next.js (Turbopack) 0.869s (-1.2%) 1.021s (-1.7%) 0.152s 59 1.54x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 7.405s (+34.3% 🔺) 9.440s (+30.1% 🔺) 2.035s 7 1.00x
▲ Vercel Next.js (Turbopack) 7.440s (+21.4% 🔺) 9.546s (+22.5% 🔺) 2.106s 7 1.00x
▲ Vercel Nitro 8.356s (+63.2% 🔺) 9.919s (+45.3% 🔺) 1.562s 7 1.13x

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

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.386s (+2.3%) 2.029s (~) 0.643s 45 1.00x
🐘 Postgres Nitro 1.489s (+9.9% 🔺) 2.076s (+3.4%) 0.587s 44 1.07x
💻 Local Express 1.550s (+3.8%) 2.006s (~) 0.456s 45 1.12x
💻 Local Nitro 1.553s (~) 2.007s (~) 0.454s 45 1.12x
🐘 Postgres Next.js (Turbopack) 1.947s (-2.3%) 2.151s (-10.5% 🟢) 0.204s 42 1.40x
💻 Local Next.js (Turbopack) 2.141s (-0.9%) 3.007s (+1.1%) 0.867s 30 1.54x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 15.850s (+3.6%) 18.095s (+4.7%) 2.244s 6 1.00x
▲ Vercel Nitro 17.193s (+18.2% 🔺) 18.559s (+12.9% 🔺) 1.365s 5 1.08x
▲ Vercel Next.js (Turbopack) 17.738s (+7.9% 🔺) 20.031s (+8.1% 🔺) 2.294s 5 1.12x

🔍 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
🐘 Postgres 🥇 Express 2.628s (-3.5%) 3.008s (-3.3%) 0.380s 40 1.00x
🐘 Postgres Nitro 2.830s (+4.8%) 3.193s (+3.5%) 0.363s 38 1.08x
💻 Local Express 3.322s (+4.2%) 4.010s (+0.8%) 0.688s 30 1.26x
💻 Local Nitro 3.436s (+1.8%) 4.010s (~) 0.574s 30 1.31x
🐘 Postgres Next.js (Turbopack) 3.851s (~) 4.042s (~) 0.192s 30 1.47x
💻 Local Next.js (Turbopack) 4.342s (+0.8%) 5.010s (~) 0.668s 24 1.65x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 28.277s (+4.7%) 30.346s (+4.9%) 2.069s 4 1.00x
▲ Vercel Express 29.279s (+10.0% 🔺) 32.091s (+12.5% 🔺) 2.812s 4 1.04x
▲ Vercel Next.js (Turbopack) 33.528s (+12.6% 🔺) 35.693s (+11.7% 🔺) 2.165s 4 1.19x

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

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.247s (+1.4%) 1.006s (~) 0.759s 60 1.00x
🐘 Postgres Nitro 0.268s (+10.5% 🔺) 1.023s (+1.7%) 0.756s 59 1.08x
🐘 Postgres Next.js (Turbopack) 0.308s (+4.4%) 1.006s (~) 0.698s 60 1.25x
💻 Local Express 0.420s (+5.8% 🔺) 1.004s (~) 0.584s 60 1.70x
💻 Local Nitro 0.434s (+4.5%) 1.005s (~) 0.571s 60 1.76x
💻 Local Next.js (Turbopack) 0.585s (-1.3%) 1.039s (~) 0.455s 58 2.37x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 1.910s (-6.0% 🟢) 3.788s (-3.2%) 1.878s 16 1.00x
▲ Vercel Express 2.080s (+1.5%) 3.926s (+3.0%) 1.846s 16 1.09x
▲ Vercel Nitro 2.101s (+12.0% 🔺) 3.800s (+4.3%) 1.699s 16 1.10x

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

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.431s (+8.2% 🔺) 1.104s (+8.5% 🔺) 0.673s 82 1.00x
🐘 Postgres Nitro 0.431s (+7.0% 🔺) 1.064s (+4.6%) 0.633s 86 1.00x
🐘 Postgres Next.js (Turbopack) 0.566s (-14.8% 🟢) 1.040s (-17.0% 🟢) 0.474s 87 1.31x
💻 Local Nitro 2.155s (-1.7%) 2.946s (+3.3%) 0.791s 31 5.00x
💻 Local Express 2.216s (+8.1% 🔺) 2.799s (+7.3% 🔺) 0.583s 33 5.14x
💻 Local Next.js (Turbopack) 2.349s (+1.6%) 3.110s (-1.2%) 0.761s 30 5.45x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.613s (-8.4% 🟢) 4.027s (-19.3% 🟢) 1.414s 23 1.00x
▲ Vercel Express 2.741s (+5.4% 🔺) 4.741s (+14.8% 🔺) 1.999s 19 1.05x
▲ Vercel Next.js (Turbopack) 2.976s (-9.4% 🟢) 4.728s (-10.7% 🟢) 1.752s 20 1.14x

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

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.780s (+2.8%) 1.309s (-6.7% 🟢) 0.528s 93 1.00x
🐘 Postgres Nitro 0.831s (+6.0% 🔺) 1.471s (+4.3%) 0.639s 82 1.07x
🐘 Postgres Next.js (Turbopack) 2.805s (-10.0% 🟢) 3.764s (-6.2% 🟢) 0.959s 32 3.60x
💻 Local Express 9.655s (+8.6% 🔺) 10.278s (+8.4% 🔺) 0.624s 12 12.38x
💻 Local Nitro 10.081s (+1.6%) 10.617s (+1.6%) 0.536s 12 12.92x
💻 Local Next.js (Turbopack) 10.565s (+0.6%) 11.573s (+1.6%) 1.008s 11 13.54x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 5.276s (-14.2% 🟢) 6.693s (-15.2% 🟢) 1.417s 18 1.00x
▲ Vercel Express 5.460s (-30.5% 🟢) 7.319s (-23.0% 🟢) 1.859s 17 1.03x
▲ Vercel Next.js (Turbopack) 6.263s (-31.8% 🟢) 8.116s (-28.3% 🟢) 1.852s 15 1.19x

🔍 Observability: Nitro | Express | 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
💻 Local 🥇 Express 1.168s (+1.3%) 2.005s (~) 0.012s (+23.2% 🔺) 2.020s (~) 0.852s 10 1.00x
🐘 Postgres Express 1.170s (+0.9%) 1.996s (~) 0.001s (+11.1% 🔺) 2.010s (~) 0.839s 10 1.00x
💻 Local Nitro 1.174s (~) 2.006s (~) 0.011s (-14.4% 🟢) 2.019s (~) 0.845s 10 1.01x
🐘 Postgres Nitro 1.183s (+1.1%) 1.997s (~) 0.001s (+30.0% 🔺) 2.010s (~) 0.827s 10 1.01x
💻 Local Next.js (Turbopack) 1.205s (-1.0%) 2.003s (~) 0.011s (~) 2.018s (~) 0.813s 10 1.03x
🐘 Postgres Next.js (Turbopack) 1.230s (~) 2.002s (~) 0.001s (-15.4% 🟢) 2.010s (~) 0.780s 10 1.05x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.876s (+19.6% 🔺) 3.819s (+8.7% 🔺) 0.866s (-35.5% 🟢) 5.309s (~) 2.433s 10 1.00x
▲ Vercel Next.js (Turbopack) 2.924s (+29.0% 🔺) 3.657s (+0.6%) 1.102s (+22.5% 🔺) 5.655s (+11.7% 🔺) 2.731s 10 1.02x
▲ Vercel Express 3.160s (+39.9% 🔺) 4.375s (+25.8% 🔺) 0.912s (-11.9% 🟢) 6.041s (+22.3% 🔺) 2.881s 10 1.10x

🔍 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
🐘 Postgres 🥇 Express 1.582s (+0.8%) 2.003s (~) 0.005s (~) 2.026s (~) 0.445s 30 1.00x
💻 Local Express 1.596s (+1.2%) 2.010s (~) 0.013s (+3.6%) 2.025s (~) 0.429s 30 1.01x
🐘 Postgres Nitro 1.597s (+1.0%) 2.007s (~) 0.005s (-6.3% 🟢) 2.028s (~) 0.431s 30 1.01x
💻 Local Nitro 1.628s (+1.8%) 2.012s (~) 0.013s (+6.7% 🔺) 2.027s (~) 0.400s 30 1.03x
💻 Local Next.js (Turbopack) 1.719s (~) 2.008s (~) 0.012s (+8.1% 🔺) 2.024s (~) 0.305s 30 1.09x
🐘 Postgres Next.js (Turbopack) 1.778s (-0.5%) 2.010s (~) 0.005s (-7.8% 🟢) 2.025s (~) 0.247s 30 1.12x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 5.899s (-2.0%) 7.196s (-1.5%) 0.231s (-13.6% 🟢) 7.958s (-2.6%) 2.059s 8 1.00x
▲ Vercel Express 6.045s (-4.5%) 7.444s (~) 0.252s (-52.8% 🟢) 8.176s (-3.8%) 2.131s 8 1.02x
▲ Vercel Next.js (Turbopack) 6.119s (-22.9% 🟢) 7.674s (-17.9% 🟢) 0.266s (-23.2% 🟢) 8.471s (-18.1% 🟢) 2.352s 8 1.04x

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

10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.775s (+1.1%) 1.063s (+1.4%) 0.000s (-49.1% 🟢) 1.088s (+2.6%) 0.313s 56 1.00x
🐘 Postgres Nitro 0.798s (+7.2% 🔺) 1.086s (+3.9%) 0.000s (+107.3% 🔺) 1.101s (+3.1%) 0.304s 55 1.03x
🐘 Postgres Next.js (Turbopack) 1.002s (-4.2%) 1.417s (-7.1% 🟢) 0.000s (+Infinity% 🔺) 1.427s (-6.9% 🟢) 0.425s 43 1.29x
💻 Local Express 1.408s (+1.7%) 2.013s (~) 0.000s (-14.3% 🟢) 2.015s (~) 0.607s 30 1.82x
💻 Local Next.js (Turbopack) 1.474s (+2.0%) 2.013s (~) 0.000s (+83.3% 🔺) 2.016s (~) 0.542s 30 1.90x
💻 Local Nitro 1.491s (+5.0%) 2.015s (~) 0.000s (-42.9% 🟢) 2.017s (~) 0.526s 30 1.92x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.697s (-18.4% 🟢) 4.054s (-16.7% 🟢) 0.000s (-100.0% 🟢) 4.474s (-16.8% 🟢) 1.777s 14 1.00x
▲ Vercel Express 2.937s (-4.1%) 4.111s (-1.8%) 0.000s (NaN%) 4.609s (~) 1.673s 14 1.09x
▲ Vercel Next.js (Turbopack) 3.033s (-29.9% 🟢) 4.570s (-21.2% 🟢) 0.000s (+Infinity% 🔺) 5.181s (-18.0% 🟢) 2.149s 12 1.12x

🔍 Observability: Nitro | Express | 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 🥇 Express 1.581s (-4.1%) 2.172s (+1.4%) 0.000s (+Infinity% 🔺) 2.193s (~) 0.612s 28 1.00x
🐘 Postgres Nitro 1.626s (-5.8% 🟢) 2.138s (-5.2% 🟢) 0.000s (+Infinity% 🔺) 2.151s (-5.1% 🟢) 0.525s 28 1.03x
🐘 Postgres Next.js (Turbopack) 2.129s (-2.3%) 2.656s (+1.7%) 0.000s (-100.0% 🟢) 2.663s (+1.3%) 0.533s 23 1.35x
💻 Local Next.js (Turbopack) 2.784s (-4.8%) 3.356s (-5.6% 🟢) 0.000s (-78.2% 🟢) 3.363s (-5.5% 🟢) 0.578s 18 1.76x
💻 Local Express 3.152s (+3.6%) 3.901s (+4.6%) 0.001s (-7.0% 🟢) 3.904s (+4.6%) 0.753s 16 1.99x
💻 Local Nitro 3.397s (+7.4% 🔺) 3.966s (+3.3%) 0.001s (+27.3% 🔺) 3.973s (+3.4%) 0.576s 16 2.15x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 4.260s (-22.5% 🟢) 5.353s (-24.5% 🟢) 0.001s (+409.1% 🔺) 5.806s (-23.4% 🟢) 1.546s 11 1.00x
▲ Vercel Express 4.622s (-28.6% 🟢) 5.816s (-24.1% 🟢) 0.000s (-100.0% 🟢) 6.305s (-23.2% 🟢) 1.683s 10 1.08x
▲ Vercel Next.js (Turbopack) 4.895s (-25.7% 🟢) 6.509s (-21.8% 🟢) 0.000s (+Infinity% 🔺) 6.966s (-21.4% 🟢) 2.071s 9 1.15x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World 🥇 Fastest Framework Wins
💻 Local Nitro 10/21
🐘 Postgres Express 16/21
▲ Vercel Nitro 12/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 🐘 Postgres 15/21
Next.js (Turbopack) 🐘 Postgres 16/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)
  • 🌐 Redis: Community world (local development)
  • 🌐 Redis + BullMQ: Community world (local development)
  • 🌐 Cloudflare: Community world (local development)
  • 🌐 MySQL: Community world (local development)
  • 🌐 Azure: Community world (local development)
  • 🌐 NATS JetStream: Community world (local development)
  • 🌐 Upstash: Community world (local development)

📋 View full workflow run

@github-actions

github-actions Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
❌ ▲ Vercel Production 1439 3 219 1661
✅ 💻 Local Development 1895 0 219 2114
✅ 📦 Local Production 1744 0 219 1963
✅ 🐘 Local Postgres 1881 0 233 2114
✅ 🪟 Windows 151 0 0 151
✅ 📋 Other 879 0 178 1057
Total 7989 3 1068 9060

❌ Failed Tests

▲ Vercel Production (3 failed)

fastify (1 failed):

hono (1 failed):

  • AbortController abortFromStepWorkflow: step abort cancels an in-flight sibling step

vite (1 failed):

Details by Category

❌ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 125 0 26
✅ example 125 0 26
✅ express 125 0 26
❌ fastify 124 1 26
❌ hono 124 1 26
✅ nextjs-turbopack 149 0 2
✅ nextjs-webpack 149 0 2
✅ nitro 125 0 26
✅ nuxt 125 0 26
✅ sveltekit 144 0 7
❌ vite 124 1 26
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 126 0 25
✅ express-stable 126 0 25
✅ fastify-stable 126 0 25
✅ hono-stable 126 0 25
✅ nextjs-turbopack-canary 132 0 19
✅ nextjs-turbopack-stable-lazy-discovery-disabled 151 0 0
✅ nextjs-turbopack-stable-lazy-discovery-enabled 151 0 0
✅ nextjs-webpack-canary 132 0 19
✅ nextjs-webpack-stable-lazy-discovery-disabled 151 0 0
✅ nextjs-webpack-stable-lazy-discovery-enabled 151 0 0
✅ nitro-stable 126 0 25
✅ nuxt-stable 126 0 25
✅ sveltekit-stable 145 0 6
✅ vite-stable 126 0 25
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 126 0 25
✅ express-stable 126 0 25
✅ fastify-stable 126 0 25
✅ hono-stable 126 0 25
✅ nextjs-turbopack-canary 132 0 19
✅ nextjs-turbopack-stable-lazy-discovery-disabled 151 0 0
✅ nextjs-webpack-canary 132 0 19
✅ nextjs-webpack-stable-lazy-discovery-disabled 151 0 0
✅ nextjs-webpack-stable-lazy-discovery-enabled 151 0 0
✅ nitro-stable 126 0 25
✅ nuxt-stable 126 0 25
✅ sveltekit-stable 145 0 6
✅ vite-stable 126 0 25
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 125 0 26
✅ express-stable 125 0 26
✅ fastify-stable 125 0 26
✅ hono-stable 125 0 26
✅ nextjs-turbopack-canary 131 0 20
✅ nextjs-turbopack-stable-lazy-discovery-disabled 150 0 1
✅ nextjs-turbopack-stable-lazy-discovery-enabled 150 0 1
✅ nextjs-webpack-canary 131 0 20
✅ nextjs-webpack-stable-lazy-discovery-disabled 150 0 1
✅ nextjs-webpack-stable-lazy-discovery-enabled 150 0 1
✅ nitro-stable 125 0 26
✅ nuxt-stable 125 0 26
✅ sveltekit-stable 144 0 7
✅ vite-stable 125 0 26
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 151 0 0
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 126 0 25
✅ e2e-local-dev-tanstack-start- 126 0 25
✅ e2e-local-postgres-nest-stable 125 0 26
✅ e2e-local-postgres-tanstack-start- 125 0 26
✅ e2e-local-prod-nest-stable 126 0 25
✅ e2e-local-prod-tanstack-start- 126 0 25
✅ e2e-vercel-prod-tanstack-start 125 0 26

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes the SWC plugin’s DCE usage analysis so module-scope declarations referenced only via destructuring default initializers (e.g. const { ttl = TTL } = options) are correctly counted as “used”, preventing runtime ReferenceErrors when defaults fire.

Changes:

  • Extend ComprehensiveUsageCollector to traverse destructuring patterns and visit only default-value initializers (and computed keys), without marking binding identifiers as used.
  • Add a regression fixture covering module-scope const preservation in both step and workflow modes (and confirming truly-unused consts are still removed).
  • Update the SWC plugin spec and add a changeset for the patch release.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/swc-plugin-workflow/transform/src/lib.rs Adds visit_pat_default_initializers and wires it into visit_mut_var_declarator so destructuring-default references are counted for DCE.
packages/swc-plugin-workflow/transform/tests/fixture/destructuring-default-references-module-const/input.js New fixture input reproducing the destructuring-default-only reference pattern and an unused const.
packages/swc-plugin-workflow/transform/tests/fixture/destructuring-default-references-module-const/output-step.js Step-mode snapshot asserting referenced consts survive and unused const is removed.
packages/swc-plugin-workflow/transform/tests/fixture/destructuring-default-references-module-const/output-workflow.js Workflow-mode snapshot asserting referenced consts survive and unused const is removed.
packages/swc-plugin-workflow/spec.md Documents that destructuring-default initializers count as references for DCE in both modes.
.changeset/swc-destructuring-default-dce.md Patch changeset describing the DCE fix and the prevented runtime error.

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

* origin/main:
  docs: document run idempotency (#2011)
  Render attr_set events and run attributes in observability UI (#2393)
  [ci] Fix backport job model slug (#2403)
  [ci] Comment on PR when backport fails, revert to use opus 4.8 (#2400)
  Update queue client to 0.3.1 (#2399)
  fix(deps): upgrade esbuild to 0.28.1 (GHSA-gv7w-rqvm-qjhr) (#2395)
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.

[swc-plugin] DCE strips module-scope declarations referenced only by destructuring defaults → ReferenceError

2 participants