[errors] Replace chalk import with inline ANSI shim - #1915

Merged
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk
May 5, 2026
Merged

[errors] Replace chalk import with inline ANSI shim#1915
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

@workflow/errors/ansi imports chalk, which transitively require('os')s via supports-color. That module is reachable from the workflow-VM bundle through:

workflow → @workflow/core/workflow/index.ts
→ context-errors.ts
→ context-violation-error.ts
→ @workflow/errors/ansi ← imports chalk

The workflow VM has no require(), so importing chalk crashes every workflow at registration time:

./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)

Fix

Replace chalk with a 25-line inline SGR helper that exposes the same call surface (chalk.red, chalk.bold, …). Color detection mirrors chalk's defaults at a coarse level — FORCE_COLOR on, NO_COLOR off, otherwise gated on process.stdout.isTTY — so non-TTY logs and the workflow VM (no process) get plain text, matching the prior behavior in tests and in production log drains.

Drops the chalk runtime dependency from @workflow/errors and removes the now-unused __mocks__/chalk.ts Vitest mock. All 820 @workflow/core unit tests + 11 @workflow/errors snapshot tests pass.

Test plan

  • pnpm --filter @workflow/errors test (26 tests pass)
  • pnpm --filter @workflow/core test (820 unit tests pass; e2e excluded)
  • CI green
  • Reproduce: install latest tarballs in a v2-flow consumer (vade) and confirm step registration no longer throws ReferenceError: require is not defined

🤖 Generated with Claude Code

`@workflow/errors/ansi` is reachable from the workflow-VM bundle via
`@workflow/core/workflow` → `context-errors` → `context-violation-error`
→ here. The real `chalk` package pulls in `supports-color`, which calls
`require('os')` at module load — crashing every workflow with
`ReferenceError: require is not defined` in the sandboxed VM the moment
any user step is registered:
./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)
Replace `chalk` with a 25-line inline SGR helper exposing the same call
surface (`chalk.red`, `chalk.bold`, …). Color detection mirrors chalk's
default at a coarse level — `FORCE_COLOR` on, `NO_COLOR` off, otherwise
gated on `process.stdout.isTTY` — so non-TTY logs and the workflow VM
(no `process`) get plain text, matching previous behavior in tests and
in production log drains.
Drops the `chalk` runtime dependency from `@workflow/errors` and removes
the now-unused `__mocks__/chalk.ts` Vitest mock.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 47be549

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

This PR includes changesets to release 22 packages
NameType
@workflow/errorsPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/webPatch
workflowPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-vercelPatch
tarballsPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/world-testingPatch
@workflow/web-sharedPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

@github-actions

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.033s (-24.6% 🟢)1.005s (~)0.973s101.00x
💻 LocalExpress0.035s (-20.1% 🟢)1.006s (~)0.970s101.09x
🐘 PostgresExpress0.046s (-19.8% 🟢)1.011s (~)0.964s101.43x
💻 LocalNext.js (Turbopack)0.049s1.005s0.956s101.50x
🐘 PostgresNitro0.052s (-45.5% 🟢)1.011s (-3.0%)0.959s101.60x
🌐 RedisNext.js (Turbopack)0.054s1.005s0.951s101.65x
🐘 PostgresNext.js (Turbopack)0.057s1.011s0.953s101.76x
🌐 MongoDBNext.js (Turbopack)0.091s1.007s0.916s102.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.295s (+25.2% 🔺)2.353s (+10.2% 🔺)2.058s101.00x
▲ VercelNitro0.366s (-10.6% 🟢)2.033s (-19.0% 🟢)1.667s101.24x
▲ VercelNext.js (Turbopack)0.558s (+122.1% 🔺)2.742s (+17.5% 🔺)2.183s101.90x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.070s (-5.4% 🟢)2.007s (~)0.937s101.00x
💻 LocalExpress1.072s (-4.7%)2.007s (~)0.935s101.00x
🐘 PostgresExpress1.082s (-5.6% 🟢)2.009s (~)0.927s101.01x
🐘 PostgresNitro1.084s (-4.9%)2.009s (~)0.925s101.01x
💻 LocalNext.js (Turbopack)1.110s2.006s0.896s101.04x
🌐 RedisNext.js (Turbopack)1.111s2.008s0.897s101.04x
🐘 PostgresNext.js (Turbopack)1.114s2.010s0.897s101.04x
🌐 MongoDBNext.js (Turbopack)1.148s2.009s0.861s101.07x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.586s (-15.4% 🟢)3.902s (+2.5%)2.317s101.00x
▲ VercelNitro1.641s (-57.8% 🟢)4.080s (-30.9% 🟢)2.439s101.04x
▲ VercelNext.js (Turbopack)2.861s (+40.6% 🔺)4.792s (+25.1% 🔺)1.931s101.80x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express10.400s (-5.1% 🟢)11.014s (~)0.614s31.00x
🐘 PostgresNitro10.402s (-4.3%)11.015s (~)0.613s31.00x
💻 LocalExpress10.409s (-4.7%)11.022s (~)0.613s31.00x
💻 LocalNitro10.422s (-4.8%)11.023s (~)0.601s31.00x
🌐 MongoDBNext.js (Turbopack)10.655s11.023s0.368s31.02x
🐘 PostgresNext.js (Turbopack)10.667s11.015s0.348s31.03x
🌐 RedisNext.js (Turbopack)10.669s11.023s0.354s31.03x
💻 LocalNext.js (Turbopack)10.682s11.020s0.338s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.559s (-42.9% 🟢)15.361s (-38.8% 🟢)1.802s21.00x
▲ VercelExpress13.917s (-18.0% 🟢)15.853s (-20.8% 🟢)1.936s21.03x
▲ VercelNext.js (Turbopack)14.322s (-17.3% 🟢)16.622s (-14.3% 🟢)2.299s21.06x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.471s (-7.6% 🟢)14.017s (-6.7% 🟢)0.546s51.00x
💻 LocalExpress13.477s (-10.0% 🟢)14.027s (-6.7% 🟢)0.550s51.00x
🐘 PostgresNitro13.481s (-7.6% 🟢)14.021s (-6.7% 🟢)0.540s51.00x
💻 LocalNitro13.483s (-10.5% 🟢)14.027s (-12.5% 🟢)0.544s51.00x
🌐 MongoDBNext.js (Turbopack)13.835s14.025s0.190s51.03x
🌐 RedisNext.js (Turbopack)14.050s15.030s0.980s41.04x
💻 LocalNext.js (Turbopack)14.060s14.628s0.569s51.04x
🐘 PostgresNext.js (Turbopack)14.069s15.020s0.951s41.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.544s (-66.6% 🟢)23.739s (-64.4% 🟢)2.195s31.00x
▲ VercelExpress22.795s (-54.7% 🟢)25.107s (-52.2% 🟢)2.312s31.06x
▲ VercelNext.js (Turbopack)23.551s (-55.2% 🟢)26.276s (-51.9% 🟢)2.725s31.09x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express11.836s (-15.5% 🟢)12.015s (-17.7% 🟢)0.179s81.00x
💻 LocalNitro11.946s (-28.8% 🟢)12.272s (-27.9% 🟢)0.326s81.01x
💻 LocalExpress11.949s (-28.0% 🟢)12.146s (-28.7% 🟢)0.197s81.01x
🐘 PostgresNitro11.970s (-14.3% 🟢)12.394s (-13.4% 🟢)0.424s81.01x
🌐 MongoDBNext.js (Turbopack)12.590s13.024s0.433s71.06x
💻 LocalNext.js (Turbopack)12.972s13.452s0.480s71.10x
🌐 RedisNext.js (Turbopack)13.100s13.884s0.784s71.11x
🐘 PostgresNext.js (Turbopack)13.185s14.020s0.835s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express34.548s (-71.5% 🟢)36.366s (-70.6% 🟢)1.818s31.00x
▲ VercelNitro35.156s (-91.7% 🟢)37.155s (-91.2% 🟢)1.999s31.02x
▲ VercelNext.js (Turbopack)35.694s (-90.9% 🟢)37.608s (-90.5% 🟢)1.913s31.03x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.147s (-9.0% 🟢)2.007s (~)0.860s151.00x
🐘 PostgresNitro1.156s (-9.3% 🟢)2.007s (~)0.851s151.01x
💻 LocalNitro1.188s (-27.2% 🟢)2.006s (-3.3%)0.818s151.04x
💻 LocalExpress1.197s (-19.6% 🟢)2.006s (~)0.808s151.04x
🐘 PostgresNext.js (Turbopack)1.213s2.008s0.794s151.06x
🌐 RedisNext.js (Turbopack)1.268s2.006s0.738s151.10x
💻 LocalNext.js (Turbopack)1.378s2.073s0.695s151.20x
🌐 MongoDBNext.js (Turbopack)2.011s2.591s0.580s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.918s (+3.6%)4.656s (+7.7% 🔺)1.738s71.00x
▲ VercelNext.js (Turbopack)4.329s (+27.4% 🔺)6.218s (+26.1% 🔺)1.889s51.48x
▲ VercelExpress4.519s (+58.0% 🔺)6.298s (+36.2% 🔺)1.779s51.55x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.226s (-48.1% 🟢)2.007s (-33.3% 🟢)0.780s151.00x
🐘 PostgresNitro1.234s (-47.5% 🟢)2.007s (-33.3% 🟢)0.773s151.01x
🐘 PostgresNext.js (Turbopack)1.356s2.007s0.651s151.11x
💻 LocalNitro1.791s (-43.0% 🟢)2.007s (-48.4% 🟢)0.216s151.46x
💻 LocalExpress1.792s (-39.3% 🟢)2.006s (-41.9% 🟢)0.214s151.46x
💻 LocalNext.js (Turbopack)1.806s2.073s0.267s151.47x
🌐 RedisNext.js (Turbopack)2.385s3.008s0.623s101.94x
🌐 MongoDBNext.js (Turbopack)3.564s4.008s0.444s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.458s (+23.2% 🔺)6.268s (+22.7% 🔺)1.811s51.00x
▲ VercelNext.js (Turbopack)5.613s (-20.9% 🟢)7.797s (-12.4% 🟢)2.184s41.26x
▲ VercelNitro6.080s (+50.1% 🔺)8.092s (+36.7% 🔺)2.012s41.36x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.364s (-60.9% 🟢)2.006s (-50.0% 🟢)0.642s151.00x
🐘 PostgresNitro1.374s (-60.5% 🟢)2.007s (-49.9% 🟢)0.633s151.01x
🐘 PostgresNext.js (Turbopack)1.627s2.007s0.380s151.19x
🌐 RedisNext.js (Turbopack)3.621s4.009s0.389s82.65x
💻 LocalNext.js (Turbopack)5.000s5.511s0.511s63.67x
💻 LocalNitro5.331s (-36.2% 🟢)5.847s (-35.2% 🟢)0.517s63.91x
💻 LocalExpress5.417s (-35.0% 🟢)5.679s (-37.1% 🟢)0.263s63.97x
🌐 MongoDBNext.js (Turbopack)6.223s7.012s0.788s54.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.257s (+47.6% 🔺)8.386s (+36.9% 🔺)2.129s41.00x
▲ VercelNext.js (Turbopack)7.288s (-18.3% 🟢)9.279s (-15.3% 🟢)1.991s41.16x
▲ VercelNitro8.700s (+146.8% 🔺)10.899s (+96.9% 🔺)2.199s31.39x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.153s (-8.3% 🟢)2.009s (~)0.857s151.00x
🐘 PostgresExpress1.154s (-8.2% 🟢)2.009s (~)0.855s151.00x
🐘 PostgresNext.js (Turbopack)1.210s2.007s0.797s151.05x
🌐 RedisNext.js (Turbopack)1.257s2.006s0.749s151.09x
💻 LocalNext.js (Turbopack)1.353s2.006s0.653s151.17x
💻 LocalExpress1.384s (-26.9% 🟢)2.006s (-15.1% 🟢)0.622s151.20x
💻 LocalNitro1.397s (-25.1% 🟢)2.007s (-14.3% 🟢)0.610s151.21x
🌐 MongoDBNext.js (Turbopack)2.013s2.590s0.577s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.363s (-8.5% 🟢)4.099s (-5.8% 🟢)1.736s81.00x
▲ VercelNitro3.169s (+28.9% 🔺)5.005s (+20.0% 🔺)1.836s71.34x
▲ VercelNext.js (Turbopack)3.909s (+33.3% 🔺)6.008s (+29.4% 🔺)2.100s51.65x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.231s (-47.4% 🟢)2.009s (-33.3% 🟢)0.778s151.00x
🐘 PostgresExpress1.232s (-47.4% 🟢)2.007s (-33.3% 🟢)0.776s151.00x
🐘 PostgresNext.js (Turbopack)1.354s2.008s0.654s151.10x
💻 LocalNitro2.049s (-33.1% 🟢)2.509s (-35.4% 🟢)0.460s121.67x
💻 LocalNext.js (Turbopack)2.074s2.735s0.661s111.69x
💻 LocalExpress2.135s (-31.8% 🟢)2.593s (-31.1% 🟢)0.457s121.74x
🌐 RedisNext.js (Turbopack)2.370s3.008s0.639s101.93x
🌐 MongoDBNext.js (Turbopack)3.577s4.009s0.432s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.907s (+20.8% 🔺)5.774s (+13.7% 🔺)1.867s61.00x
▲ VercelExpress4.205s (+31.7% 🔺)6.052s (+26.3% 🔺)1.847s51.08x
▲ VercelNext.js (Turbopack)4.751s (+51.2% 🔺)6.510s (+44.0% 🔺)1.759s51.22x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.358s (-61.2% 🟢)2.008s (-49.9% 🟢)0.650s151.00x
🐘 PostgresNitro1.366s (-60.7% 🟢)2.008s (-49.9% 🟢)0.642s151.01x
🐘 PostgresNext.js (Turbopack)1.651s2.009s0.358s151.22x
🌐 RedisNext.js (Turbopack)3.648s4.010s0.362s82.69x
💻 LocalExpress5.565s (-36.8% 🟢)6.015s (-35.1% 🟢)0.450s54.10x
💻 LocalNext.js (Turbopack)5.704s6.214s0.510s54.20x
💻 LocalNitro5.946s (-35.0% 🟢)6.348s (-36.7% 🟢)0.402s64.38x
🌐 MongoDBNext.js (Turbopack)6.250s7.013s0.763s54.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.891s (+15.7% 🔺)7.641s (+12.1% 🔺)1.750s41.00x
▲ VercelExpress6.208s (-3.2%)7.682s (-6.1% 🟢)1.474s41.05x
▲ VercelNext.js (Turbopack)6.830s (+1.1%)8.929s (+4.5%)2.099s41.16x

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

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.451s (-46.2% 🟢)1.006s (-1.7%)0.555s601.00x
🐘 PostgresNitro0.453s (-44.7% 🟢)1.006s (~)0.553s601.01x
💻 LocalNitro0.471s (-52.0% 🟢)1.004s (-8.2% 🟢)0.533s601.04x
💻 LocalExpress0.506s (-48.6% 🟢)1.022s (-5.1% 🟢)0.515s591.12x
🌐 MongoDBNext.js (Turbopack)0.597s1.006s0.409s601.32x
🌐 RedisNext.js (Turbopack)0.639s1.004s0.365s601.42x
🐘 PostgresNext.js (Turbopack)0.662s1.006s0.344s601.47x
💻 LocalNext.js (Turbopack)0.711s1.004s0.293s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.747s (-78.5% 🟢)6.670s (-72.2% 🟢)1.923s101.00x
▲ VercelExpress4.916s (-74.1% 🟢)6.991s (-67.2% 🟢)2.075s91.04x
▲ VercelNext.js (Turbopack)6.804s (-53.1% 🟢)8.802s (-45.3% 🟢)1.998s71.43x

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

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.032s (-47.8% 🟢)1.458s (-35.4% 🟢)0.426s621.00x
🐘 PostgresNitro1.047s (-45.7% 🟢)1.507s (-28.3% 🟢)0.459s601.01x
💻 LocalExpress1.184s (-60.7% 🟢)2.006s (-44.1% 🟢)0.821s451.15x
💻 LocalNitro1.191s (-60.8% 🟢)2.006s (-46.6% 🟢)0.815s451.15x
🌐 MongoDBNext.js (Turbopack)1.503s2.007s0.504s451.46x
🐘 PostgresNext.js (Turbopack)1.587s2.007s0.419s451.54x
🌐 RedisNext.js (Turbopack)1.611s2.028s0.417s451.56x
💻 LocalNext.js (Turbopack)1.741s2.005s0.265s451.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.285s (-64.4% 🟢)14.394s (-60.9% 🟢)2.108s71.00x
▲ VercelNitro12.810s (-67.6% 🟢)15.636s (-62.1% 🟢)2.825s61.04x
▲ VercelNext.js (Turbopack)15.611s (-68.7% 🟢)18.106s (-65.0% 🟢)2.494s61.27x

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

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.878s (-52.9% 🟢)2.076s (-52.5% 🟢)0.198s581.00x
🐘 PostgresNitro2.079s (-49.3% 🟢)2.550s (-44.6% 🟢)0.470s481.11x
💻 LocalNitro2.680s (-71.2% 🟢)3.008s (-70.0% 🟢)0.327s401.43x
💻 LocalExpress2.692s (-70.8% 🟢)3.008s (-70.0% 🟢)0.316s401.43x
🐘 PostgresNext.js (Turbopack)3.092s4.009s0.917s301.65x
🌐 RedisNext.js (Turbopack)3.096s3.880s0.783s311.65x
🌐 MongoDBNext.js (Turbopack)3.670s4.013s0.343s301.95x
💻 LocalNext.js (Turbopack)3.704s4.008s0.303s301.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro39.197s (-59.6% 🟢)42.019s (-57.3% 🟢)2.822s31.00x
▲ VercelExpress40.332s (-69.0% 🟢)42.693s (-67.7% 🟢)2.361s31.03x
▲ VercelNext.js (Turbopack)44.774s (-58.2% 🟢)48.551s (-55.4% 🟢)3.777s31.14x

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

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.176s (-37.5% 🟢)1.006s (~)0.829s601.00x
🐘 PostgresNitro0.182s (-35.8% 🟢)1.005s (~)0.824s601.03x
🐘 PostgresNext.js (Turbopack)0.232s1.006s0.774s601.32x
🌐 RedisNext.js (Turbopack)0.242s1.004s0.762s601.37x
💻 LocalExpress0.439s (-21.6% 🟢)1.004s (~)0.565s602.49x
💻 LocalNitro0.474s (-21.6% 🟢)1.021s (~)0.547s592.69x
💻 LocalNext.js (Turbopack)0.539s1.004s0.465s603.06x
🌐 MongoDBNext.js (Turbopack)1.032s1.825s0.793s335.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.231s (+34.3% 🔺)4.189s (+25.0% 🔺)1.958s151.00x
▲ VercelExpress2.259s (+15.6% 🔺)4.431s (+21.8% 🔺)2.172s141.01x
▲ VercelNext.js (Turbopack)4.335s (+114.4% 🔺)6.320s (+66.6% 🔺)1.984s101.94x

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

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.309s (-39.4% 🟢)1.006s (~)0.697s901.00x
🐘 PostgresNitro0.312s (-37.2% 🟢)1.006s (~)0.694s901.01x
🌐 RedisNext.js (Turbopack)0.412s1.004s0.592s901.33x
🐘 PostgresNext.js (Turbopack)0.450s1.006s0.555s901.46x
💻 LocalNitro2.179s (-14.2% 🟢)3.008s (~)0.829s307.05x
💻 LocalExpress2.268s (-9.8% 🟢)2.911s (-3.3%)0.644s317.34x
💻 LocalNext.js (Turbopack)2.382s2.977s0.595s317.71x
🌐 MongoDBNext.js (Turbopack)2.594s3.008s0.414s308.39x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express7.284s (+139.1% 🔺)9.227s (+91.9% 🔺)1.943s101.00x
▲ VercelNitro7.305s (+126.5% 🔺)9.647s (+100.1% 🔺)2.342s101.00x
▲ VercelNext.js (Turbopack)8.118s (+129.6% 🔺)10.545s (+103.1% 🔺)2.427s91.11x

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

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.618s (-24.5% 🟢)1.006s (-1.1%)0.388s1201.00x
🐘 PostgresNitro0.649s (-17.9% 🟢)1.006s (~)0.357s1201.05x
🌐 RedisNext.js (Turbopack)0.770s1.004s0.233s1201.25x
🐘 PostgresNext.js (Turbopack)0.933s1.219s0.286s991.51x
🌐 MongoDBNext.js (Turbopack)5.361s6.012s0.651s208.67x
💻 LocalExpress10.227s (-8.6% 🟢)10.863s (-9.0% 🟢)0.636s1216.54x
💻 LocalNitro10.251s (-8.4% 🟢)10.781s (-7.6% 🟢)0.530s1216.58x
💻 LocalNext.js (Turbopack)10.950s11.757s0.807s1117.71x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express20.578s (+177.3% 🔺)23.078s (+149.6% 🔺)2.500s61.00x
▲ VercelNext.js (Turbopack)21.742s (+110.5% 🔺)24.590s (+100.1% 🔺)2.848s51.06x
▲ VercelNitro25.208s (+226.4% 🔺)27.039s (+187.6% 🔺)1.831s51.22x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.129s (+450.3% 🔺)1.997s (+99.9% 🔺)0.001s (-18.8% 🟢)2.009s (+98.7% 🔺)0.881s101.00x
💻 LocalExpress1.131s (+468.1% 🔺)2.005s (+99.6% 🔺)0.012s (+2.5%)2.019s (+98.4% 🔺)0.888s101.00x
💻 LocalNitro1.132s (+429.6% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.021s (+98.3% 🔺)0.889s101.00x
🐘 PostgresNitro1.149s (+460.7% 🔺)1.994s (+99.5% 🔺)0.002s (+6.7% 🔺)2.012s (+99.0% 🔺)0.863s101.02x
💻 LocalNext.js (Turbopack)1.178s2.003s0.012s2.019s0.840s101.04x
🐘 PostgresNext.js (Turbopack)1.192s2.002s0.001s2.010s0.818s101.06x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.184s (-43.0% 🟢)3.612s (-31.5% 🟢)1.614s (+117.5% 🔺)5.719s (-11.8% 🟢)3.535s101.00x
▲ VercelExpress2.198s (-12.3% 🟢)3.538s (-13.5% 🟢)1.751s (+82.3% 🔺)5.836s (+4.4%)3.637s101.01x
▲ VercelNext.js (Turbopack)4.942s (-27.9% 🟢)5.438s (-37.1% 🟢)1.145s (+81.2% 🔺)8.364s (-14.6% 🟢)3.421s102.26x

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

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.496s (+137.4% 🔺)2.000s (+98.7% 🔺)0.003s (-9.5% 🟢)2.026s (+98.1% 🔺)0.531s301.00x
🐘 PostgresNitro1.507s (+141.5% 🔺)2.002s (+98.8% 🔺)0.004s (-8.9% 🟢)2.024s (+97.9% 🔺)0.517s301.01x
💻 LocalNitro1.538s (+83.4% 🔺)2.012s (+98.8% 🔺)0.010s (+11.7% 🔺)2.025s (+81.4% 🔺)0.486s301.03x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.004s2.025s0.367s301.11x
💻 LocalExpress1.716s (+126.6% 🔺)2.012s (+95.5% 🔺)0.009s (-2.1%)2.202s (+111.8% 🔺)0.486s281.15x
💻 LocalNext.js (Turbopack)2.055s2.009s0.010s2.423s0.368s251.37x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.789s (-80.3% 🟢)7.537s (-75.5% 🟢)0.268s (+139.0% 🔺)8.404s (-73.6% 🟢)2.615s81.00x
▲ VercelExpress6.045s (-7.1% 🟢)7.653s (-4.5%)0.313s (-23.5% 🟢)8.503s (-3.8%)2.457s81.04x
▲ VercelNext.js (Turbopack)12.326s (-27.1% 🟢)13.810s (-24.3% 🟢)0.429s (+103.2% 🔺)15.364s (-18.9% 🟢)3.038s42.13x

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

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.660s (-31.8% 🟢)1.031s (-17.4% 🟢)0.000s (+24.1% 🔺)1.048s (-16.7% 🟢)0.387s581.00x
🐘 PostgresExpress0.688s (-28.4% 🟢)1.061s (-17.0% 🟢)0.000s (+105.4% 🔺)1.079s (-17.4% 🟢)0.391s561.04x
🐘 PostgresNext.js (Turbopack)0.783s1.090s0.000s1.097s0.314s561.19x
💻 LocalNitro1.345s (+10.0% 🔺)2.016s (~)0.000s (+333.3% 🔺)2.018s (~)0.674s302.04x
💻 LocalNext.js (Turbopack)1.400s2.014s0.000s2.017s0.617s302.12x
💻 LocalExpress1.515s (+23.7% 🔺)2.015s (~)0.000s (-35.7% 🟢)2.196s (+8.6% 🔺)0.681s282.29x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.737s (+22.5% 🔺)5.067s (+15.4% 🔺)0.000s (+18.2% 🔺)5.709s (+18.7% 🔺)1.972s111.00x
▲ VercelExpress4.218s (+12.8% 🔺)5.770s (+13.1% 🔺)0.000s (+50.0% 🔺)6.218s (+12.4% 🔺)2.000s111.13x
▲ VercelNext.js (Turbopack)6.160s (-39.5% 🟢)6.979s (-39.4% 🟢)0.000s (NaN%)8.458s (-29.8% 🟢)2.298s81.65x

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

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.286s (-28.2% 🟢)2.001s (-6.6% 🟢)0.000s (-100.0% 🟢)2.020s (-7.1% 🟢)0.734s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.067s (-5.1% 🟢)0.000s (+Infinity% 🔺)2.115s (-3.8%)0.730s291.08x
🐘 PostgresNext.js (Turbopack)1.629s2.225s0.000s2.239s0.610s271.27x
💻 LocalNext.js (Turbopack)2.539s3.125s0.000s3.128s0.589s201.97x
💻 LocalExpress3.054s (-11.9% 🟢)3.903s (-3.2%)0.000s (-45.3% 🟢)3.905s (-3.2%)0.852s162.37x
💻 LocalNitro3.180s (-6.1% 🟢)3.966s (-1.6%)0.000s (-41.4% 🟢)3.971s (-1.6%)0.791s162.47x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.004s (+30.9% 🔺)7.517s (+24.8% 🔺)0.000s (+Infinity% 🔺)8.103s (+25.5% 🔺)2.099s81.00x
▲ VercelNitro6.750s (+64.9% 🔺)8.558s (+59.2% 🔺)0.000s (-100.0% 🟢)9.120s (+57.4% 🔺)2.370s71.12x
▲ VercelNext.js (Turbopack)8.561s (+52.4% 🔺)9.916s (+42.0% 🔺)0.000s (-100.0% 🟢)11.240s (+49.1% 🔺)2.679s61.43x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro11/21
🐘 PostgresExpress17/21
▲ VercelNitro11/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres19/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres17/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

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production85902191078
✅ 💻 Local Development95702191176
✅ 📦 Local Production95702191176
✅ 🐘 Local Postgres95702191176
✅ 🪟 Windows980098
✅ 📋 Other5100176686
Total4338010525390

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro72026
✅ example72026
✅ express72026
✅ fastify72026
✅ hono72026
✅ nextjs-turbopack9602
✅ nextjs-webpack9602
✅ nitro72026
✅ nuxt72026
✅ sveltekit9107
✅ vite72026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9800
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable73025
✅ e2e-local-dev-tanstack-start-stable73025
✅ e2e-local-postgres-nest-stable73025
✅ e2e-local-postgres-tanstack-start-stable73025
✅ e2e-local-prod-nest-stable73025
✅ e2e-local-prod-tanstack-start-stable73025
✅ e2e-vercel-prod-tanstack-start72026

📋 View full workflow run

Move the inline ANSI shim out of `ansi.ts` into its own
`./internal-chalk.ts` so tests can swap it for a tag-emitting mock via
`vi.mock('./internal-chalk.js', …)`. Snapshots for `Ansi.code` /
`Ansi.hint` / `Ansi.note` / `Ansi.help` / `Ansi.docs` go back to the
readable HTML-like form (`<blue><b>hint:</b> try reloading</blue>`),
making it obvious from the test file which fragments are colored and
how — the same review affordance the previous `__mocks__/chalk.ts`
mock provided before the chalk dep was dropped.
`Ansi.frame` and `Ansi.inline` remain plain in snapshots because they
don't apply any styling themselves; that matches the pre-fix behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. The diagnosis is correct, the fix is appropriately surgical, and the shim is well-written.

Verified

Reachability claim — confirmed: packages/core/src/workflow/index.tscontext-errors.tscontext-violation-error.tsimport * as Ansi from '@workflow/errors/ansi'. So any consumer that uses workflow-context-checked APIs (createHook, createWebhook, getWorkflowMetadata, etc.) ends up dragging the entire ansi/chalk transitive graph into the workflow VM bundle.

Output parity — for the only nested call in ansi.ts (code(): chalk.italic(\${chalk.dim('`')}${str}${chalk.dim('`')}`)), the shim produces byte-for-byte the same SGR sequence as chalk@5.6.2`:

\x1b[3m\x1b[2m`\x1b[22mfoo\x1b[2m`\x1b[22m\x1b[23m

The 22 (normal intensity) close after each dim correctly leaves italic (3) on, since 22 only resets bold/dim — same as chalk's behavior.

Bundle effect — built workbench/example from this branch and grepped the workflow VM bundle (the string passed to workflowEntrypoint):

$ grep -cE "supports-color|require\(['\"](node:)?os['\"]|chalk|internal_chalk" /tmp/vm-bundle.txt
0

No chalk, no supports-color, no os anywhere. (The example workbench's VM bundle didn't actually pull in context-violation-error due to tree-shaking in this particular workflow set, so the chalk references were already absent on main for this specific bundle — but the change is still correct, and downstream consumers like vade that exercise the workflow-context paths will get the fix.)

Testspnpm --filter @workflow/errors test passes (4 files, 26 tests including all 11 ansi.test.ts snapshots), pnpm --filter @workflow/core test passes (820 tests).

CI failures are pre-existing flakes

The two red checks (Unit Tests (ubuntu-latest), E2E Required Check) are both from the same flaky world-postgres test:

FAIL test/spec.test.ts > sequential steps with stream complete in a single flow invocation
AssertionError: expected +0 to be 1 // Object.is equality

This same test fails on origin/main HEAD (run 25343680860 job 74307194029) and is unrelated to this change.

Minor nits (non-blocking)

  • colorEnabled is evaluated once at module-load time, whereas chalk re-evaluates lazily on each call. If a host process programmatically flips FORCE_COLOR/NO_COLOR after @workflow/errors is imported, the shim won't pick up the change. In practice no one in this codebase does that, and the JSDoc on internal-chalk.ts is implicit about it — but if you wanted to be belt-and-braces, wrapping the env-var check inside sgr() would keep parity. Totally fine to leave as-is.

  • The __mocks__/chalk.ts deletion is correct, but worth noting that the module-graph entrypoint for the test mock changed from "the chalk package as resolved by node" to "this sibling file at a literal relative path". That's fine for this codebase but means the mock now relies on vi.mock('./internal-chalk.js', ...)paths rather than package identity — slightly more fragile if ansi.ts is ever moved to a different directory. Fine for now.

Approving.

@VaguelySerious
VaguelySerious merged commit 540a2ef into mainMay 5, 2026
175 of 177 checks passed
@VaguelySerious
VaguelySerious deleted the peter/errors-no-chalk branch May 5, 2026 00:47
@github-actionsgithub-actionsBot mentioned this pull request May 5, 2026
pranaygp added a commit that referenced this pull request May 5, 2026
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
pranaygp added a commit that referenced this pull request May 5, 2026
…ignal
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
Allow disabling step sourcemap with new `sourcemap` option in builders (#1842)
[ci] Enable Vercel-prod e2e for tanstack-start (#1904)
web: configure vercelPreset() for Vercel deployments (#1815)
[core] Combine flow+step bundle and process steps eagerly (#1338)
[world-vercel] Revert stream close control framing (#1891)
[tarballs] Use turbo to build workspace deps before packing (#1908)
# Conflicts:
#	packages/core/src/runtime/step-handler.test.ts
#	packages/core/src/runtime/step-handler.ts
#	packages/core/src/runtime/suspension-handler.ts
#	packages/core/src/step.test.ts
#	packages/world-local/src/storage/events-storage.ts
#	packages/world-postgres/src/drizzle/migrations/meta/_journal.json
Sign up for freeto 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.

2 participants

@VaguelySerious@TooTallNate
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

[errors] Replace chalk import with inline ANSI shim - #1915

Merged
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk
May 5, 2026
Merged

[errors] Replace chalk import with inline ANSI shim#1915
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

@workflow/errors/ansi imports chalk, which transitively require('os')s via supports-color. That module is reachable from the workflow-VM bundle through:

workflow → @workflow/core/workflow/index.ts
→ context-errors.ts
→ context-violation-error.ts
→ @workflow/errors/ansi ← imports chalk

The workflow VM has no require(), so importing chalk crashes every workflow at registration time:

./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)

Fix

Replace chalk with a 25-line inline SGR helper that exposes the same call surface (chalk.red, chalk.bold, …). Color detection mirrors chalk's defaults at a coarse level — FORCE_COLOR on, NO_COLOR off, otherwise gated on process.stdout.isTTY — so non-TTY logs and the workflow VM (no process) get plain text, matching the prior behavior in tests and in production log drains.

Drops the chalk runtime dependency from @workflow/errors and removes the now-unused __mocks__/chalk.ts Vitest mock. All 820 @workflow/core unit tests + 11 @workflow/errors snapshot tests pass.

Test plan

  • pnpm --filter @workflow/errors test (26 tests pass)
  • pnpm --filter @workflow/core test (820 unit tests pass; e2e excluded)
  • CI green
  • Reproduce: install latest tarballs in a v2-flow consumer (vade) and confirm step registration no longer throws ReferenceError: require is not defined

🤖 Generated with Claude Code

`@workflow/errors/ansi` is reachable from the workflow-VM bundle via
`@workflow/core/workflow` → `context-errors` → `context-violation-error`
→ here. The real `chalk` package pulls in `supports-color`, which calls
`require('os')` at module load — crashing every workflow with
`ReferenceError: require is not defined` in the sandboxed VM the moment
any user step is registered:
./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)
Replace `chalk` with a 25-line inline SGR helper exposing the same call
surface (`chalk.red`, `chalk.bold`, …). Color detection mirrors chalk's
default at a coarse level — `FORCE_COLOR` on, `NO_COLOR` off, otherwise
gated on `process.stdout.isTTY` — so non-TTY logs and the workflow VM
(no `process`) get plain text, matching previous behavior in tests and
in production log drains.
Drops the `chalk` runtime dependency from `@workflow/errors` and removes
the now-unused `__mocks__/chalk.ts` Vitest mock.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 47be549

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

This PR includes changesets to release 22 packages
NameType
@workflow/errorsPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/webPatch
workflowPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-vercelPatch
tarballsPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/world-testingPatch
@workflow/web-sharedPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

@github-actions

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.033s (-24.6% 🟢)1.005s (~)0.973s101.00x
💻 LocalExpress0.035s (-20.1% 🟢)1.006s (~)0.970s101.09x
🐘 PostgresExpress0.046s (-19.8% 🟢)1.011s (~)0.964s101.43x
💻 LocalNext.js (Turbopack)0.049s1.005s0.956s101.50x
🐘 PostgresNitro0.052s (-45.5% 🟢)1.011s (-3.0%)0.959s101.60x
🌐 RedisNext.js (Turbopack)0.054s1.005s0.951s101.65x
🐘 PostgresNext.js (Turbopack)0.057s1.011s0.953s101.76x
🌐 MongoDBNext.js (Turbopack)0.091s1.007s0.916s102.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.295s (+25.2% 🔺)2.353s (+10.2% 🔺)2.058s101.00x
▲ VercelNitro0.366s (-10.6% 🟢)2.033s (-19.0% 🟢)1.667s101.24x
▲ VercelNext.js (Turbopack)0.558s (+122.1% 🔺)2.742s (+17.5% 🔺)2.183s101.90x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.070s (-5.4% 🟢)2.007s (~)0.937s101.00x
💻 LocalExpress1.072s (-4.7%)2.007s (~)0.935s101.00x
🐘 PostgresExpress1.082s (-5.6% 🟢)2.009s (~)0.927s101.01x
🐘 PostgresNitro1.084s (-4.9%)2.009s (~)0.925s101.01x
💻 LocalNext.js (Turbopack)1.110s2.006s0.896s101.04x
🌐 RedisNext.js (Turbopack)1.111s2.008s0.897s101.04x
🐘 PostgresNext.js (Turbopack)1.114s2.010s0.897s101.04x
🌐 MongoDBNext.js (Turbopack)1.148s2.009s0.861s101.07x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.586s (-15.4% 🟢)3.902s (+2.5%)2.317s101.00x
▲ VercelNitro1.641s (-57.8% 🟢)4.080s (-30.9% 🟢)2.439s101.04x
▲ VercelNext.js (Turbopack)2.861s (+40.6% 🔺)4.792s (+25.1% 🔺)1.931s101.80x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express10.400s (-5.1% 🟢)11.014s (~)0.614s31.00x
🐘 PostgresNitro10.402s (-4.3%)11.015s (~)0.613s31.00x
💻 LocalExpress10.409s (-4.7%)11.022s (~)0.613s31.00x
💻 LocalNitro10.422s (-4.8%)11.023s (~)0.601s31.00x
🌐 MongoDBNext.js (Turbopack)10.655s11.023s0.368s31.02x
🐘 PostgresNext.js (Turbopack)10.667s11.015s0.348s31.03x
🌐 RedisNext.js (Turbopack)10.669s11.023s0.354s31.03x
💻 LocalNext.js (Turbopack)10.682s11.020s0.338s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.559s (-42.9% 🟢)15.361s (-38.8% 🟢)1.802s21.00x
▲ VercelExpress13.917s (-18.0% 🟢)15.853s (-20.8% 🟢)1.936s21.03x
▲ VercelNext.js (Turbopack)14.322s (-17.3% 🟢)16.622s (-14.3% 🟢)2.299s21.06x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.471s (-7.6% 🟢)14.017s (-6.7% 🟢)0.546s51.00x
💻 LocalExpress13.477s (-10.0% 🟢)14.027s (-6.7% 🟢)0.550s51.00x
🐘 PostgresNitro13.481s (-7.6% 🟢)14.021s (-6.7% 🟢)0.540s51.00x
💻 LocalNitro13.483s (-10.5% 🟢)14.027s (-12.5% 🟢)0.544s51.00x
🌐 MongoDBNext.js (Turbopack)13.835s14.025s0.190s51.03x
🌐 RedisNext.js (Turbopack)14.050s15.030s0.980s41.04x
💻 LocalNext.js (Turbopack)14.060s14.628s0.569s51.04x
🐘 PostgresNext.js (Turbopack)14.069s15.020s0.951s41.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.544s (-66.6% 🟢)23.739s (-64.4% 🟢)2.195s31.00x
▲ VercelExpress22.795s (-54.7% 🟢)25.107s (-52.2% 🟢)2.312s31.06x
▲ VercelNext.js (Turbopack)23.551s (-55.2% 🟢)26.276s (-51.9% 🟢)2.725s31.09x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express11.836s (-15.5% 🟢)12.015s (-17.7% 🟢)0.179s81.00x
💻 LocalNitro11.946s (-28.8% 🟢)12.272s (-27.9% 🟢)0.326s81.01x
💻 LocalExpress11.949s (-28.0% 🟢)12.146s (-28.7% 🟢)0.197s81.01x
🐘 PostgresNitro11.970s (-14.3% 🟢)12.394s (-13.4% 🟢)0.424s81.01x
🌐 MongoDBNext.js (Turbopack)12.590s13.024s0.433s71.06x
💻 LocalNext.js (Turbopack)12.972s13.452s0.480s71.10x
🌐 RedisNext.js (Turbopack)13.100s13.884s0.784s71.11x
🐘 PostgresNext.js (Turbopack)13.185s14.020s0.835s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express34.548s (-71.5% 🟢)36.366s (-70.6% 🟢)1.818s31.00x
▲ VercelNitro35.156s (-91.7% 🟢)37.155s (-91.2% 🟢)1.999s31.02x
▲ VercelNext.js (Turbopack)35.694s (-90.9% 🟢)37.608s (-90.5% 🟢)1.913s31.03x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.147s (-9.0% 🟢)2.007s (~)0.860s151.00x
🐘 PostgresNitro1.156s (-9.3% 🟢)2.007s (~)0.851s151.01x
💻 LocalNitro1.188s (-27.2% 🟢)2.006s (-3.3%)0.818s151.04x
💻 LocalExpress1.197s (-19.6% 🟢)2.006s (~)0.808s151.04x
🐘 PostgresNext.js (Turbopack)1.213s2.008s0.794s151.06x
🌐 RedisNext.js (Turbopack)1.268s2.006s0.738s151.10x
💻 LocalNext.js (Turbopack)1.378s2.073s0.695s151.20x
🌐 MongoDBNext.js (Turbopack)2.011s2.591s0.580s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.918s (+3.6%)4.656s (+7.7% 🔺)1.738s71.00x
▲ VercelNext.js (Turbopack)4.329s (+27.4% 🔺)6.218s (+26.1% 🔺)1.889s51.48x
▲ VercelExpress4.519s (+58.0% 🔺)6.298s (+36.2% 🔺)1.779s51.55x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.226s (-48.1% 🟢)2.007s (-33.3% 🟢)0.780s151.00x
🐘 PostgresNitro1.234s (-47.5% 🟢)2.007s (-33.3% 🟢)0.773s151.01x
🐘 PostgresNext.js (Turbopack)1.356s2.007s0.651s151.11x
💻 LocalNitro1.791s (-43.0% 🟢)2.007s (-48.4% 🟢)0.216s151.46x
💻 LocalExpress1.792s (-39.3% 🟢)2.006s (-41.9% 🟢)0.214s151.46x
💻 LocalNext.js (Turbopack)1.806s2.073s0.267s151.47x
🌐 RedisNext.js (Turbopack)2.385s3.008s0.623s101.94x
🌐 MongoDBNext.js (Turbopack)3.564s4.008s0.444s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.458s (+23.2% 🔺)6.268s (+22.7% 🔺)1.811s51.00x
▲ VercelNext.js (Turbopack)5.613s (-20.9% 🟢)7.797s (-12.4% 🟢)2.184s41.26x
▲ VercelNitro6.080s (+50.1% 🔺)8.092s (+36.7% 🔺)2.012s41.36x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.364s (-60.9% 🟢)2.006s (-50.0% 🟢)0.642s151.00x
🐘 PostgresNitro1.374s (-60.5% 🟢)2.007s (-49.9% 🟢)0.633s151.01x
🐘 PostgresNext.js (Turbopack)1.627s2.007s0.380s151.19x
🌐 RedisNext.js (Turbopack)3.621s4.009s0.389s82.65x
💻 LocalNext.js (Turbopack)5.000s5.511s0.511s63.67x
💻 LocalNitro5.331s (-36.2% 🟢)5.847s (-35.2% 🟢)0.517s63.91x
💻 LocalExpress5.417s (-35.0% 🟢)5.679s (-37.1% 🟢)0.263s63.97x
🌐 MongoDBNext.js (Turbopack)6.223s7.012s0.788s54.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.257s (+47.6% 🔺)8.386s (+36.9% 🔺)2.129s41.00x
▲ VercelNext.js (Turbopack)7.288s (-18.3% 🟢)9.279s (-15.3% 🟢)1.991s41.16x
▲ VercelNitro8.700s (+146.8% 🔺)10.899s (+96.9% 🔺)2.199s31.39x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.153s (-8.3% 🟢)2.009s (~)0.857s151.00x
🐘 PostgresExpress1.154s (-8.2% 🟢)2.009s (~)0.855s151.00x
🐘 PostgresNext.js (Turbopack)1.210s2.007s0.797s151.05x
🌐 RedisNext.js (Turbopack)1.257s2.006s0.749s151.09x
💻 LocalNext.js (Turbopack)1.353s2.006s0.653s151.17x
💻 LocalExpress1.384s (-26.9% 🟢)2.006s (-15.1% 🟢)0.622s151.20x
💻 LocalNitro1.397s (-25.1% 🟢)2.007s (-14.3% 🟢)0.610s151.21x
🌐 MongoDBNext.js (Turbopack)2.013s2.590s0.577s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.363s (-8.5% 🟢)4.099s (-5.8% 🟢)1.736s81.00x
▲ VercelNitro3.169s (+28.9% 🔺)5.005s (+20.0% 🔺)1.836s71.34x
▲ VercelNext.js (Turbopack)3.909s (+33.3% 🔺)6.008s (+29.4% 🔺)2.100s51.65x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.231s (-47.4% 🟢)2.009s (-33.3% 🟢)0.778s151.00x
🐘 PostgresExpress1.232s (-47.4% 🟢)2.007s (-33.3% 🟢)0.776s151.00x
🐘 PostgresNext.js (Turbopack)1.354s2.008s0.654s151.10x
💻 LocalNitro2.049s (-33.1% 🟢)2.509s (-35.4% 🟢)0.460s121.67x
💻 LocalNext.js (Turbopack)2.074s2.735s0.661s111.69x
💻 LocalExpress2.135s (-31.8% 🟢)2.593s (-31.1% 🟢)0.457s121.74x
🌐 RedisNext.js (Turbopack)2.370s3.008s0.639s101.93x
🌐 MongoDBNext.js (Turbopack)3.577s4.009s0.432s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.907s (+20.8% 🔺)5.774s (+13.7% 🔺)1.867s61.00x
▲ VercelExpress4.205s (+31.7% 🔺)6.052s (+26.3% 🔺)1.847s51.08x
▲ VercelNext.js (Turbopack)4.751s (+51.2% 🔺)6.510s (+44.0% 🔺)1.759s51.22x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.358s (-61.2% 🟢)2.008s (-49.9% 🟢)0.650s151.00x
🐘 PostgresNitro1.366s (-60.7% 🟢)2.008s (-49.9% 🟢)0.642s151.01x
🐘 PostgresNext.js (Turbopack)1.651s2.009s0.358s151.22x
🌐 RedisNext.js (Turbopack)3.648s4.010s0.362s82.69x
💻 LocalExpress5.565s (-36.8% 🟢)6.015s (-35.1% 🟢)0.450s54.10x
💻 LocalNext.js (Turbopack)5.704s6.214s0.510s54.20x
💻 LocalNitro5.946s (-35.0% 🟢)6.348s (-36.7% 🟢)0.402s64.38x
🌐 MongoDBNext.js (Turbopack)6.250s7.013s0.763s54.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.891s (+15.7% 🔺)7.641s (+12.1% 🔺)1.750s41.00x
▲ VercelExpress6.208s (-3.2%)7.682s (-6.1% 🟢)1.474s41.05x
▲ VercelNext.js (Turbopack)6.830s (+1.1%)8.929s (+4.5%)2.099s41.16x

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

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.451s (-46.2% 🟢)1.006s (-1.7%)0.555s601.00x
🐘 PostgresNitro0.453s (-44.7% 🟢)1.006s (~)0.553s601.01x
💻 LocalNitro0.471s (-52.0% 🟢)1.004s (-8.2% 🟢)0.533s601.04x
💻 LocalExpress0.506s (-48.6% 🟢)1.022s (-5.1% 🟢)0.515s591.12x
🌐 MongoDBNext.js (Turbopack)0.597s1.006s0.409s601.32x
🌐 RedisNext.js (Turbopack)0.639s1.004s0.365s601.42x
🐘 PostgresNext.js (Turbopack)0.662s1.006s0.344s601.47x
💻 LocalNext.js (Turbopack)0.711s1.004s0.293s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.747s (-78.5% 🟢)6.670s (-72.2% 🟢)1.923s101.00x
▲ VercelExpress4.916s (-74.1% 🟢)6.991s (-67.2% 🟢)2.075s91.04x
▲ VercelNext.js (Turbopack)6.804s (-53.1% 🟢)8.802s (-45.3% 🟢)1.998s71.43x

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

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.032s (-47.8% 🟢)1.458s (-35.4% 🟢)0.426s621.00x
🐘 PostgresNitro1.047s (-45.7% 🟢)1.507s (-28.3% 🟢)0.459s601.01x
💻 LocalExpress1.184s (-60.7% 🟢)2.006s (-44.1% 🟢)0.821s451.15x
💻 LocalNitro1.191s (-60.8% 🟢)2.006s (-46.6% 🟢)0.815s451.15x
🌐 MongoDBNext.js (Turbopack)1.503s2.007s0.504s451.46x
🐘 PostgresNext.js (Turbopack)1.587s2.007s0.419s451.54x
🌐 RedisNext.js (Turbopack)1.611s2.028s0.417s451.56x
💻 LocalNext.js (Turbopack)1.741s2.005s0.265s451.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.285s (-64.4% 🟢)14.394s (-60.9% 🟢)2.108s71.00x
▲ VercelNitro12.810s (-67.6% 🟢)15.636s (-62.1% 🟢)2.825s61.04x
▲ VercelNext.js (Turbopack)15.611s (-68.7% 🟢)18.106s (-65.0% 🟢)2.494s61.27x

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

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.878s (-52.9% 🟢)2.076s (-52.5% 🟢)0.198s581.00x
🐘 PostgresNitro2.079s (-49.3% 🟢)2.550s (-44.6% 🟢)0.470s481.11x
💻 LocalNitro2.680s (-71.2% 🟢)3.008s (-70.0% 🟢)0.327s401.43x
💻 LocalExpress2.692s (-70.8% 🟢)3.008s (-70.0% 🟢)0.316s401.43x
🐘 PostgresNext.js (Turbopack)3.092s4.009s0.917s301.65x
🌐 RedisNext.js (Turbopack)3.096s3.880s0.783s311.65x
🌐 MongoDBNext.js (Turbopack)3.670s4.013s0.343s301.95x
💻 LocalNext.js (Turbopack)3.704s4.008s0.303s301.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro39.197s (-59.6% 🟢)42.019s (-57.3% 🟢)2.822s31.00x
▲ VercelExpress40.332s (-69.0% 🟢)42.693s (-67.7% 🟢)2.361s31.03x
▲ VercelNext.js (Turbopack)44.774s (-58.2% 🟢)48.551s (-55.4% 🟢)3.777s31.14x

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

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.176s (-37.5% 🟢)1.006s (~)0.829s601.00x
🐘 PostgresNitro0.182s (-35.8% 🟢)1.005s (~)0.824s601.03x
🐘 PostgresNext.js (Turbopack)0.232s1.006s0.774s601.32x
🌐 RedisNext.js (Turbopack)0.242s1.004s0.762s601.37x
💻 LocalExpress0.439s (-21.6% 🟢)1.004s (~)0.565s602.49x
💻 LocalNitro0.474s (-21.6% 🟢)1.021s (~)0.547s592.69x
💻 LocalNext.js (Turbopack)0.539s1.004s0.465s603.06x
🌐 MongoDBNext.js (Turbopack)1.032s1.825s0.793s335.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.231s (+34.3% 🔺)4.189s (+25.0% 🔺)1.958s151.00x
▲ VercelExpress2.259s (+15.6% 🔺)4.431s (+21.8% 🔺)2.172s141.01x
▲ VercelNext.js (Turbopack)4.335s (+114.4% 🔺)6.320s (+66.6% 🔺)1.984s101.94x

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

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.309s (-39.4% 🟢)1.006s (~)0.697s901.00x
🐘 PostgresNitro0.312s (-37.2% 🟢)1.006s (~)0.694s901.01x
🌐 RedisNext.js (Turbopack)0.412s1.004s0.592s901.33x
🐘 PostgresNext.js (Turbopack)0.450s1.006s0.555s901.46x
💻 LocalNitro2.179s (-14.2% 🟢)3.008s (~)0.829s307.05x
💻 LocalExpress2.268s (-9.8% 🟢)2.911s (-3.3%)0.644s317.34x
💻 LocalNext.js (Turbopack)2.382s2.977s0.595s317.71x
🌐 MongoDBNext.js (Turbopack)2.594s3.008s0.414s308.39x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express7.284s (+139.1% 🔺)9.227s (+91.9% 🔺)1.943s101.00x
▲ VercelNitro7.305s (+126.5% 🔺)9.647s (+100.1% 🔺)2.342s101.00x
▲ VercelNext.js (Turbopack)8.118s (+129.6% 🔺)10.545s (+103.1% 🔺)2.427s91.11x

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

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.618s (-24.5% 🟢)1.006s (-1.1%)0.388s1201.00x
🐘 PostgresNitro0.649s (-17.9% 🟢)1.006s (~)0.357s1201.05x
🌐 RedisNext.js (Turbopack)0.770s1.004s0.233s1201.25x
🐘 PostgresNext.js (Turbopack)0.933s1.219s0.286s991.51x
🌐 MongoDBNext.js (Turbopack)5.361s6.012s0.651s208.67x
💻 LocalExpress10.227s (-8.6% 🟢)10.863s (-9.0% 🟢)0.636s1216.54x
💻 LocalNitro10.251s (-8.4% 🟢)10.781s (-7.6% 🟢)0.530s1216.58x
💻 LocalNext.js (Turbopack)10.950s11.757s0.807s1117.71x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express20.578s (+177.3% 🔺)23.078s (+149.6% 🔺)2.500s61.00x
▲ VercelNext.js (Turbopack)21.742s (+110.5% 🔺)24.590s (+100.1% 🔺)2.848s51.06x
▲ VercelNitro25.208s (+226.4% 🔺)27.039s (+187.6% 🔺)1.831s51.22x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.129s (+450.3% 🔺)1.997s (+99.9% 🔺)0.001s (-18.8% 🟢)2.009s (+98.7% 🔺)0.881s101.00x
💻 LocalExpress1.131s (+468.1% 🔺)2.005s (+99.6% 🔺)0.012s (+2.5%)2.019s (+98.4% 🔺)0.888s101.00x
💻 LocalNitro1.132s (+429.6% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.021s (+98.3% 🔺)0.889s101.00x
🐘 PostgresNitro1.149s (+460.7% 🔺)1.994s (+99.5% 🔺)0.002s (+6.7% 🔺)2.012s (+99.0% 🔺)0.863s101.02x
💻 LocalNext.js (Turbopack)1.178s2.003s0.012s2.019s0.840s101.04x
🐘 PostgresNext.js (Turbopack)1.192s2.002s0.001s2.010s0.818s101.06x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.184s (-43.0% 🟢)3.612s (-31.5% 🟢)1.614s (+117.5% 🔺)5.719s (-11.8% 🟢)3.535s101.00x
▲ VercelExpress2.198s (-12.3% 🟢)3.538s (-13.5% 🟢)1.751s (+82.3% 🔺)5.836s (+4.4%)3.637s101.01x
▲ VercelNext.js (Turbopack)4.942s (-27.9% 🟢)5.438s (-37.1% 🟢)1.145s (+81.2% 🔺)8.364s (-14.6% 🟢)3.421s102.26x

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

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.496s (+137.4% 🔺)2.000s (+98.7% 🔺)0.003s (-9.5% 🟢)2.026s (+98.1% 🔺)0.531s301.00x
🐘 PostgresNitro1.507s (+141.5% 🔺)2.002s (+98.8% 🔺)0.004s (-8.9% 🟢)2.024s (+97.9% 🔺)0.517s301.01x
💻 LocalNitro1.538s (+83.4% 🔺)2.012s (+98.8% 🔺)0.010s (+11.7% 🔺)2.025s (+81.4% 🔺)0.486s301.03x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.004s2.025s0.367s301.11x
💻 LocalExpress1.716s (+126.6% 🔺)2.012s (+95.5% 🔺)0.009s (-2.1%)2.202s (+111.8% 🔺)0.486s281.15x
💻 LocalNext.js (Turbopack)2.055s2.009s0.010s2.423s0.368s251.37x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.789s (-80.3% 🟢)7.537s (-75.5% 🟢)0.268s (+139.0% 🔺)8.404s (-73.6% 🟢)2.615s81.00x
▲ VercelExpress6.045s (-7.1% 🟢)7.653s (-4.5%)0.313s (-23.5% 🟢)8.503s (-3.8%)2.457s81.04x
▲ VercelNext.js (Turbopack)12.326s (-27.1% 🟢)13.810s (-24.3% 🟢)0.429s (+103.2% 🔺)15.364s (-18.9% 🟢)3.038s42.13x

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

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.660s (-31.8% 🟢)1.031s (-17.4% 🟢)0.000s (+24.1% 🔺)1.048s (-16.7% 🟢)0.387s581.00x
🐘 PostgresExpress0.688s (-28.4% 🟢)1.061s (-17.0% 🟢)0.000s (+105.4% 🔺)1.079s (-17.4% 🟢)0.391s561.04x
🐘 PostgresNext.js (Turbopack)0.783s1.090s0.000s1.097s0.314s561.19x
💻 LocalNitro1.345s (+10.0% 🔺)2.016s (~)0.000s (+333.3% 🔺)2.018s (~)0.674s302.04x
💻 LocalNext.js (Turbopack)1.400s2.014s0.000s2.017s0.617s302.12x
💻 LocalExpress1.515s (+23.7% 🔺)2.015s (~)0.000s (-35.7% 🟢)2.196s (+8.6% 🔺)0.681s282.29x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.737s (+22.5% 🔺)5.067s (+15.4% 🔺)0.000s (+18.2% 🔺)5.709s (+18.7% 🔺)1.972s111.00x
▲ VercelExpress4.218s (+12.8% 🔺)5.770s (+13.1% 🔺)0.000s (+50.0% 🔺)6.218s (+12.4% 🔺)2.000s111.13x
▲ VercelNext.js (Turbopack)6.160s (-39.5% 🟢)6.979s (-39.4% 🟢)0.000s (NaN%)8.458s (-29.8% 🟢)2.298s81.65x

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

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.286s (-28.2% 🟢)2.001s (-6.6% 🟢)0.000s (-100.0% 🟢)2.020s (-7.1% 🟢)0.734s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.067s (-5.1% 🟢)0.000s (+Infinity% 🔺)2.115s (-3.8%)0.730s291.08x
🐘 PostgresNext.js (Turbopack)1.629s2.225s0.000s2.239s0.610s271.27x
💻 LocalNext.js (Turbopack)2.539s3.125s0.000s3.128s0.589s201.97x
💻 LocalExpress3.054s (-11.9% 🟢)3.903s (-3.2%)0.000s (-45.3% 🟢)3.905s (-3.2%)0.852s162.37x
💻 LocalNitro3.180s (-6.1% 🟢)3.966s (-1.6%)0.000s (-41.4% 🟢)3.971s (-1.6%)0.791s162.47x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.004s (+30.9% 🔺)7.517s (+24.8% 🔺)0.000s (+Infinity% 🔺)8.103s (+25.5% 🔺)2.099s81.00x
▲ VercelNitro6.750s (+64.9% 🔺)8.558s (+59.2% 🔺)0.000s (-100.0% 🟢)9.120s (+57.4% 🔺)2.370s71.12x
▲ VercelNext.js (Turbopack)8.561s (+52.4% 🔺)9.916s (+42.0% 🔺)0.000s (-100.0% 🟢)11.240s (+49.1% 🔺)2.679s61.43x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro11/21
🐘 PostgresExpress17/21
▲ VercelNitro11/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres19/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres17/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

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production85902191078
✅ 💻 Local Development95702191176
✅ 📦 Local Production95702191176
✅ 🐘 Local Postgres95702191176
✅ 🪟 Windows980098
✅ 📋 Other5100176686
Total4338010525390

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro72026
✅ example72026
✅ express72026
✅ fastify72026
✅ hono72026
✅ nextjs-turbopack9602
✅ nextjs-webpack9602
✅ nitro72026
✅ nuxt72026
✅ sveltekit9107
✅ vite72026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9800
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable73025
✅ e2e-local-dev-tanstack-start-stable73025
✅ e2e-local-postgres-nest-stable73025
✅ e2e-local-postgres-tanstack-start-stable73025
✅ e2e-local-prod-nest-stable73025
✅ e2e-local-prod-tanstack-start-stable73025
✅ e2e-vercel-prod-tanstack-start72026

📋 View full workflow run

Move the inline ANSI shim out of `ansi.ts` into its own
`./internal-chalk.ts` so tests can swap it for a tag-emitting mock via
`vi.mock('./internal-chalk.js', …)`. Snapshots for `Ansi.code` /
`Ansi.hint` / `Ansi.note` / `Ansi.help` / `Ansi.docs` go back to the
readable HTML-like form (`<blue><b>hint:</b> try reloading</blue>`),
making it obvious from the test file which fragments are colored and
how — the same review affordance the previous `__mocks__/chalk.ts`
mock provided before the chalk dep was dropped.
`Ansi.frame` and `Ansi.inline` remain plain in snapshots because they
don't apply any styling themselves; that matches the pre-fix behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. The diagnosis is correct, the fix is appropriately surgical, and the shim is well-written.

Verified

Reachability claim — confirmed: packages/core/src/workflow/index.tscontext-errors.tscontext-violation-error.tsimport * as Ansi from '@workflow/errors/ansi'. So any consumer that uses workflow-context-checked APIs (createHook, createWebhook, getWorkflowMetadata, etc.) ends up dragging the entire ansi/chalk transitive graph into the workflow VM bundle.

Output parity — for the only nested call in ansi.ts (code(): chalk.italic(\${chalk.dim('`')}${str}${chalk.dim('`')}`)), the shim produces byte-for-byte the same SGR sequence as chalk@5.6.2`:

\x1b[3m\x1b[2m`\x1b[22mfoo\x1b[2m`\x1b[22m\x1b[23m

The 22 (normal intensity) close after each dim correctly leaves italic (3) on, since 22 only resets bold/dim — same as chalk's behavior.

Bundle effect — built workbench/example from this branch and grepped the workflow VM bundle (the string passed to workflowEntrypoint):

$ grep -cE "supports-color|require\(['\"](node:)?os['\"]|chalk|internal_chalk" /tmp/vm-bundle.txt
0

No chalk, no supports-color, no os anywhere. (The example workbench's VM bundle didn't actually pull in context-violation-error due to tree-shaking in this particular workflow set, so the chalk references were already absent on main for this specific bundle — but the change is still correct, and downstream consumers like vade that exercise the workflow-context paths will get the fix.)

Testspnpm --filter @workflow/errors test passes (4 files, 26 tests including all 11 ansi.test.ts snapshots), pnpm --filter @workflow/core test passes (820 tests).

CI failures are pre-existing flakes

The two red checks (Unit Tests (ubuntu-latest), E2E Required Check) are both from the same flaky world-postgres test:

FAIL test/spec.test.ts > sequential steps with stream complete in a single flow invocation
AssertionError: expected +0 to be 1 // Object.is equality

This same test fails on origin/main HEAD (run 25343680860 job 74307194029) and is unrelated to this change.

Minor nits (non-blocking)

  • colorEnabled is evaluated once at module-load time, whereas chalk re-evaluates lazily on each call. If a host process programmatically flips FORCE_COLOR/NO_COLOR after @workflow/errors is imported, the shim won't pick up the change. In practice no one in this codebase does that, and the JSDoc on internal-chalk.ts is implicit about it — but if you wanted to be belt-and-braces, wrapping the env-var check inside sgr() would keep parity. Totally fine to leave as-is.

  • The __mocks__/chalk.ts deletion is correct, but worth noting that the module-graph entrypoint for the test mock changed from "the chalk package as resolved by node" to "this sibling file at a literal relative path". That's fine for this codebase but means the mock now relies on vi.mock('./internal-chalk.js', ...)paths rather than package identity — slightly more fragile if ansi.ts is ever moved to a different directory. Fine for now.

Approving.

@VaguelySerious
VaguelySerious merged commit 540a2ef into mainMay 5, 2026
175 of 177 checks passed
@VaguelySerious
VaguelySerious deleted the peter/errors-no-chalk branch May 5, 2026 00:47
@github-actionsgithub-actionsBot mentioned this pull request May 5, 2026
pranaygp added a commit that referenced this pull request May 5, 2026
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
pranaygp added a commit that referenced this pull request May 5, 2026
…ignal
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
Allow disabling step sourcemap with new `sourcemap` option in builders (#1842)
[ci] Enable Vercel-prod e2e for tanstack-start (#1904)
web: configure vercelPreset() for Vercel deployments (#1815)
[core] Combine flow+step bundle and process steps eagerly (#1338)
[world-vercel] Revert stream close control framing (#1891)
[tarballs] Use turbo to build workspace deps before packing (#1908)
# Conflicts:
#	packages/core/src/runtime/step-handler.test.ts
#	packages/core/src/runtime/step-handler.ts
#	packages/core/src/runtime/suspension-handler.ts
#	packages/core/src/step.test.ts
#	packages/world-local/src/storage/events-storage.ts
#	packages/world-postgres/src/drizzle/migrations/meta/_journal.json
Sign up for freeto 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.

2 participants

@VaguelySerious@TooTallNate
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[errors] Replace chalk import with inline ANSI shim - #1915

Merged
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk
May 5, 2026
Merged

[errors] Replace chalk import with inline ANSI shim#1915
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

@workflow/errors/ansi imports chalk, which transitively require('os')s via supports-color. That module is reachable from the workflow-VM bundle through:

workflow → @workflow/core/workflow/index.ts
→ context-errors.ts
→ context-violation-error.ts
→ @workflow/errors/ansi ← imports chalk

The workflow VM has no require(), so importing chalk crashes every workflow at registration time:

./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)

Fix

Replace chalk with a 25-line inline SGR helper that exposes the same call surface (chalk.red, chalk.bold, …). Color detection mirrors chalk's defaults at a coarse level — FORCE_COLOR on, NO_COLOR off, otherwise gated on process.stdout.isTTY — so non-TTY logs and the workflow VM (no process) get plain text, matching the prior behavior in tests and in production log drains.

Drops the chalk runtime dependency from @workflow/errors and removes the now-unused __mocks__/chalk.ts Vitest mock. All 820 @workflow/core unit tests + 11 @workflow/errors snapshot tests pass.

Test plan

  • pnpm --filter @workflow/errors test (26 tests pass)
  • pnpm --filter @workflow/core test (820 unit tests pass; e2e excluded)
  • CI green
  • Reproduce: install latest tarballs in a v2-flow consumer (vade) and confirm step registration no longer throws ReferenceError: require is not defined

🤖 Generated with Claude Code

`@workflow/errors/ansi` is reachable from the workflow-VM bundle via
`@workflow/core/workflow` → `context-errors` → `context-violation-error`
→ here. The real `chalk` package pulls in `supports-color`, which calls
`require('os')` at module load — crashing every workflow with
`ReferenceError: require is not defined` in the sandboxed VM the moment
any user step is registered:
./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)
Replace `chalk` with a 25-line inline SGR helper exposing the same call
surface (`chalk.red`, `chalk.bold`, …). Color detection mirrors chalk's
default at a coarse level — `FORCE_COLOR` on, `NO_COLOR` off, otherwise
gated on `process.stdout.isTTY` — so non-TTY logs and the workflow VM
(no `process`) get plain text, matching previous behavior in tests and
in production log drains.
Drops the `chalk` runtime dependency from `@workflow/errors` and removes
the now-unused `__mocks__/chalk.ts` Vitest mock.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 47be549

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

This PR includes changesets to release 22 packages
NameType
@workflow/errorsPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/webPatch
workflowPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-vercelPatch
tarballsPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/world-testingPatch
@workflow/web-sharedPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

@github-actions

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.033s (-24.6% 🟢)1.005s (~)0.973s101.00x
💻 LocalExpress0.035s (-20.1% 🟢)1.006s (~)0.970s101.09x
🐘 PostgresExpress0.046s (-19.8% 🟢)1.011s (~)0.964s101.43x
💻 LocalNext.js (Turbopack)0.049s1.005s0.956s101.50x
🐘 PostgresNitro0.052s (-45.5% 🟢)1.011s (-3.0%)0.959s101.60x
🌐 RedisNext.js (Turbopack)0.054s1.005s0.951s101.65x
🐘 PostgresNext.js (Turbopack)0.057s1.011s0.953s101.76x
🌐 MongoDBNext.js (Turbopack)0.091s1.007s0.916s102.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.295s (+25.2% 🔺)2.353s (+10.2% 🔺)2.058s101.00x
▲ VercelNitro0.366s (-10.6% 🟢)2.033s (-19.0% 🟢)1.667s101.24x
▲ VercelNext.js (Turbopack)0.558s (+122.1% 🔺)2.742s (+17.5% 🔺)2.183s101.90x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.070s (-5.4% 🟢)2.007s (~)0.937s101.00x
💻 LocalExpress1.072s (-4.7%)2.007s (~)0.935s101.00x
🐘 PostgresExpress1.082s (-5.6% 🟢)2.009s (~)0.927s101.01x
🐘 PostgresNitro1.084s (-4.9%)2.009s (~)0.925s101.01x
💻 LocalNext.js (Turbopack)1.110s2.006s0.896s101.04x
🌐 RedisNext.js (Turbopack)1.111s2.008s0.897s101.04x
🐘 PostgresNext.js (Turbopack)1.114s2.010s0.897s101.04x
🌐 MongoDBNext.js (Turbopack)1.148s2.009s0.861s101.07x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.586s (-15.4% 🟢)3.902s (+2.5%)2.317s101.00x
▲ VercelNitro1.641s (-57.8% 🟢)4.080s (-30.9% 🟢)2.439s101.04x
▲ VercelNext.js (Turbopack)2.861s (+40.6% 🔺)4.792s (+25.1% 🔺)1.931s101.80x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express10.400s (-5.1% 🟢)11.014s (~)0.614s31.00x
🐘 PostgresNitro10.402s (-4.3%)11.015s (~)0.613s31.00x
💻 LocalExpress10.409s (-4.7%)11.022s (~)0.613s31.00x
💻 LocalNitro10.422s (-4.8%)11.023s (~)0.601s31.00x
🌐 MongoDBNext.js (Turbopack)10.655s11.023s0.368s31.02x
🐘 PostgresNext.js (Turbopack)10.667s11.015s0.348s31.03x
🌐 RedisNext.js (Turbopack)10.669s11.023s0.354s31.03x
💻 LocalNext.js (Turbopack)10.682s11.020s0.338s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.559s (-42.9% 🟢)15.361s (-38.8% 🟢)1.802s21.00x
▲ VercelExpress13.917s (-18.0% 🟢)15.853s (-20.8% 🟢)1.936s21.03x
▲ VercelNext.js (Turbopack)14.322s (-17.3% 🟢)16.622s (-14.3% 🟢)2.299s21.06x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.471s (-7.6% 🟢)14.017s (-6.7% 🟢)0.546s51.00x
💻 LocalExpress13.477s (-10.0% 🟢)14.027s (-6.7% 🟢)0.550s51.00x
🐘 PostgresNitro13.481s (-7.6% 🟢)14.021s (-6.7% 🟢)0.540s51.00x
💻 LocalNitro13.483s (-10.5% 🟢)14.027s (-12.5% 🟢)0.544s51.00x
🌐 MongoDBNext.js (Turbopack)13.835s14.025s0.190s51.03x
🌐 RedisNext.js (Turbopack)14.050s15.030s0.980s41.04x
💻 LocalNext.js (Turbopack)14.060s14.628s0.569s51.04x
🐘 PostgresNext.js (Turbopack)14.069s15.020s0.951s41.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.544s (-66.6% 🟢)23.739s (-64.4% 🟢)2.195s31.00x
▲ VercelExpress22.795s (-54.7% 🟢)25.107s (-52.2% 🟢)2.312s31.06x
▲ VercelNext.js (Turbopack)23.551s (-55.2% 🟢)26.276s (-51.9% 🟢)2.725s31.09x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express11.836s (-15.5% 🟢)12.015s (-17.7% 🟢)0.179s81.00x
💻 LocalNitro11.946s (-28.8% 🟢)12.272s (-27.9% 🟢)0.326s81.01x
💻 LocalExpress11.949s (-28.0% 🟢)12.146s (-28.7% 🟢)0.197s81.01x
🐘 PostgresNitro11.970s (-14.3% 🟢)12.394s (-13.4% 🟢)0.424s81.01x
🌐 MongoDBNext.js (Turbopack)12.590s13.024s0.433s71.06x
💻 LocalNext.js (Turbopack)12.972s13.452s0.480s71.10x
🌐 RedisNext.js (Turbopack)13.100s13.884s0.784s71.11x
🐘 PostgresNext.js (Turbopack)13.185s14.020s0.835s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express34.548s (-71.5% 🟢)36.366s (-70.6% 🟢)1.818s31.00x
▲ VercelNitro35.156s (-91.7% 🟢)37.155s (-91.2% 🟢)1.999s31.02x
▲ VercelNext.js (Turbopack)35.694s (-90.9% 🟢)37.608s (-90.5% 🟢)1.913s31.03x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.147s (-9.0% 🟢)2.007s (~)0.860s151.00x
🐘 PostgresNitro1.156s (-9.3% 🟢)2.007s (~)0.851s151.01x
💻 LocalNitro1.188s (-27.2% 🟢)2.006s (-3.3%)0.818s151.04x
💻 LocalExpress1.197s (-19.6% 🟢)2.006s (~)0.808s151.04x
🐘 PostgresNext.js (Turbopack)1.213s2.008s0.794s151.06x
🌐 RedisNext.js (Turbopack)1.268s2.006s0.738s151.10x
💻 LocalNext.js (Turbopack)1.378s2.073s0.695s151.20x
🌐 MongoDBNext.js (Turbopack)2.011s2.591s0.580s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.918s (+3.6%)4.656s (+7.7% 🔺)1.738s71.00x
▲ VercelNext.js (Turbopack)4.329s (+27.4% 🔺)6.218s (+26.1% 🔺)1.889s51.48x
▲ VercelExpress4.519s (+58.0% 🔺)6.298s (+36.2% 🔺)1.779s51.55x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.226s (-48.1% 🟢)2.007s (-33.3% 🟢)0.780s151.00x
🐘 PostgresNitro1.234s (-47.5% 🟢)2.007s (-33.3% 🟢)0.773s151.01x
🐘 PostgresNext.js (Turbopack)1.356s2.007s0.651s151.11x
💻 LocalNitro1.791s (-43.0% 🟢)2.007s (-48.4% 🟢)0.216s151.46x
💻 LocalExpress1.792s (-39.3% 🟢)2.006s (-41.9% 🟢)0.214s151.46x
💻 LocalNext.js (Turbopack)1.806s2.073s0.267s151.47x
🌐 RedisNext.js (Turbopack)2.385s3.008s0.623s101.94x
🌐 MongoDBNext.js (Turbopack)3.564s4.008s0.444s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.458s (+23.2% 🔺)6.268s (+22.7% 🔺)1.811s51.00x
▲ VercelNext.js (Turbopack)5.613s (-20.9% 🟢)7.797s (-12.4% 🟢)2.184s41.26x
▲ VercelNitro6.080s (+50.1% 🔺)8.092s (+36.7% 🔺)2.012s41.36x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.364s (-60.9% 🟢)2.006s (-50.0% 🟢)0.642s151.00x
🐘 PostgresNitro1.374s (-60.5% 🟢)2.007s (-49.9% 🟢)0.633s151.01x
🐘 PostgresNext.js (Turbopack)1.627s2.007s0.380s151.19x
🌐 RedisNext.js (Turbopack)3.621s4.009s0.389s82.65x
💻 LocalNext.js (Turbopack)5.000s5.511s0.511s63.67x
💻 LocalNitro5.331s (-36.2% 🟢)5.847s (-35.2% 🟢)0.517s63.91x
💻 LocalExpress5.417s (-35.0% 🟢)5.679s (-37.1% 🟢)0.263s63.97x
🌐 MongoDBNext.js (Turbopack)6.223s7.012s0.788s54.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.257s (+47.6% 🔺)8.386s (+36.9% 🔺)2.129s41.00x
▲ VercelNext.js (Turbopack)7.288s (-18.3% 🟢)9.279s (-15.3% 🟢)1.991s41.16x
▲ VercelNitro8.700s (+146.8% 🔺)10.899s (+96.9% 🔺)2.199s31.39x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.153s (-8.3% 🟢)2.009s (~)0.857s151.00x
🐘 PostgresExpress1.154s (-8.2% 🟢)2.009s (~)0.855s151.00x
🐘 PostgresNext.js (Turbopack)1.210s2.007s0.797s151.05x
🌐 RedisNext.js (Turbopack)1.257s2.006s0.749s151.09x
💻 LocalNext.js (Turbopack)1.353s2.006s0.653s151.17x
💻 LocalExpress1.384s (-26.9% 🟢)2.006s (-15.1% 🟢)0.622s151.20x
💻 LocalNitro1.397s (-25.1% 🟢)2.007s (-14.3% 🟢)0.610s151.21x
🌐 MongoDBNext.js (Turbopack)2.013s2.590s0.577s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.363s (-8.5% 🟢)4.099s (-5.8% 🟢)1.736s81.00x
▲ VercelNitro3.169s (+28.9% 🔺)5.005s (+20.0% 🔺)1.836s71.34x
▲ VercelNext.js (Turbopack)3.909s (+33.3% 🔺)6.008s (+29.4% 🔺)2.100s51.65x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.231s (-47.4% 🟢)2.009s (-33.3% 🟢)0.778s151.00x
🐘 PostgresExpress1.232s (-47.4% 🟢)2.007s (-33.3% 🟢)0.776s151.00x
🐘 PostgresNext.js (Turbopack)1.354s2.008s0.654s151.10x
💻 LocalNitro2.049s (-33.1% 🟢)2.509s (-35.4% 🟢)0.460s121.67x
💻 LocalNext.js (Turbopack)2.074s2.735s0.661s111.69x
💻 LocalExpress2.135s (-31.8% 🟢)2.593s (-31.1% 🟢)0.457s121.74x
🌐 RedisNext.js (Turbopack)2.370s3.008s0.639s101.93x
🌐 MongoDBNext.js (Turbopack)3.577s4.009s0.432s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.907s (+20.8% 🔺)5.774s (+13.7% 🔺)1.867s61.00x
▲ VercelExpress4.205s (+31.7% 🔺)6.052s (+26.3% 🔺)1.847s51.08x
▲ VercelNext.js (Turbopack)4.751s (+51.2% 🔺)6.510s (+44.0% 🔺)1.759s51.22x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.358s (-61.2% 🟢)2.008s (-49.9% 🟢)0.650s151.00x
🐘 PostgresNitro1.366s (-60.7% 🟢)2.008s (-49.9% 🟢)0.642s151.01x
🐘 PostgresNext.js (Turbopack)1.651s2.009s0.358s151.22x
🌐 RedisNext.js (Turbopack)3.648s4.010s0.362s82.69x
💻 LocalExpress5.565s (-36.8% 🟢)6.015s (-35.1% 🟢)0.450s54.10x
💻 LocalNext.js (Turbopack)5.704s6.214s0.510s54.20x
💻 LocalNitro5.946s (-35.0% 🟢)6.348s (-36.7% 🟢)0.402s64.38x
🌐 MongoDBNext.js (Turbopack)6.250s7.013s0.763s54.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.891s (+15.7% 🔺)7.641s (+12.1% 🔺)1.750s41.00x
▲ VercelExpress6.208s (-3.2%)7.682s (-6.1% 🟢)1.474s41.05x
▲ VercelNext.js (Turbopack)6.830s (+1.1%)8.929s (+4.5%)2.099s41.16x

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

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.451s (-46.2% 🟢)1.006s (-1.7%)0.555s601.00x
🐘 PostgresNitro0.453s (-44.7% 🟢)1.006s (~)0.553s601.01x
💻 LocalNitro0.471s (-52.0% 🟢)1.004s (-8.2% 🟢)0.533s601.04x
💻 LocalExpress0.506s (-48.6% 🟢)1.022s (-5.1% 🟢)0.515s591.12x
🌐 MongoDBNext.js (Turbopack)0.597s1.006s0.409s601.32x
🌐 RedisNext.js (Turbopack)0.639s1.004s0.365s601.42x
🐘 PostgresNext.js (Turbopack)0.662s1.006s0.344s601.47x
💻 LocalNext.js (Turbopack)0.711s1.004s0.293s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.747s (-78.5% 🟢)6.670s (-72.2% 🟢)1.923s101.00x
▲ VercelExpress4.916s (-74.1% 🟢)6.991s (-67.2% 🟢)2.075s91.04x
▲ VercelNext.js (Turbopack)6.804s (-53.1% 🟢)8.802s (-45.3% 🟢)1.998s71.43x

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

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.032s (-47.8% 🟢)1.458s (-35.4% 🟢)0.426s621.00x
🐘 PostgresNitro1.047s (-45.7% 🟢)1.507s (-28.3% 🟢)0.459s601.01x
💻 LocalExpress1.184s (-60.7% 🟢)2.006s (-44.1% 🟢)0.821s451.15x
💻 LocalNitro1.191s (-60.8% 🟢)2.006s (-46.6% 🟢)0.815s451.15x
🌐 MongoDBNext.js (Turbopack)1.503s2.007s0.504s451.46x
🐘 PostgresNext.js (Turbopack)1.587s2.007s0.419s451.54x
🌐 RedisNext.js (Turbopack)1.611s2.028s0.417s451.56x
💻 LocalNext.js (Turbopack)1.741s2.005s0.265s451.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.285s (-64.4% 🟢)14.394s (-60.9% 🟢)2.108s71.00x
▲ VercelNitro12.810s (-67.6% 🟢)15.636s (-62.1% 🟢)2.825s61.04x
▲ VercelNext.js (Turbopack)15.611s (-68.7% 🟢)18.106s (-65.0% 🟢)2.494s61.27x

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

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.878s (-52.9% 🟢)2.076s (-52.5% 🟢)0.198s581.00x
🐘 PostgresNitro2.079s (-49.3% 🟢)2.550s (-44.6% 🟢)0.470s481.11x
💻 LocalNitro2.680s (-71.2% 🟢)3.008s (-70.0% 🟢)0.327s401.43x
💻 LocalExpress2.692s (-70.8% 🟢)3.008s (-70.0% 🟢)0.316s401.43x
🐘 PostgresNext.js (Turbopack)3.092s4.009s0.917s301.65x
🌐 RedisNext.js (Turbopack)3.096s3.880s0.783s311.65x
🌐 MongoDBNext.js (Turbopack)3.670s4.013s0.343s301.95x
💻 LocalNext.js (Turbopack)3.704s4.008s0.303s301.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro39.197s (-59.6% 🟢)42.019s (-57.3% 🟢)2.822s31.00x
▲ VercelExpress40.332s (-69.0% 🟢)42.693s (-67.7% 🟢)2.361s31.03x
▲ VercelNext.js (Turbopack)44.774s (-58.2% 🟢)48.551s (-55.4% 🟢)3.777s31.14x

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

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.176s (-37.5% 🟢)1.006s (~)0.829s601.00x
🐘 PostgresNitro0.182s (-35.8% 🟢)1.005s (~)0.824s601.03x
🐘 PostgresNext.js (Turbopack)0.232s1.006s0.774s601.32x
🌐 RedisNext.js (Turbopack)0.242s1.004s0.762s601.37x
💻 LocalExpress0.439s (-21.6% 🟢)1.004s (~)0.565s602.49x
💻 LocalNitro0.474s (-21.6% 🟢)1.021s (~)0.547s592.69x
💻 LocalNext.js (Turbopack)0.539s1.004s0.465s603.06x
🌐 MongoDBNext.js (Turbopack)1.032s1.825s0.793s335.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.231s (+34.3% 🔺)4.189s (+25.0% 🔺)1.958s151.00x
▲ VercelExpress2.259s (+15.6% 🔺)4.431s (+21.8% 🔺)2.172s141.01x
▲ VercelNext.js (Turbopack)4.335s (+114.4% 🔺)6.320s (+66.6% 🔺)1.984s101.94x

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

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.309s (-39.4% 🟢)1.006s (~)0.697s901.00x
🐘 PostgresNitro0.312s (-37.2% 🟢)1.006s (~)0.694s901.01x
🌐 RedisNext.js (Turbopack)0.412s1.004s0.592s901.33x
🐘 PostgresNext.js (Turbopack)0.450s1.006s0.555s901.46x
💻 LocalNitro2.179s (-14.2% 🟢)3.008s (~)0.829s307.05x
💻 LocalExpress2.268s (-9.8% 🟢)2.911s (-3.3%)0.644s317.34x
💻 LocalNext.js (Turbopack)2.382s2.977s0.595s317.71x
🌐 MongoDBNext.js (Turbopack)2.594s3.008s0.414s308.39x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express7.284s (+139.1% 🔺)9.227s (+91.9% 🔺)1.943s101.00x
▲ VercelNitro7.305s (+126.5% 🔺)9.647s (+100.1% 🔺)2.342s101.00x
▲ VercelNext.js (Turbopack)8.118s (+129.6% 🔺)10.545s (+103.1% 🔺)2.427s91.11x

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

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.618s (-24.5% 🟢)1.006s (-1.1%)0.388s1201.00x
🐘 PostgresNitro0.649s (-17.9% 🟢)1.006s (~)0.357s1201.05x
🌐 RedisNext.js (Turbopack)0.770s1.004s0.233s1201.25x
🐘 PostgresNext.js (Turbopack)0.933s1.219s0.286s991.51x
🌐 MongoDBNext.js (Turbopack)5.361s6.012s0.651s208.67x
💻 LocalExpress10.227s (-8.6% 🟢)10.863s (-9.0% 🟢)0.636s1216.54x
💻 LocalNitro10.251s (-8.4% 🟢)10.781s (-7.6% 🟢)0.530s1216.58x
💻 LocalNext.js (Turbopack)10.950s11.757s0.807s1117.71x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express20.578s (+177.3% 🔺)23.078s (+149.6% 🔺)2.500s61.00x
▲ VercelNext.js (Turbopack)21.742s (+110.5% 🔺)24.590s (+100.1% 🔺)2.848s51.06x
▲ VercelNitro25.208s (+226.4% 🔺)27.039s (+187.6% 🔺)1.831s51.22x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.129s (+450.3% 🔺)1.997s (+99.9% 🔺)0.001s (-18.8% 🟢)2.009s (+98.7% 🔺)0.881s101.00x
💻 LocalExpress1.131s (+468.1% 🔺)2.005s (+99.6% 🔺)0.012s (+2.5%)2.019s (+98.4% 🔺)0.888s101.00x
💻 LocalNitro1.132s (+429.6% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.021s (+98.3% 🔺)0.889s101.00x
🐘 PostgresNitro1.149s (+460.7% 🔺)1.994s (+99.5% 🔺)0.002s (+6.7% 🔺)2.012s (+99.0% 🔺)0.863s101.02x
💻 LocalNext.js (Turbopack)1.178s2.003s0.012s2.019s0.840s101.04x
🐘 PostgresNext.js (Turbopack)1.192s2.002s0.001s2.010s0.818s101.06x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.184s (-43.0% 🟢)3.612s (-31.5% 🟢)1.614s (+117.5% 🔺)5.719s (-11.8% 🟢)3.535s101.00x
▲ VercelExpress2.198s (-12.3% 🟢)3.538s (-13.5% 🟢)1.751s (+82.3% 🔺)5.836s (+4.4%)3.637s101.01x
▲ VercelNext.js (Turbopack)4.942s (-27.9% 🟢)5.438s (-37.1% 🟢)1.145s (+81.2% 🔺)8.364s (-14.6% 🟢)3.421s102.26x

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

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.496s (+137.4% 🔺)2.000s (+98.7% 🔺)0.003s (-9.5% 🟢)2.026s (+98.1% 🔺)0.531s301.00x
🐘 PostgresNitro1.507s (+141.5% 🔺)2.002s (+98.8% 🔺)0.004s (-8.9% 🟢)2.024s (+97.9% 🔺)0.517s301.01x
💻 LocalNitro1.538s (+83.4% 🔺)2.012s (+98.8% 🔺)0.010s (+11.7% 🔺)2.025s (+81.4% 🔺)0.486s301.03x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.004s2.025s0.367s301.11x
💻 LocalExpress1.716s (+126.6% 🔺)2.012s (+95.5% 🔺)0.009s (-2.1%)2.202s (+111.8% 🔺)0.486s281.15x
💻 LocalNext.js (Turbopack)2.055s2.009s0.010s2.423s0.368s251.37x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.789s (-80.3% 🟢)7.537s (-75.5% 🟢)0.268s (+139.0% 🔺)8.404s (-73.6% 🟢)2.615s81.00x
▲ VercelExpress6.045s (-7.1% 🟢)7.653s (-4.5%)0.313s (-23.5% 🟢)8.503s (-3.8%)2.457s81.04x
▲ VercelNext.js (Turbopack)12.326s (-27.1% 🟢)13.810s (-24.3% 🟢)0.429s (+103.2% 🔺)15.364s (-18.9% 🟢)3.038s42.13x

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

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.660s (-31.8% 🟢)1.031s (-17.4% 🟢)0.000s (+24.1% 🔺)1.048s (-16.7% 🟢)0.387s581.00x
🐘 PostgresExpress0.688s (-28.4% 🟢)1.061s (-17.0% 🟢)0.000s (+105.4% 🔺)1.079s (-17.4% 🟢)0.391s561.04x
🐘 PostgresNext.js (Turbopack)0.783s1.090s0.000s1.097s0.314s561.19x
💻 LocalNitro1.345s (+10.0% 🔺)2.016s (~)0.000s (+333.3% 🔺)2.018s (~)0.674s302.04x
💻 LocalNext.js (Turbopack)1.400s2.014s0.000s2.017s0.617s302.12x
💻 LocalExpress1.515s (+23.7% 🔺)2.015s (~)0.000s (-35.7% 🟢)2.196s (+8.6% 🔺)0.681s282.29x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.737s (+22.5% 🔺)5.067s (+15.4% 🔺)0.000s (+18.2% 🔺)5.709s (+18.7% 🔺)1.972s111.00x
▲ VercelExpress4.218s (+12.8% 🔺)5.770s (+13.1% 🔺)0.000s (+50.0% 🔺)6.218s (+12.4% 🔺)2.000s111.13x
▲ VercelNext.js (Turbopack)6.160s (-39.5% 🟢)6.979s (-39.4% 🟢)0.000s (NaN%)8.458s (-29.8% 🟢)2.298s81.65x

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

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.286s (-28.2% 🟢)2.001s (-6.6% 🟢)0.000s (-100.0% 🟢)2.020s (-7.1% 🟢)0.734s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.067s (-5.1% 🟢)0.000s (+Infinity% 🔺)2.115s (-3.8%)0.730s291.08x
🐘 PostgresNext.js (Turbopack)1.629s2.225s0.000s2.239s0.610s271.27x
💻 LocalNext.js (Turbopack)2.539s3.125s0.000s3.128s0.589s201.97x
💻 LocalExpress3.054s (-11.9% 🟢)3.903s (-3.2%)0.000s (-45.3% 🟢)3.905s (-3.2%)0.852s162.37x
💻 LocalNitro3.180s (-6.1% 🟢)3.966s (-1.6%)0.000s (-41.4% 🟢)3.971s (-1.6%)0.791s162.47x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.004s (+30.9% 🔺)7.517s (+24.8% 🔺)0.000s (+Infinity% 🔺)8.103s (+25.5% 🔺)2.099s81.00x
▲ VercelNitro6.750s (+64.9% 🔺)8.558s (+59.2% 🔺)0.000s (-100.0% 🟢)9.120s (+57.4% 🔺)2.370s71.12x
▲ VercelNext.js (Turbopack)8.561s (+52.4% 🔺)9.916s (+42.0% 🔺)0.000s (-100.0% 🟢)11.240s (+49.1% 🔺)2.679s61.43x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro11/21
🐘 PostgresExpress17/21
▲ VercelNitro11/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres19/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres17/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

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production85902191078
✅ 💻 Local Development95702191176
✅ 📦 Local Production95702191176
✅ 🐘 Local Postgres95702191176
✅ 🪟 Windows980098
✅ 📋 Other5100176686
Total4338010525390

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro72026
✅ example72026
✅ express72026
✅ fastify72026
✅ hono72026
✅ nextjs-turbopack9602
✅ nextjs-webpack9602
✅ nitro72026
✅ nuxt72026
✅ sveltekit9107
✅ vite72026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9800
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable73025
✅ e2e-local-dev-tanstack-start-stable73025
✅ e2e-local-postgres-nest-stable73025
✅ e2e-local-postgres-tanstack-start-stable73025
✅ e2e-local-prod-nest-stable73025
✅ e2e-local-prod-tanstack-start-stable73025
✅ e2e-vercel-prod-tanstack-start72026

📋 View full workflow run

Move the inline ANSI shim out of `ansi.ts` into its own
`./internal-chalk.ts` so tests can swap it for a tag-emitting mock via
`vi.mock('./internal-chalk.js', …)`. Snapshots for `Ansi.code` /
`Ansi.hint` / `Ansi.note` / `Ansi.help` / `Ansi.docs` go back to the
readable HTML-like form (`<blue><b>hint:</b> try reloading</blue>`),
making it obvious from the test file which fragments are colored and
how — the same review affordance the previous `__mocks__/chalk.ts`
mock provided before the chalk dep was dropped.
`Ansi.frame` and `Ansi.inline` remain plain in snapshots because they
don't apply any styling themselves; that matches the pre-fix behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. The diagnosis is correct, the fix is appropriately surgical, and the shim is well-written.

Verified

Reachability claim — confirmed: packages/core/src/workflow/index.tscontext-errors.tscontext-violation-error.tsimport * as Ansi from '@workflow/errors/ansi'. So any consumer that uses workflow-context-checked APIs (createHook, createWebhook, getWorkflowMetadata, etc.) ends up dragging the entire ansi/chalk transitive graph into the workflow VM bundle.

Output parity — for the only nested call in ansi.ts (code(): chalk.italic(\${chalk.dim('`')}${str}${chalk.dim('`')}`)), the shim produces byte-for-byte the same SGR sequence as chalk@5.6.2`:

\x1b[3m\x1b[2m`\x1b[22mfoo\x1b[2m`\x1b[22m\x1b[23m

The 22 (normal intensity) close after each dim correctly leaves italic (3) on, since 22 only resets bold/dim — same as chalk's behavior.

Bundle effect — built workbench/example from this branch and grepped the workflow VM bundle (the string passed to workflowEntrypoint):

$ grep -cE "supports-color|require\(['\"](node:)?os['\"]|chalk|internal_chalk" /tmp/vm-bundle.txt
0

No chalk, no supports-color, no os anywhere. (The example workbench's VM bundle didn't actually pull in context-violation-error due to tree-shaking in this particular workflow set, so the chalk references were already absent on main for this specific bundle — but the change is still correct, and downstream consumers like vade that exercise the workflow-context paths will get the fix.)

Testspnpm --filter @workflow/errors test passes (4 files, 26 tests including all 11 ansi.test.ts snapshots), pnpm --filter @workflow/core test passes (820 tests).

CI failures are pre-existing flakes

The two red checks (Unit Tests (ubuntu-latest), E2E Required Check) are both from the same flaky world-postgres test:

FAIL test/spec.test.ts > sequential steps with stream complete in a single flow invocation
AssertionError: expected +0 to be 1 // Object.is equality

This same test fails on origin/main HEAD (run 25343680860 job 74307194029) and is unrelated to this change.

Minor nits (non-blocking)

  • colorEnabled is evaluated once at module-load time, whereas chalk re-evaluates lazily on each call. If a host process programmatically flips FORCE_COLOR/NO_COLOR after @workflow/errors is imported, the shim won't pick up the change. In practice no one in this codebase does that, and the JSDoc on internal-chalk.ts is implicit about it — but if you wanted to be belt-and-braces, wrapping the env-var check inside sgr() would keep parity. Totally fine to leave as-is.

  • The __mocks__/chalk.ts deletion is correct, but worth noting that the module-graph entrypoint for the test mock changed from "the chalk package as resolved by node" to "this sibling file at a literal relative path". That's fine for this codebase but means the mock now relies on vi.mock('./internal-chalk.js', ...)paths rather than package identity — slightly more fragile if ansi.ts is ever moved to a different directory. Fine for now.

Approving.

@VaguelySerious
VaguelySerious merged commit 540a2ef into mainMay 5, 2026
175 of 177 checks passed
@VaguelySerious
VaguelySerious deleted the peter/errors-no-chalk branch May 5, 2026 00:47
@github-actionsgithub-actionsBot mentioned this pull request May 5, 2026
pranaygp added a commit that referenced this pull request May 5, 2026
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
pranaygp added a commit that referenced this pull request May 5, 2026
…ignal
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
Allow disabling step sourcemap with new `sourcemap` option in builders (#1842)
[ci] Enable Vercel-prod e2e for tanstack-start (#1904)
web: configure vercelPreset() for Vercel deployments (#1815)
[core] Combine flow+step bundle and process steps eagerly (#1338)
[world-vercel] Revert stream close control framing (#1891)
[tarballs] Use turbo to build workspace deps before packing (#1908)
# Conflicts:
#	packages/core/src/runtime/step-handler.test.ts
#	packages/core/src/runtime/step-handler.ts
#	packages/core/src/runtime/suspension-handler.ts
#	packages/core/src/step.test.ts
#	packages/world-local/src/storage/events-storage.ts
#	packages/world-postgres/src/drizzle/migrations/meta/_journal.json
Sign up for freeto 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.

2 participants

@VaguelySerious@TooTallNate
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[errors] Replace chalk import with inline ANSI shim - #1915

Merged
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk
May 5, 2026
Merged

[errors] Replace chalk import with inline ANSI shim#1915
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

@workflow/errors/ansi imports chalk, which transitively require('os')s via supports-color. That module is reachable from the workflow-VM bundle through:

workflow → @workflow/core/workflow/index.ts
→ context-errors.ts
→ context-violation-error.ts
→ @workflow/errors/ansi ← imports chalk

The workflow VM has no require(), so importing chalk crashes every workflow at registration time:

./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)

Fix

Replace chalk with a 25-line inline SGR helper that exposes the same call surface (chalk.red, chalk.bold, …). Color detection mirrors chalk's defaults at a coarse level — FORCE_COLOR on, NO_COLOR off, otherwise gated on process.stdout.isTTY — so non-TTY logs and the workflow VM (no process) get plain text, matching the prior behavior in tests and in production log drains.

Drops the chalk runtime dependency from @workflow/errors and removes the now-unused __mocks__/chalk.ts Vitest mock. All 820 @workflow/core unit tests + 11 @workflow/errors snapshot tests pass.

Test plan

  • pnpm --filter @workflow/errors test (26 tests pass)
  • pnpm --filter @workflow/core test (820 unit tests pass; e2e excluded)
  • CI green
  • Reproduce: install latest tarballs in a v2-flow consumer (vade) and confirm step registration no longer throws ReferenceError: require is not defined

🤖 Generated with Claude Code

`@workflow/errors/ansi` is reachable from the workflow-VM bundle via
`@workflow/core/workflow` → `context-errors` → `context-violation-error`
→ here. The real `chalk` package pulls in `supports-color`, which calls
`require('os')` at module load — crashing every workflow with
`ReferenceError: require is not defined` in the sandboxed VM the moment
any user step is registered:
./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)
Replace `chalk` with a 25-line inline SGR helper exposing the same call
surface (`chalk.red`, `chalk.bold`, …). Color detection mirrors chalk's
default at a coarse level — `FORCE_COLOR` on, `NO_COLOR` off, otherwise
gated on `process.stdout.isTTY` — so non-TTY logs and the workflow VM
(no `process`) get plain text, matching previous behavior in tests and
in production log drains.
Drops the `chalk` runtime dependency from `@workflow/errors` and removes
the now-unused `__mocks__/chalk.ts` Vitest mock.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 47be549

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

This PR includes changesets to release 22 packages
NameType
@workflow/errorsPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/webPatch
workflowPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-vercelPatch
tarballsPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/world-testingPatch
@workflow/web-sharedPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

@github-actions

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.033s (-24.6% 🟢)1.005s (~)0.973s101.00x
💻 LocalExpress0.035s (-20.1% 🟢)1.006s (~)0.970s101.09x
🐘 PostgresExpress0.046s (-19.8% 🟢)1.011s (~)0.964s101.43x
💻 LocalNext.js (Turbopack)0.049s1.005s0.956s101.50x
🐘 PostgresNitro0.052s (-45.5% 🟢)1.011s (-3.0%)0.959s101.60x
🌐 RedisNext.js (Turbopack)0.054s1.005s0.951s101.65x
🐘 PostgresNext.js (Turbopack)0.057s1.011s0.953s101.76x
🌐 MongoDBNext.js (Turbopack)0.091s1.007s0.916s102.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.295s (+25.2% 🔺)2.353s (+10.2% 🔺)2.058s101.00x
▲ VercelNitro0.366s (-10.6% 🟢)2.033s (-19.0% 🟢)1.667s101.24x
▲ VercelNext.js (Turbopack)0.558s (+122.1% 🔺)2.742s (+17.5% 🔺)2.183s101.90x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.070s (-5.4% 🟢)2.007s (~)0.937s101.00x
💻 LocalExpress1.072s (-4.7%)2.007s (~)0.935s101.00x
🐘 PostgresExpress1.082s (-5.6% 🟢)2.009s (~)0.927s101.01x
🐘 PostgresNitro1.084s (-4.9%)2.009s (~)0.925s101.01x
💻 LocalNext.js (Turbopack)1.110s2.006s0.896s101.04x
🌐 RedisNext.js (Turbopack)1.111s2.008s0.897s101.04x
🐘 PostgresNext.js (Turbopack)1.114s2.010s0.897s101.04x
🌐 MongoDBNext.js (Turbopack)1.148s2.009s0.861s101.07x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.586s (-15.4% 🟢)3.902s (+2.5%)2.317s101.00x
▲ VercelNitro1.641s (-57.8% 🟢)4.080s (-30.9% 🟢)2.439s101.04x
▲ VercelNext.js (Turbopack)2.861s (+40.6% 🔺)4.792s (+25.1% 🔺)1.931s101.80x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express10.400s (-5.1% 🟢)11.014s (~)0.614s31.00x
🐘 PostgresNitro10.402s (-4.3%)11.015s (~)0.613s31.00x
💻 LocalExpress10.409s (-4.7%)11.022s (~)0.613s31.00x
💻 LocalNitro10.422s (-4.8%)11.023s (~)0.601s31.00x
🌐 MongoDBNext.js (Turbopack)10.655s11.023s0.368s31.02x
🐘 PostgresNext.js (Turbopack)10.667s11.015s0.348s31.03x
🌐 RedisNext.js (Turbopack)10.669s11.023s0.354s31.03x
💻 LocalNext.js (Turbopack)10.682s11.020s0.338s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.559s (-42.9% 🟢)15.361s (-38.8% 🟢)1.802s21.00x
▲ VercelExpress13.917s (-18.0% 🟢)15.853s (-20.8% 🟢)1.936s21.03x
▲ VercelNext.js (Turbopack)14.322s (-17.3% 🟢)16.622s (-14.3% 🟢)2.299s21.06x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.471s (-7.6% 🟢)14.017s (-6.7% 🟢)0.546s51.00x
💻 LocalExpress13.477s (-10.0% 🟢)14.027s (-6.7% 🟢)0.550s51.00x
🐘 PostgresNitro13.481s (-7.6% 🟢)14.021s (-6.7% 🟢)0.540s51.00x
💻 LocalNitro13.483s (-10.5% 🟢)14.027s (-12.5% 🟢)0.544s51.00x
🌐 MongoDBNext.js (Turbopack)13.835s14.025s0.190s51.03x
🌐 RedisNext.js (Turbopack)14.050s15.030s0.980s41.04x
💻 LocalNext.js (Turbopack)14.060s14.628s0.569s51.04x
🐘 PostgresNext.js (Turbopack)14.069s15.020s0.951s41.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.544s (-66.6% 🟢)23.739s (-64.4% 🟢)2.195s31.00x
▲ VercelExpress22.795s (-54.7% 🟢)25.107s (-52.2% 🟢)2.312s31.06x
▲ VercelNext.js (Turbopack)23.551s (-55.2% 🟢)26.276s (-51.9% 🟢)2.725s31.09x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express11.836s (-15.5% 🟢)12.015s (-17.7% 🟢)0.179s81.00x
💻 LocalNitro11.946s (-28.8% 🟢)12.272s (-27.9% 🟢)0.326s81.01x
💻 LocalExpress11.949s (-28.0% 🟢)12.146s (-28.7% 🟢)0.197s81.01x
🐘 PostgresNitro11.970s (-14.3% 🟢)12.394s (-13.4% 🟢)0.424s81.01x
🌐 MongoDBNext.js (Turbopack)12.590s13.024s0.433s71.06x
💻 LocalNext.js (Turbopack)12.972s13.452s0.480s71.10x
🌐 RedisNext.js (Turbopack)13.100s13.884s0.784s71.11x
🐘 PostgresNext.js (Turbopack)13.185s14.020s0.835s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express34.548s (-71.5% 🟢)36.366s (-70.6% 🟢)1.818s31.00x
▲ VercelNitro35.156s (-91.7% 🟢)37.155s (-91.2% 🟢)1.999s31.02x
▲ VercelNext.js (Turbopack)35.694s (-90.9% 🟢)37.608s (-90.5% 🟢)1.913s31.03x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.147s (-9.0% 🟢)2.007s (~)0.860s151.00x
🐘 PostgresNitro1.156s (-9.3% 🟢)2.007s (~)0.851s151.01x
💻 LocalNitro1.188s (-27.2% 🟢)2.006s (-3.3%)0.818s151.04x
💻 LocalExpress1.197s (-19.6% 🟢)2.006s (~)0.808s151.04x
🐘 PostgresNext.js (Turbopack)1.213s2.008s0.794s151.06x
🌐 RedisNext.js (Turbopack)1.268s2.006s0.738s151.10x
💻 LocalNext.js (Turbopack)1.378s2.073s0.695s151.20x
🌐 MongoDBNext.js (Turbopack)2.011s2.591s0.580s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.918s (+3.6%)4.656s (+7.7% 🔺)1.738s71.00x
▲ VercelNext.js (Turbopack)4.329s (+27.4% 🔺)6.218s (+26.1% 🔺)1.889s51.48x
▲ VercelExpress4.519s (+58.0% 🔺)6.298s (+36.2% 🔺)1.779s51.55x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.226s (-48.1% 🟢)2.007s (-33.3% 🟢)0.780s151.00x
🐘 PostgresNitro1.234s (-47.5% 🟢)2.007s (-33.3% 🟢)0.773s151.01x
🐘 PostgresNext.js (Turbopack)1.356s2.007s0.651s151.11x
💻 LocalNitro1.791s (-43.0% 🟢)2.007s (-48.4% 🟢)0.216s151.46x
💻 LocalExpress1.792s (-39.3% 🟢)2.006s (-41.9% 🟢)0.214s151.46x
💻 LocalNext.js (Turbopack)1.806s2.073s0.267s151.47x
🌐 RedisNext.js (Turbopack)2.385s3.008s0.623s101.94x
🌐 MongoDBNext.js (Turbopack)3.564s4.008s0.444s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.458s (+23.2% 🔺)6.268s (+22.7% 🔺)1.811s51.00x
▲ VercelNext.js (Turbopack)5.613s (-20.9% 🟢)7.797s (-12.4% 🟢)2.184s41.26x
▲ VercelNitro6.080s (+50.1% 🔺)8.092s (+36.7% 🔺)2.012s41.36x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.364s (-60.9% 🟢)2.006s (-50.0% 🟢)0.642s151.00x
🐘 PostgresNitro1.374s (-60.5% 🟢)2.007s (-49.9% 🟢)0.633s151.01x
🐘 PostgresNext.js (Turbopack)1.627s2.007s0.380s151.19x
🌐 RedisNext.js (Turbopack)3.621s4.009s0.389s82.65x
💻 LocalNext.js (Turbopack)5.000s5.511s0.511s63.67x
💻 LocalNitro5.331s (-36.2% 🟢)5.847s (-35.2% 🟢)0.517s63.91x
💻 LocalExpress5.417s (-35.0% 🟢)5.679s (-37.1% 🟢)0.263s63.97x
🌐 MongoDBNext.js (Turbopack)6.223s7.012s0.788s54.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.257s (+47.6% 🔺)8.386s (+36.9% 🔺)2.129s41.00x
▲ VercelNext.js (Turbopack)7.288s (-18.3% 🟢)9.279s (-15.3% 🟢)1.991s41.16x
▲ VercelNitro8.700s (+146.8% 🔺)10.899s (+96.9% 🔺)2.199s31.39x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.153s (-8.3% 🟢)2.009s (~)0.857s151.00x
🐘 PostgresExpress1.154s (-8.2% 🟢)2.009s (~)0.855s151.00x
🐘 PostgresNext.js (Turbopack)1.210s2.007s0.797s151.05x
🌐 RedisNext.js (Turbopack)1.257s2.006s0.749s151.09x
💻 LocalNext.js (Turbopack)1.353s2.006s0.653s151.17x
💻 LocalExpress1.384s (-26.9% 🟢)2.006s (-15.1% 🟢)0.622s151.20x
💻 LocalNitro1.397s (-25.1% 🟢)2.007s (-14.3% 🟢)0.610s151.21x
🌐 MongoDBNext.js (Turbopack)2.013s2.590s0.577s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.363s (-8.5% 🟢)4.099s (-5.8% 🟢)1.736s81.00x
▲ VercelNitro3.169s (+28.9% 🔺)5.005s (+20.0% 🔺)1.836s71.34x
▲ VercelNext.js (Turbopack)3.909s (+33.3% 🔺)6.008s (+29.4% 🔺)2.100s51.65x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.231s (-47.4% 🟢)2.009s (-33.3% 🟢)0.778s151.00x
🐘 PostgresExpress1.232s (-47.4% 🟢)2.007s (-33.3% 🟢)0.776s151.00x
🐘 PostgresNext.js (Turbopack)1.354s2.008s0.654s151.10x
💻 LocalNitro2.049s (-33.1% 🟢)2.509s (-35.4% 🟢)0.460s121.67x
💻 LocalNext.js (Turbopack)2.074s2.735s0.661s111.69x
💻 LocalExpress2.135s (-31.8% 🟢)2.593s (-31.1% 🟢)0.457s121.74x
🌐 RedisNext.js (Turbopack)2.370s3.008s0.639s101.93x
🌐 MongoDBNext.js (Turbopack)3.577s4.009s0.432s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.907s (+20.8% 🔺)5.774s (+13.7% 🔺)1.867s61.00x
▲ VercelExpress4.205s (+31.7% 🔺)6.052s (+26.3% 🔺)1.847s51.08x
▲ VercelNext.js (Turbopack)4.751s (+51.2% 🔺)6.510s (+44.0% 🔺)1.759s51.22x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.358s (-61.2% 🟢)2.008s (-49.9% 🟢)0.650s151.00x
🐘 PostgresNitro1.366s (-60.7% 🟢)2.008s (-49.9% 🟢)0.642s151.01x
🐘 PostgresNext.js (Turbopack)1.651s2.009s0.358s151.22x
🌐 RedisNext.js (Turbopack)3.648s4.010s0.362s82.69x
💻 LocalExpress5.565s (-36.8% 🟢)6.015s (-35.1% 🟢)0.450s54.10x
💻 LocalNext.js (Turbopack)5.704s6.214s0.510s54.20x
💻 LocalNitro5.946s (-35.0% 🟢)6.348s (-36.7% 🟢)0.402s64.38x
🌐 MongoDBNext.js (Turbopack)6.250s7.013s0.763s54.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.891s (+15.7% 🔺)7.641s (+12.1% 🔺)1.750s41.00x
▲ VercelExpress6.208s (-3.2%)7.682s (-6.1% 🟢)1.474s41.05x
▲ VercelNext.js (Turbopack)6.830s (+1.1%)8.929s (+4.5%)2.099s41.16x

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

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.451s (-46.2% 🟢)1.006s (-1.7%)0.555s601.00x
🐘 PostgresNitro0.453s (-44.7% 🟢)1.006s (~)0.553s601.01x
💻 LocalNitro0.471s (-52.0% 🟢)1.004s (-8.2% 🟢)0.533s601.04x
💻 LocalExpress0.506s (-48.6% 🟢)1.022s (-5.1% 🟢)0.515s591.12x
🌐 MongoDBNext.js (Turbopack)0.597s1.006s0.409s601.32x
🌐 RedisNext.js (Turbopack)0.639s1.004s0.365s601.42x
🐘 PostgresNext.js (Turbopack)0.662s1.006s0.344s601.47x
💻 LocalNext.js (Turbopack)0.711s1.004s0.293s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.747s (-78.5% 🟢)6.670s (-72.2% 🟢)1.923s101.00x
▲ VercelExpress4.916s (-74.1% 🟢)6.991s (-67.2% 🟢)2.075s91.04x
▲ VercelNext.js (Turbopack)6.804s (-53.1% 🟢)8.802s (-45.3% 🟢)1.998s71.43x

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

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.032s (-47.8% 🟢)1.458s (-35.4% 🟢)0.426s621.00x
🐘 PostgresNitro1.047s (-45.7% 🟢)1.507s (-28.3% 🟢)0.459s601.01x
💻 LocalExpress1.184s (-60.7% 🟢)2.006s (-44.1% 🟢)0.821s451.15x
💻 LocalNitro1.191s (-60.8% 🟢)2.006s (-46.6% 🟢)0.815s451.15x
🌐 MongoDBNext.js (Turbopack)1.503s2.007s0.504s451.46x
🐘 PostgresNext.js (Turbopack)1.587s2.007s0.419s451.54x
🌐 RedisNext.js (Turbopack)1.611s2.028s0.417s451.56x
💻 LocalNext.js (Turbopack)1.741s2.005s0.265s451.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.285s (-64.4% 🟢)14.394s (-60.9% 🟢)2.108s71.00x
▲ VercelNitro12.810s (-67.6% 🟢)15.636s (-62.1% 🟢)2.825s61.04x
▲ VercelNext.js (Turbopack)15.611s (-68.7% 🟢)18.106s (-65.0% 🟢)2.494s61.27x

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

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.878s (-52.9% 🟢)2.076s (-52.5% 🟢)0.198s581.00x
🐘 PostgresNitro2.079s (-49.3% 🟢)2.550s (-44.6% 🟢)0.470s481.11x
💻 LocalNitro2.680s (-71.2% 🟢)3.008s (-70.0% 🟢)0.327s401.43x
💻 LocalExpress2.692s (-70.8% 🟢)3.008s (-70.0% 🟢)0.316s401.43x
🐘 PostgresNext.js (Turbopack)3.092s4.009s0.917s301.65x
🌐 RedisNext.js (Turbopack)3.096s3.880s0.783s311.65x
🌐 MongoDBNext.js (Turbopack)3.670s4.013s0.343s301.95x
💻 LocalNext.js (Turbopack)3.704s4.008s0.303s301.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro39.197s (-59.6% 🟢)42.019s (-57.3% 🟢)2.822s31.00x
▲ VercelExpress40.332s (-69.0% 🟢)42.693s (-67.7% 🟢)2.361s31.03x
▲ VercelNext.js (Turbopack)44.774s (-58.2% 🟢)48.551s (-55.4% 🟢)3.777s31.14x

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

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.176s (-37.5% 🟢)1.006s (~)0.829s601.00x
🐘 PostgresNitro0.182s (-35.8% 🟢)1.005s (~)0.824s601.03x
🐘 PostgresNext.js (Turbopack)0.232s1.006s0.774s601.32x
🌐 RedisNext.js (Turbopack)0.242s1.004s0.762s601.37x
💻 LocalExpress0.439s (-21.6% 🟢)1.004s (~)0.565s602.49x
💻 LocalNitro0.474s (-21.6% 🟢)1.021s (~)0.547s592.69x
💻 LocalNext.js (Turbopack)0.539s1.004s0.465s603.06x
🌐 MongoDBNext.js (Turbopack)1.032s1.825s0.793s335.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.231s (+34.3% 🔺)4.189s (+25.0% 🔺)1.958s151.00x
▲ VercelExpress2.259s (+15.6% 🔺)4.431s (+21.8% 🔺)2.172s141.01x
▲ VercelNext.js (Turbopack)4.335s (+114.4% 🔺)6.320s (+66.6% 🔺)1.984s101.94x

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

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.309s (-39.4% 🟢)1.006s (~)0.697s901.00x
🐘 PostgresNitro0.312s (-37.2% 🟢)1.006s (~)0.694s901.01x
🌐 RedisNext.js (Turbopack)0.412s1.004s0.592s901.33x
🐘 PostgresNext.js (Turbopack)0.450s1.006s0.555s901.46x
💻 LocalNitro2.179s (-14.2% 🟢)3.008s (~)0.829s307.05x
💻 LocalExpress2.268s (-9.8% 🟢)2.911s (-3.3%)0.644s317.34x
💻 LocalNext.js (Turbopack)2.382s2.977s0.595s317.71x
🌐 MongoDBNext.js (Turbopack)2.594s3.008s0.414s308.39x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express7.284s (+139.1% 🔺)9.227s (+91.9% 🔺)1.943s101.00x
▲ VercelNitro7.305s (+126.5% 🔺)9.647s (+100.1% 🔺)2.342s101.00x
▲ VercelNext.js (Turbopack)8.118s (+129.6% 🔺)10.545s (+103.1% 🔺)2.427s91.11x

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

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.618s (-24.5% 🟢)1.006s (-1.1%)0.388s1201.00x
🐘 PostgresNitro0.649s (-17.9% 🟢)1.006s (~)0.357s1201.05x
🌐 RedisNext.js (Turbopack)0.770s1.004s0.233s1201.25x
🐘 PostgresNext.js (Turbopack)0.933s1.219s0.286s991.51x
🌐 MongoDBNext.js (Turbopack)5.361s6.012s0.651s208.67x
💻 LocalExpress10.227s (-8.6% 🟢)10.863s (-9.0% 🟢)0.636s1216.54x
💻 LocalNitro10.251s (-8.4% 🟢)10.781s (-7.6% 🟢)0.530s1216.58x
💻 LocalNext.js (Turbopack)10.950s11.757s0.807s1117.71x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express20.578s (+177.3% 🔺)23.078s (+149.6% 🔺)2.500s61.00x
▲ VercelNext.js (Turbopack)21.742s (+110.5% 🔺)24.590s (+100.1% 🔺)2.848s51.06x
▲ VercelNitro25.208s (+226.4% 🔺)27.039s (+187.6% 🔺)1.831s51.22x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.129s (+450.3% 🔺)1.997s (+99.9% 🔺)0.001s (-18.8% 🟢)2.009s (+98.7% 🔺)0.881s101.00x
💻 LocalExpress1.131s (+468.1% 🔺)2.005s (+99.6% 🔺)0.012s (+2.5%)2.019s (+98.4% 🔺)0.888s101.00x
💻 LocalNitro1.132s (+429.6% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.021s (+98.3% 🔺)0.889s101.00x
🐘 PostgresNitro1.149s (+460.7% 🔺)1.994s (+99.5% 🔺)0.002s (+6.7% 🔺)2.012s (+99.0% 🔺)0.863s101.02x
💻 LocalNext.js (Turbopack)1.178s2.003s0.012s2.019s0.840s101.04x
🐘 PostgresNext.js (Turbopack)1.192s2.002s0.001s2.010s0.818s101.06x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.184s (-43.0% 🟢)3.612s (-31.5% 🟢)1.614s (+117.5% 🔺)5.719s (-11.8% 🟢)3.535s101.00x
▲ VercelExpress2.198s (-12.3% 🟢)3.538s (-13.5% 🟢)1.751s (+82.3% 🔺)5.836s (+4.4%)3.637s101.01x
▲ VercelNext.js (Turbopack)4.942s (-27.9% 🟢)5.438s (-37.1% 🟢)1.145s (+81.2% 🔺)8.364s (-14.6% 🟢)3.421s102.26x

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

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.496s (+137.4% 🔺)2.000s (+98.7% 🔺)0.003s (-9.5% 🟢)2.026s (+98.1% 🔺)0.531s301.00x
🐘 PostgresNitro1.507s (+141.5% 🔺)2.002s (+98.8% 🔺)0.004s (-8.9% 🟢)2.024s (+97.9% 🔺)0.517s301.01x
💻 LocalNitro1.538s (+83.4% 🔺)2.012s (+98.8% 🔺)0.010s (+11.7% 🔺)2.025s (+81.4% 🔺)0.486s301.03x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.004s2.025s0.367s301.11x
💻 LocalExpress1.716s (+126.6% 🔺)2.012s (+95.5% 🔺)0.009s (-2.1%)2.202s (+111.8% 🔺)0.486s281.15x
💻 LocalNext.js (Turbopack)2.055s2.009s0.010s2.423s0.368s251.37x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.789s (-80.3% 🟢)7.537s (-75.5% 🟢)0.268s (+139.0% 🔺)8.404s (-73.6% 🟢)2.615s81.00x
▲ VercelExpress6.045s (-7.1% 🟢)7.653s (-4.5%)0.313s (-23.5% 🟢)8.503s (-3.8%)2.457s81.04x
▲ VercelNext.js (Turbopack)12.326s (-27.1% 🟢)13.810s (-24.3% 🟢)0.429s (+103.2% 🔺)15.364s (-18.9% 🟢)3.038s42.13x

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

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.660s (-31.8% 🟢)1.031s (-17.4% 🟢)0.000s (+24.1% 🔺)1.048s (-16.7% 🟢)0.387s581.00x
🐘 PostgresExpress0.688s (-28.4% 🟢)1.061s (-17.0% 🟢)0.000s (+105.4% 🔺)1.079s (-17.4% 🟢)0.391s561.04x
🐘 PostgresNext.js (Turbopack)0.783s1.090s0.000s1.097s0.314s561.19x
💻 LocalNitro1.345s (+10.0% 🔺)2.016s (~)0.000s (+333.3% 🔺)2.018s (~)0.674s302.04x
💻 LocalNext.js (Turbopack)1.400s2.014s0.000s2.017s0.617s302.12x
💻 LocalExpress1.515s (+23.7% 🔺)2.015s (~)0.000s (-35.7% 🟢)2.196s (+8.6% 🔺)0.681s282.29x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.737s (+22.5% 🔺)5.067s (+15.4% 🔺)0.000s (+18.2% 🔺)5.709s (+18.7% 🔺)1.972s111.00x
▲ VercelExpress4.218s (+12.8% 🔺)5.770s (+13.1% 🔺)0.000s (+50.0% 🔺)6.218s (+12.4% 🔺)2.000s111.13x
▲ VercelNext.js (Turbopack)6.160s (-39.5% 🟢)6.979s (-39.4% 🟢)0.000s (NaN%)8.458s (-29.8% 🟢)2.298s81.65x

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

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.286s (-28.2% 🟢)2.001s (-6.6% 🟢)0.000s (-100.0% 🟢)2.020s (-7.1% 🟢)0.734s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.067s (-5.1% 🟢)0.000s (+Infinity% 🔺)2.115s (-3.8%)0.730s291.08x
🐘 PostgresNext.js (Turbopack)1.629s2.225s0.000s2.239s0.610s271.27x
💻 LocalNext.js (Turbopack)2.539s3.125s0.000s3.128s0.589s201.97x
💻 LocalExpress3.054s (-11.9% 🟢)3.903s (-3.2%)0.000s (-45.3% 🟢)3.905s (-3.2%)0.852s162.37x
💻 LocalNitro3.180s (-6.1% 🟢)3.966s (-1.6%)0.000s (-41.4% 🟢)3.971s (-1.6%)0.791s162.47x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.004s (+30.9% 🔺)7.517s (+24.8% 🔺)0.000s (+Infinity% 🔺)8.103s (+25.5% 🔺)2.099s81.00x
▲ VercelNitro6.750s (+64.9% 🔺)8.558s (+59.2% 🔺)0.000s (-100.0% 🟢)9.120s (+57.4% 🔺)2.370s71.12x
▲ VercelNext.js (Turbopack)8.561s (+52.4% 🔺)9.916s (+42.0% 🔺)0.000s (-100.0% 🟢)11.240s (+49.1% 🔺)2.679s61.43x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro11/21
🐘 PostgresExpress17/21
▲ VercelNitro11/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres19/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres17/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

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production85902191078
✅ 💻 Local Development95702191176
✅ 📦 Local Production95702191176
✅ 🐘 Local Postgres95702191176
✅ 🪟 Windows980098
✅ 📋 Other5100176686
Total4338010525390

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro72026
✅ example72026
✅ express72026
✅ fastify72026
✅ hono72026
✅ nextjs-turbopack9602
✅ nextjs-webpack9602
✅ nitro72026
✅ nuxt72026
✅ sveltekit9107
✅ vite72026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9800
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable73025
✅ e2e-local-dev-tanstack-start-stable73025
✅ e2e-local-postgres-nest-stable73025
✅ e2e-local-postgres-tanstack-start-stable73025
✅ e2e-local-prod-nest-stable73025
✅ e2e-local-prod-tanstack-start-stable73025
✅ e2e-vercel-prod-tanstack-start72026

📋 View full workflow run

Move the inline ANSI shim out of `ansi.ts` into its own
`./internal-chalk.ts` so tests can swap it for a tag-emitting mock via
`vi.mock('./internal-chalk.js', …)`. Snapshots for `Ansi.code` /
`Ansi.hint` / `Ansi.note` / `Ansi.help` / `Ansi.docs` go back to the
readable HTML-like form (`<blue><b>hint:</b> try reloading</blue>`),
making it obvious from the test file which fragments are colored and
how — the same review affordance the previous `__mocks__/chalk.ts`
mock provided before the chalk dep was dropped.
`Ansi.frame` and `Ansi.inline` remain plain in snapshots because they
don't apply any styling themselves; that matches the pre-fix behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. The diagnosis is correct, the fix is appropriately surgical, and the shim is well-written.

Verified

Reachability claim — confirmed: packages/core/src/workflow/index.tscontext-errors.tscontext-violation-error.tsimport * as Ansi from '@workflow/errors/ansi'. So any consumer that uses workflow-context-checked APIs (createHook, createWebhook, getWorkflowMetadata, etc.) ends up dragging the entire ansi/chalk transitive graph into the workflow VM bundle.

Output parity — for the only nested call in ansi.ts (code(): chalk.italic(\${chalk.dim('`')}${str}${chalk.dim('`')}`)), the shim produces byte-for-byte the same SGR sequence as chalk@5.6.2`:

\x1b[3m\x1b[2m`\x1b[22mfoo\x1b[2m`\x1b[22m\x1b[23m

The 22 (normal intensity) close after each dim correctly leaves italic (3) on, since 22 only resets bold/dim — same as chalk's behavior.

Bundle effect — built workbench/example from this branch and grepped the workflow VM bundle (the string passed to workflowEntrypoint):

$ grep -cE "supports-color|require\(['\"](node:)?os['\"]|chalk|internal_chalk" /tmp/vm-bundle.txt
0

No chalk, no supports-color, no os anywhere. (The example workbench's VM bundle didn't actually pull in context-violation-error due to tree-shaking in this particular workflow set, so the chalk references were already absent on main for this specific bundle — but the change is still correct, and downstream consumers like vade that exercise the workflow-context paths will get the fix.)

Testspnpm --filter @workflow/errors test passes (4 files, 26 tests including all 11 ansi.test.ts snapshots), pnpm --filter @workflow/core test passes (820 tests).

CI failures are pre-existing flakes

The two red checks (Unit Tests (ubuntu-latest), E2E Required Check) are both from the same flaky world-postgres test:

FAIL test/spec.test.ts > sequential steps with stream complete in a single flow invocation
AssertionError: expected +0 to be 1 // Object.is equality

This same test fails on origin/main HEAD (run 25343680860 job 74307194029) and is unrelated to this change.

Minor nits (non-blocking)

  • colorEnabled is evaluated once at module-load time, whereas chalk re-evaluates lazily on each call. If a host process programmatically flips FORCE_COLOR/NO_COLOR after @workflow/errors is imported, the shim won't pick up the change. In practice no one in this codebase does that, and the JSDoc on internal-chalk.ts is implicit about it — but if you wanted to be belt-and-braces, wrapping the env-var check inside sgr() would keep parity. Totally fine to leave as-is.

  • The __mocks__/chalk.ts deletion is correct, but worth noting that the module-graph entrypoint for the test mock changed from "the chalk package as resolved by node" to "this sibling file at a literal relative path". That's fine for this codebase but means the mock now relies on vi.mock('./internal-chalk.js', ...)paths rather than package identity — slightly more fragile if ansi.ts is ever moved to a different directory. Fine for now.

Approving.

@VaguelySerious
VaguelySerious merged commit 540a2ef into mainMay 5, 2026
175 of 177 checks passed
@VaguelySerious
VaguelySerious deleted the peter/errors-no-chalk branch May 5, 2026 00:47
@github-actionsgithub-actionsBot mentioned this pull request May 5, 2026
pranaygp added a commit that referenced this pull request May 5, 2026
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
pranaygp added a commit that referenced this pull request May 5, 2026
…ignal
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
Allow disabling step sourcemap with new `sourcemap` option in builders (#1842)
[ci] Enable Vercel-prod e2e for tanstack-start (#1904)
web: configure vercelPreset() for Vercel deployments (#1815)
[core] Combine flow+step bundle and process steps eagerly (#1338)
[world-vercel] Revert stream close control framing (#1891)
[tarballs] Use turbo to build workspace deps before packing (#1908)
# Conflicts:
#	packages/core/src/runtime/step-handler.test.ts
#	packages/core/src/runtime/step-handler.ts
#	packages/core/src/runtime/suspension-handler.ts
#	packages/core/src/step.test.ts
#	packages/world-local/src/storage/events-storage.ts
#	packages/world-postgres/src/drizzle/migrations/meta/_journal.json
Sign up for freeto 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.

2 participants

@VaguelySerious@TooTallNate
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

[errors] Replace chalk import with inline ANSI shim - #1915

Merged
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk
May 5, 2026
Merged

[errors] Replace chalk import with inline ANSI shim#1915
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

@workflow/errors/ansi imports chalk, which transitively require('os')s via supports-color. That module is reachable from the workflow-VM bundle through:

workflow → @workflow/core/workflow/index.ts
→ context-errors.ts
→ context-violation-error.ts
→ @workflow/errors/ansi ← imports chalk

The workflow VM has no require(), so importing chalk crashes every workflow at registration time:

./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)

Fix

Replace chalk with a 25-line inline SGR helper that exposes the same call surface (chalk.red, chalk.bold, …). Color detection mirrors chalk's defaults at a coarse level — FORCE_COLOR on, NO_COLOR off, otherwise gated on process.stdout.isTTY — so non-TTY logs and the workflow VM (no process) get plain text, matching the prior behavior in tests and in production log drains.

Drops the chalk runtime dependency from @workflow/errors and removes the now-unused __mocks__/chalk.ts Vitest mock. All 820 @workflow/core unit tests + 11 @workflow/errors snapshot tests pass.

Test plan

  • pnpm --filter @workflow/errors test (26 tests pass)
  • pnpm --filter @workflow/core test (820 unit tests pass; e2e excluded)
  • CI green
  • Reproduce: install latest tarballs in a v2-flow consumer (vade) and confirm step registration no longer throws ReferenceError: require is not defined

🤖 Generated with Claude Code

`@workflow/errors/ansi` is reachable from the workflow-VM bundle via
`@workflow/core/workflow` → `context-errors` → `context-violation-error`
→ here. The real `chalk` package pulls in `supports-color`, which calls
`require('os')` at module load — crashing every workflow with
`ReferenceError: require is not defined` in the sandboxed VM the moment
any user step is registered:
./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)
Replace `chalk` with a 25-line inline SGR helper exposing the same call
surface (`chalk.red`, `chalk.bold`, …). Color detection mirrors chalk's
default at a coarse level — `FORCE_COLOR` on, `NO_COLOR` off, otherwise
gated on `process.stdout.isTTY` — so non-TTY logs and the workflow VM
(no `process`) get plain text, matching previous behavior in tests and
in production log drains.
Drops the `chalk` runtime dependency from `@workflow/errors` and removes
the now-unused `__mocks__/chalk.ts` Vitest mock.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 47be549

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

This PR includes changesets to release 22 packages
NameType
@workflow/errorsPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/webPatch
workflowPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-vercelPatch
tarballsPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/world-testingPatch
@workflow/web-sharedPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

@github-actions

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.033s (-24.6% 🟢)1.005s (~)0.973s101.00x
💻 LocalExpress0.035s (-20.1% 🟢)1.006s (~)0.970s101.09x
🐘 PostgresExpress0.046s (-19.8% 🟢)1.011s (~)0.964s101.43x
💻 LocalNext.js (Turbopack)0.049s1.005s0.956s101.50x
🐘 PostgresNitro0.052s (-45.5% 🟢)1.011s (-3.0%)0.959s101.60x
🌐 RedisNext.js (Turbopack)0.054s1.005s0.951s101.65x
🐘 PostgresNext.js (Turbopack)0.057s1.011s0.953s101.76x
🌐 MongoDBNext.js (Turbopack)0.091s1.007s0.916s102.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.295s (+25.2% 🔺)2.353s (+10.2% 🔺)2.058s101.00x
▲ VercelNitro0.366s (-10.6% 🟢)2.033s (-19.0% 🟢)1.667s101.24x
▲ VercelNext.js (Turbopack)0.558s (+122.1% 🔺)2.742s (+17.5% 🔺)2.183s101.90x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.070s (-5.4% 🟢)2.007s (~)0.937s101.00x
💻 LocalExpress1.072s (-4.7%)2.007s (~)0.935s101.00x
🐘 PostgresExpress1.082s (-5.6% 🟢)2.009s (~)0.927s101.01x
🐘 PostgresNitro1.084s (-4.9%)2.009s (~)0.925s101.01x
💻 LocalNext.js (Turbopack)1.110s2.006s0.896s101.04x
🌐 RedisNext.js (Turbopack)1.111s2.008s0.897s101.04x
🐘 PostgresNext.js (Turbopack)1.114s2.010s0.897s101.04x
🌐 MongoDBNext.js (Turbopack)1.148s2.009s0.861s101.07x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.586s (-15.4% 🟢)3.902s (+2.5%)2.317s101.00x
▲ VercelNitro1.641s (-57.8% 🟢)4.080s (-30.9% 🟢)2.439s101.04x
▲ VercelNext.js (Turbopack)2.861s (+40.6% 🔺)4.792s (+25.1% 🔺)1.931s101.80x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express10.400s (-5.1% 🟢)11.014s (~)0.614s31.00x
🐘 PostgresNitro10.402s (-4.3%)11.015s (~)0.613s31.00x
💻 LocalExpress10.409s (-4.7%)11.022s (~)0.613s31.00x
💻 LocalNitro10.422s (-4.8%)11.023s (~)0.601s31.00x
🌐 MongoDBNext.js (Turbopack)10.655s11.023s0.368s31.02x
🐘 PostgresNext.js (Turbopack)10.667s11.015s0.348s31.03x
🌐 RedisNext.js (Turbopack)10.669s11.023s0.354s31.03x
💻 LocalNext.js (Turbopack)10.682s11.020s0.338s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.559s (-42.9% 🟢)15.361s (-38.8% 🟢)1.802s21.00x
▲ VercelExpress13.917s (-18.0% 🟢)15.853s (-20.8% 🟢)1.936s21.03x
▲ VercelNext.js (Turbopack)14.322s (-17.3% 🟢)16.622s (-14.3% 🟢)2.299s21.06x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.471s (-7.6% 🟢)14.017s (-6.7% 🟢)0.546s51.00x
💻 LocalExpress13.477s (-10.0% 🟢)14.027s (-6.7% 🟢)0.550s51.00x
🐘 PostgresNitro13.481s (-7.6% 🟢)14.021s (-6.7% 🟢)0.540s51.00x
💻 LocalNitro13.483s (-10.5% 🟢)14.027s (-12.5% 🟢)0.544s51.00x
🌐 MongoDBNext.js (Turbopack)13.835s14.025s0.190s51.03x
🌐 RedisNext.js (Turbopack)14.050s15.030s0.980s41.04x
💻 LocalNext.js (Turbopack)14.060s14.628s0.569s51.04x
🐘 PostgresNext.js (Turbopack)14.069s15.020s0.951s41.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.544s (-66.6% 🟢)23.739s (-64.4% 🟢)2.195s31.00x
▲ VercelExpress22.795s (-54.7% 🟢)25.107s (-52.2% 🟢)2.312s31.06x
▲ VercelNext.js (Turbopack)23.551s (-55.2% 🟢)26.276s (-51.9% 🟢)2.725s31.09x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express11.836s (-15.5% 🟢)12.015s (-17.7% 🟢)0.179s81.00x
💻 LocalNitro11.946s (-28.8% 🟢)12.272s (-27.9% 🟢)0.326s81.01x
💻 LocalExpress11.949s (-28.0% 🟢)12.146s (-28.7% 🟢)0.197s81.01x
🐘 PostgresNitro11.970s (-14.3% 🟢)12.394s (-13.4% 🟢)0.424s81.01x
🌐 MongoDBNext.js (Turbopack)12.590s13.024s0.433s71.06x
💻 LocalNext.js (Turbopack)12.972s13.452s0.480s71.10x
🌐 RedisNext.js (Turbopack)13.100s13.884s0.784s71.11x
🐘 PostgresNext.js (Turbopack)13.185s14.020s0.835s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express34.548s (-71.5% 🟢)36.366s (-70.6% 🟢)1.818s31.00x
▲ VercelNitro35.156s (-91.7% 🟢)37.155s (-91.2% 🟢)1.999s31.02x
▲ VercelNext.js (Turbopack)35.694s (-90.9% 🟢)37.608s (-90.5% 🟢)1.913s31.03x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.147s (-9.0% 🟢)2.007s (~)0.860s151.00x
🐘 PostgresNitro1.156s (-9.3% 🟢)2.007s (~)0.851s151.01x
💻 LocalNitro1.188s (-27.2% 🟢)2.006s (-3.3%)0.818s151.04x
💻 LocalExpress1.197s (-19.6% 🟢)2.006s (~)0.808s151.04x
🐘 PostgresNext.js (Turbopack)1.213s2.008s0.794s151.06x
🌐 RedisNext.js (Turbopack)1.268s2.006s0.738s151.10x
💻 LocalNext.js (Turbopack)1.378s2.073s0.695s151.20x
🌐 MongoDBNext.js (Turbopack)2.011s2.591s0.580s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.918s (+3.6%)4.656s (+7.7% 🔺)1.738s71.00x
▲ VercelNext.js (Turbopack)4.329s (+27.4% 🔺)6.218s (+26.1% 🔺)1.889s51.48x
▲ VercelExpress4.519s (+58.0% 🔺)6.298s (+36.2% 🔺)1.779s51.55x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.226s (-48.1% 🟢)2.007s (-33.3% 🟢)0.780s151.00x
🐘 PostgresNitro1.234s (-47.5% 🟢)2.007s (-33.3% 🟢)0.773s151.01x
🐘 PostgresNext.js (Turbopack)1.356s2.007s0.651s151.11x
💻 LocalNitro1.791s (-43.0% 🟢)2.007s (-48.4% 🟢)0.216s151.46x
💻 LocalExpress1.792s (-39.3% 🟢)2.006s (-41.9% 🟢)0.214s151.46x
💻 LocalNext.js (Turbopack)1.806s2.073s0.267s151.47x
🌐 RedisNext.js (Turbopack)2.385s3.008s0.623s101.94x
🌐 MongoDBNext.js (Turbopack)3.564s4.008s0.444s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.458s (+23.2% 🔺)6.268s (+22.7% 🔺)1.811s51.00x
▲ VercelNext.js (Turbopack)5.613s (-20.9% 🟢)7.797s (-12.4% 🟢)2.184s41.26x
▲ VercelNitro6.080s (+50.1% 🔺)8.092s (+36.7% 🔺)2.012s41.36x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.364s (-60.9% 🟢)2.006s (-50.0% 🟢)0.642s151.00x
🐘 PostgresNitro1.374s (-60.5% 🟢)2.007s (-49.9% 🟢)0.633s151.01x
🐘 PostgresNext.js (Turbopack)1.627s2.007s0.380s151.19x
🌐 RedisNext.js (Turbopack)3.621s4.009s0.389s82.65x
💻 LocalNext.js (Turbopack)5.000s5.511s0.511s63.67x
💻 LocalNitro5.331s (-36.2% 🟢)5.847s (-35.2% 🟢)0.517s63.91x
💻 LocalExpress5.417s (-35.0% 🟢)5.679s (-37.1% 🟢)0.263s63.97x
🌐 MongoDBNext.js (Turbopack)6.223s7.012s0.788s54.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.257s (+47.6% 🔺)8.386s (+36.9% 🔺)2.129s41.00x
▲ VercelNext.js (Turbopack)7.288s (-18.3% 🟢)9.279s (-15.3% 🟢)1.991s41.16x
▲ VercelNitro8.700s (+146.8% 🔺)10.899s (+96.9% 🔺)2.199s31.39x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.153s (-8.3% 🟢)2.009s (~)0.857s151.00x
🐘 PostgresExpress1.154s (-8.2% 🟢)2.009s (~)0.855s151.00x
🐘 PostgresNext.js (Turbopack)1.210s2.007s0.797s151.05x
🌐 RedisNext.js (Turbopack)1.257s2.006s0.749s151.09x
💻 LocalNext.js (Turbopack)1.353s2.006s0.653s151.17x
💻 LocalExpress1.384s (-26.9% 🟢)2.006s (-15.1% 🟢)0.622s151.20x
💻 LocalNitro1.397s (-25.1% 🟢)2.007s (-14.3% 🟢)0.610s151.21x
🌐 MongoDBNext.js (Turbopack)2.013s2.590s0.577s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.363s (-8.5% 🟢)4.099s (-5.8% 🟢)1.736s81.00x
▲ VercelNitro3.169s (+28.9% 🔺)5.005s (+20.0% 🔺)1.836s71.34x
▲ VercelNext.js (Turbopack)3.909s (+33.3% 🔺)6.008s (+29.4% 🔺)2.100s51.65x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.231s (-47.4% 🟢)2.009s (-33.3% 🟢)0.778s151.00x
🐘 PostgresExpress1.232s (-47.4% 🟢)2.007s (-33.3% 🟢)0.776s151.00x
🐘 PostgresNext.js (Turbopack)1.354s2.008s0.654s151.10x
💻 LocalNitro2.049s (-33.1% 🟢)2.509s (-35.4% 🟢)0.460s121.67x
💻 LocalNext.js (Turbopack)2.074s2.735s0.661s111.69x
💻 LocalExpress2.135s (-31.8% 🟢)2.593s (-31.1% 🟢)0.457s121.74x
🌐 RedisNext.js (Turbopack)2.370s3.008s0.639s101.93x
🌐 MongoDBNext.js (Turbopack)3.577s4.009s0.432s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.907s (+20.8% 🔺)5.774s (+13.7% 🔺)1.867s61.00x
▲ VercelExpress4.205s (+31.7% 🔺)6.052s (+26.3% 🔺)1.847s51.08x
▲ VercelNext.js (Turbopack)4.751s (+51.2% 🔺)6.510s (+44.0% 🔺)1.759s51.22x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.358s (-61.2% 🟢)2.008s (-49.9% 🟢)0.650s151.00x
🐘 PostgresNitro1.366s (-60.7% 🟢)2.008s (-49.9% 🟢)0.642s151.01x
🐘 PostgresNext.js (Turbopack)1.651s2.009s0.358s151.22x
🌐 RedisNext.js (Turbopack)3.648s4.010s0.362s82.69x
💻 LocalExpress5.565s (-36.8% 🟢)6.015s (-35.1% 🟢)0.450s54.10x
💻 LocalNext.js (Turbopack)5.704s6.214s0.510s54.20x
💻 LocalNitro5.946s (-35.0% 🟢)6.348s (-36.7% 🟢)0.402s64.38x
🌐 MongoDBNext.js (Turbopack)6.250s7.013s0.763s54.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.891s (+15.7% 🔺)7.641s (+12.1% 🔺)1.750s41.00x
▲ VercelExpress6.208s (-3.2%)7.682s (-6.1% 🟢)1.474s41.05x
▲ VercelNext.js (Turbopack)6.830s (+1.1%)8.929s (+4.5%)2.099s41.16x

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

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.451s (-46.2% 🟢)1.006s (-1.7%)0.555s601.00x
🐘 PostgresNitro0.453s (-44.7% 🟢)1.006s (~)0.553s601.01x
💻 LocalNitro0.471s (-52.0% 🟢)1.004s (-8.2% 🟢)0.533s601.04x
💻 LocalExpress0.506s (-48.6% 🟢)1.022s (-5.1% 🟢)0.515s591.12x
🌐 MongoDBNext.js (Turbopack)0.597s1.006s0.409s601.32x
🌐 RedisNext.js (Turbopack)0.639s1.004s0.365s601.42x
🐘 PostgresNext.js (Turbopack)0.662s1.006s0.344s601.47x
💻 LocalNext.js (Turbopack)0.711s1.004s0.293s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.747s (-78.5% 🟢)6.670s (-72.2% 🟢)1.923s101.00x
▲ VercelExpress4.916s (-74.1% 🟢)6.991s (-67.2% 🟢)2.075s91.04x
▲ VercelNext.js (Turbopack)6.804s (-53.1% 🟢)8.802s (-45.3% 🟢)1.998s71.43x

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

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.032s (-47.8% 🟢)1.458s (-35.4% 🟢)0.426s621.00x
🐘 PostgresNitro1.047s (-45.7% 🟢)1.507s (-28.3% 🟢)0.459s601.01x
💻 LocalExpress1.184s (-60.7% 🟢)2.006s (-44.1% 🟢)0.821s451.15x
💻 LocalNitro1.191s (-60.8% 🟢)2.006s (-46.6% 🟢)0.815s451.15x
🌐 MongoDBNext.js (Turbopack)1.503s2.007s0.504s451.46x
🐘 PostgresNext.js (Turbopack)1.587s2.007s0.419s451.54x
🌐 RedisNext.js (Turbopack)1.611s2.028s0.417s451.56x
💻 LocalNext.js (Turbopack)1.741s2.005s0.265s451.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.285s (-64.4% 🟢)14.394s (-60.9% 🟢)2.108s71.00x
▲ VercelNitro12.810s (-67.6% 🟢)15.636s (-62.1% 🟢)2.825s61.04x
▲ VercelNext.js (Turbopack)15.611s (-68.7% 🟢)18.106s (-65.0% 🟢)2.494s61.27x

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

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.878s (-52.9% 🟢)2.076s (-52.5% 🟢)0.198s581.00x
🐘 PostgresNitro2.079s (-49.3% 🟢)2.550s (-44.6% 🟢)0.470s481.11x
💻 LocalNitro2.680s (-71.2% 🟢)3.008s (-70.0% 🟢)0.327s401.43x
💻 LocalExpress2.692s (-70.8% 🟢)3.008s (-70.0% 🟢)0.316s401.43x
🐘 PostgresNext.js (Turbopack)3.092s4.009s0.917s301.65x
🌐 RedisNext.js (Turbopack)3.096s3.880s0.783s311.65x
🌐 MongoDBNext.js (Turbopack)3.670s4.013s0.343s301.95x
💻 LocalNext.js (Turbopack)3.704s4.008s0.303s301.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro39.197s (-59.6% 🟢)42.019s (-57.3% 🟢)2.822s31.00x
▲ VercelExpress40.332s (-69.0% 🟢)42.693s (-67.7% 🟢)2.361s31.03x
▲ VercelNext.js (Turbopack)44.774s (-58.2% 🟢)48.551s (-55.4% 🟢)3.777s31.14x

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

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.176s (-37.5% 🟢)1.006s (~)0.829s601.00x
🐘 PostgresNitro0.182s (-35.8% 🟢)1.005s (~)0.824s601.03x
🐘 PostgresNext.js (Turbopack)0.232s1.006s0.774s601.32x
🌐 RedisNext.js (Turbopack)0.242s1.004s0.762s601.37x
💻 LocalExpress0.439s (-21.6% 🟢)1.004s (~)0.565s602.49x
💻 LocalNitro0.474s (-21.6% 🟢)1.021s (~)0.547s592.69x
💻 LocalNext.js (Turbopack)0.539s1.004s0.465s603.06x
🌐 MongoDBNext.js (Turbopack)1.032s1.825s0.793s335.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.231s (+34.3% 🔺)4.189s (+25.0% 🔺)1.958s151.00x
▲ VercelExpress2.259s (+15.6% 🔺)4.431s (+21.8% 🔺)2.172s141.01x
▲ VercelNext.js (Turbopack)4.335s (+114.4% 🔺)6.320s (+66.6% 🔺)1.984s101.94x

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

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.309s (-39.4% 🟢)1.006s (~)0.697s901.00x
🐘 PostgresNitro0.312s (-37.2% 🟢)1.006s (~)0.694s901.01x
🌐 RedisNext.js (Turbopack)0.412s1.004s0.592s901.33x
🐘 PostgresNext.js (Turbopack)0.450s1.006s0.555s901.46x
💻 LocalNitro2.179s (-14.2% 🟢)3.008s (~)0.829s307.05x
💻 LocalExpress2.268s (-9.8% 🟢)2.911s (-3.3%)0.644s317.34x
💻 LocalNext.js (Turbopack)2.382s2.977s0.595s317.71x
🌐 MongoDBNext.js (Turbopack)2.594s3.008s0.414s308.39x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express7.284s (+139.1% 🔺)9.227s (+91.9% 🔺)1.943s101.00x
▲ VercelNitro7.305s (+126.5% 🔺)9.647s (+100.1% 🔺)2.342s101.00x
▲ VercelNext.js (Turbopack)8.118s (+129.6% 🔺)10.545s (+103.1% 🔺)2.427s91.11x

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

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.618s (-24.5% 🟢)1.006s (-1.1%)0.388s1201.00x
🐘 PostgresNitro0.649s (-17.9% 🟢)1.006s (~)0.357s1201.05x
🌐 RedisNext.js (Turbopack)0.770s1.004s0.233s1201.25x
🐘 PostgresNext.js (Turbopack)0.933s1.219s0.286s991.51x
🌐 MongoDBNext.js (Turbopack)5.361s6.012s0.651s208.67x
💻 LocalExpress10.227s (-8.6% 🟢)10.863s (-9.0% 🟢)0.636s1216.54x
💻 LocalNitro10.251s (-8.4% 🟢)10.781s (-7.6% 🟢)0.530s1216.58x
💻 LocalNext.js (Turbopack)10.950s11.757s0.807s1117.71x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express20.578s (+177.3% 🔺)23.078s (+149.6% 🔺)2.500s61.00x
▲ VercelNext.js (Turbopack)21.742s (+110.5% 🔺)24.590s (+100.1% 🔺)2.848s51.06x
▲ VercelNitro25.208s (+226.4% 🔺)27.039s (+187.6% 🔺)1.831s51.22x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.129s (+450.3% 🔺)1.997s (+99.9% 🔺)0.001s (-18.8% 🟢)2.009s (+98.7% 🔺)0.881s101.00x
💻 LocalExpress1.131s (+468.1% 🔺)2.005s (+99.6% 🔺)0.012s (+2.5%)2.019s (+98.4% 🔺)0.888s101.00x
💻 LocalNitro1.132s (+429.6% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.021s (+98.3% 🔺)0.889s101.00x
🐘 PostgresNitro1.149s (+460.7% 🔺)1.994s (+99.5% 🔺)0.002s (+6.7% 🔺)2.012s (+99.0% 🔺)0.863s101.02x
💻 LocalNext.js (Turbopack)1.178s2.003s0.012s2.019s0.840s101.04x
🐘 PostgresNext.js (Turbopack)1.192s2.002s0.001s2.010s0.818s101.06x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.184s (-43.0% 🟢)3.612s (-31.5% 🟢)1.614s (+117.5% 🔺)5.719s (-11.8% 🟢)3.535s101.00x
▲ VercelExpress2.198s (-12.3% 🟢)3.538s (-13.5% 🟢)1.751s (+82.3% 🔺)5.836s (+4.4%)3.637s101.01x
▲ VercelNext.js (Turbopack)4.942s (-27.9% 🟢)5.438s (-37.1% 🟢)1.145s (+81.2% 🔺)8.364s (-14.6% 🟢)3.421s102.26x

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

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.496s (+137.4% 🔺)2.000s (+98.7% 🔺)0.003s (-9.5% 🟢)2.026s (+98.1% 🔺)0.531s301.00x
🐘 PostgresNitro1.507s (+141.5% 🔺)2.002s (+98.8% 🔺)0.004s (-8.9% 🟢)2.024s (+97.9% 🔺)0.517s301.01x
💻 LocalNitro1.538s (+83.4% 🔺)2.012s (+98.8% 🔺)0.010s (+11.7% 🔺)2.025s (+81.4% 🔺)0.486s301.03x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.004s2.025s0.367s301.11x
💻 LocalExpress1.716s (+126.6% 🔺)2.012s (+95.5% 🔺)0.009s (-2.1%)2.202s (+111.8% 🔺)0.486s281.15x
💻 LocalNext.js (Turbopack)2.055s2.009s0.010s2.423s0.368s251.37x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.789s (-80.3% 🟢)7.537s (-75.5% 🟢)0.268s (+139.0% 🔺)8.404s (-73.6% 🟢)2.615s81.00x
▲ VercelExpress6.045s (-7.1% 🟢)7.653s (-4.5%)0.313s (-23.5% 🟢)8.503s (-3.8%)2.457s81.04x
▲ VercelNext.js (Turbopack)12.326s (-27.1% 🟢)13.810s (-24.3% 🟢)0.429s (+103.2% 🔺)15.364s (-18.9% 🟢)3.038s42.13x

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

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.660s (-31.8% 🟢)1.031s (-17.4% 🟢)0.000s (+24.1% 🔺)1.048s (-16.7% 🟢)0.387s581.00x
🐘 PostgresExpress0.688s (-28.4% 🟢)1.061s (-17.0% 🟢)0.000s (+105.4% 🔺)1.079s (-17.4% 🟢)0.391s561.04x
🐘 PostgresNext.js (Turbopack)0.783s1.090s0.000s1.097s0.314s561.19x
💻 LocalNitro1.345s (+10.0% 🔺)2.016s (~)0.000s (+333.3% 🔺)2.018s (~)0.674s302.04x
💻 LocalNext.js (Turbopack)1.400s2.014s0.000s2.017s0.617s302.12x
💻 LocalExpress1.515s (+23.7% 🔺)2.015s (~)0.000s (-35.7% 🟢)2.196s (+8.6% 🔺)0.681s282.29x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.737s (+22.5% 🔺)5.067s (+15.4% 🔺)0.000s (+18.2% 🔺)5.709s (+18.7% 🔺)1.972s111.00x
▲ VercelExpress4.218s (+12.8% 🔺)5.770s (+13.1% 🔺)0.000s (+50.0% 🔺)6.218s (+12.4% 🔺)2.000s111.13x
▲ VercelNext.js (Turbopack)6.160s (-39.5% 🟢)6.979s (-39.4% 🟢)0.000s (NaN%)8.458s (-29.8% 🟢)2.298s81.65x

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

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.286s (-28.2% 🟢)2.001s (-6.6% 🟢)0.000s (-100.0% 🟢)2.020s (-7.1% 🟢)0.734s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.067s (-5.1% 🟢)0.000s (+Infinity% 🔺)2.115s (-3.8%)0.730s291.08x
🐘 PostgresNext.js (Turbopack)1.629s2.225s0.000s2.239s0.610s271.27x
💻 LocalNext.js (Turbopack)2.539s3.125s0.000s3.128s0.589s201.97x
💻 LocalExpress3.054s (-11.9% 🟢)3.903s (-3.2%)0.000s (-45.3% 🟢)3.905s (-3.2%)0.852s162.37x
💻 LocalNitro3.180s (-6.1% 🟢)3.966s (-1.6%)0.000s (-41.4% 🟢)3.971s (-1.6%)0.791s162.47x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.004s (+30.9% 🔺)7.517s (+24.8% 🔺)0.000s (+Infinity% 🔺)8.103s (+25.5% 🔺)2.099s81.00x
▲ VercelNitro6.750s (+64.9% 🔺)8.558s (+59.2% 🔺)0.000s (-100.0% 🟢)9.120s (+57.4% 🔺)2.370s71.12x
▲ VercelNext.js (Turbopack)8.561s (+52.4% 🔺)9.916s (+42.0% 🔺)0.000s (-100.0% 🟢)11.240s (+49.1% 🔺)2.679s61.43x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro11/21
🐘 PostgresExpress17/21
▲ VercelNitro11/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres19/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres17/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

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production85902191078
✅ 💻 Local Development95702191176
✅ 📦 Local Production95702191176
✅ 🐘 Local Postgres95702191176
✅ 🪟 Windows980098
✅ 📋 Other5100176686
Total4338010525390

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro72026
✅ example72026
✅ express72026
✅ fastify72026
✅ hono72026
✅ nextjs-turbopack9602
✅ nextjs-webpack9602
✅ nitro72026
✅ nuxt72026
✅ sveltekit9107
✅ vite72026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9800
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable73025
✅ e2e-local-dev-tanstack-start-stable73025
✅ e2e-local-postgres-nest-stable73025
✅ e2e-local-postgres-tanstack-start-stable73025
✅ e2e-local-prod-nest-stable73025
✅ e2e-local-prod-tanstack-start-stable73025
✅ e2e-vercel-prod-tanstack-start72026

📋 View full workflow run

Move the inline ANSI shim out of `ansi.ts` into its own
`./internal-chalk.ts` so tests can swap it for a tag-emitting mock via
`vi.mock('./internal-chalk.js', …)`. Snapshots for `Ansi.code` /
`Ansi.hint` / `Ansi.note` / `Ansi.help` / `Ansi.docs` go back to the
readable HTML-like form (`<blue><b>hint:</b> try reloading</blue>`),
making it obvious from the test file which fragments are colored and
how — the same review affordance the previous `__mocks__/chalk.ts`
mock provided before the chalk dep was dropped.
`Ansi.frame` and `Ansi.inline` remain plain in snapshots because they
don't apply any styling themselves; that matches the pre-fix behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. The diagnosis is correct, the fix is appropriately surgical, and the shim is well-written.

Verified

Reachability claim — confirmed: packages/core/src/workflow/index.tscontext-errors.tscontext-violation-error.tsimport * as Ansi from '@workflow/errors/ansi'. So any consumer that uses workflow-context-checked APIs (createHook, createWebhook, getWorkflowMetadata, etc.) ends up dragging the entire ansi/chalk transitive graph into the workflow VM bundle.

Output parity — for the only nested call in ansi.ts (code(): chalk.italic(\${chalk.dim('`')}${str}${chalk.dim('`')}`)), the shim produces byte-for-byte the same SGR sequence as chalk@5.6.2`:

\x1b[3m\x1b[2m`\x1b[22mfoo\x1b[2m`\x1b[22m\x1b[23m

The 22 (normal intensity) close after each dim correctly leaves italic (3) on, since 22 only resets bold/dim — same as chalk's behavior.

Bundle effect — built workbench/example from this branch and grepped the workflow VM bundle (the string passed to workflowEntrypoint):

$ grep -cE "supports-color|require\(['\"](node:)?os['\"]|chalk|internal_chalk" /tmp/vm-bundle.txt
0

No chalk, no supports-color, no os anywhere. (The example workbench's VM bundle didn't actually pull in context-violation-error due to tree-shaking in this particular workflow set, so the chalk references were already absent on main for this specific bundle — but the change is still correct, and downstream consumers like vade that exercise the workflow-context paths will get the fix.)

Testspnpm --filter @workflow/errors test passes (4 files, 26 tests including all 11 ansi.test.ts snapshots), pnpm --filter @workflow/core test passes (820 tests).

CI failures are pre-existing flakes

The two red checks (Unit Tests (ubuntu-latest), E2E Required Check) are both from the same flaky world-postgres test:

FAIL test/spec.test.ts > sequential steps with stream complete in a single flow invocation
AssertionError: expected +0 to be 1 // Object.is equality

This same test fails on origin/main HEAD (run 25343680860 job 74307194029) and is unrelated to this change.

Minor nits (non-blocking)

  • colorEnabled is evaluated once at module-load time, whereas chalk re-evaluates lazily on each call. If a host process programmatically flips FORCE_COLOR/NO_COLOR after @workflow/errors is imported, the shim won't pick up the change. In practice no one in this codebase does that, and the JSDoc on internal-chalk.ts is implicit about it — but if you wanted to be belt-and-braces, wrapping the env-var check inside sgr() would keep parity. Totally fine to leave as-is.

  • The __mocks__/chalk.ts deletion is correct, but worth noting that the module-graph entrypoint for the test mock changed from "the chalk package as resolved by node" to "this sibling file at a literal relative path". That's fine for this codebase but means the mock now relies on vi.mock('./internal-chalk.js', ...)paths rather than package identity — slightly more fragile if ansi.ts is ever moved to a different directory. Fine for now.

Approving.

@VaguelySerious
VaguelySerious merged commit 540a2ef into mainMay 5, 2026
175 of 177 checks passed
@VaguelySerious
VaguelySerious deleted the peter/errors-no-chalk branch May 5, 2026 00:47
@github-actionsgithub-actionsBot mentioned this pull request May 5, 2026
pranaygp added a commit that referenced this pull request May 5, 2026
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
pranaygp added a commit that referenced this pull request May 5, 2026
…ignal
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
Allow disabling step sourcemap with new `sourcemap` option in builders (#1842)
[ci] Enable Vercel-prod e2e for tanstack-start (#1904)
web: configure vercelPreset() for Vercel deployments (#1815)
[core] Combine flow+step bundle and process steps eagerly (#1338)
[world-vercel] Revert stream close control framing (#1891)
[tarballs] Use turbo to build workspace deps before packing (#1908)
# Conflicts:
#	packages/core/src/runtime/step-handler.test.ts
#	packages/core/src/runtime/step-handler.ts
#	packages/core/src/runtime/suspension-handler.ts
#	packages/core/src/step.test.ts
#	packages/world-local/src/storage/events-storage.ts
#	packages/world-postgres/src/drizzle/migrations/meta/_journal.json
Sign up for freeto 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.

2 participants

@VaguelySerious@TooTallNate
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[errors] Replace chalk import with inline ANSI shim - #1915

Merged
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk
May 5, 2026
Merged

[errors] Replace chalk import with inline ANSI shim#1915
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

@workflow/errors/ansi imports chalk, which transitively require('os')s via supports-color. That module is reachable from the workflow-VM bundle through:

workflow → @workflow/core/workflow/index.ts
→ context-errors.ts
→ context-violation-error.ts
→ @workflow/errors/ansi ← imports chalk

The workflow VM has no require(), so importing chalk crashes every workflow at registration time:

./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)

Fix

Replace chalk with a 25-line inline SGR helper that exposes the same call surface (chalk.red, chalk.bold, …). Color detection mirrors chalk's defaults at a coarse level — FORCE_COLOR on, NO_COLOR off, otherwise gated on process.stdout.isTTY — so non-TTY logs and the workflow VM (no process) get plain text, matching the prior behavior in tests and in production log drains.

Drops the chalk runtime dependency from @workflow/errors and removes the now-unused __mocks__/chalk.ts Vitest mock. All 820 @workflow/core unit tests + 11 @workflow/errors snapshot tests pass.

Test plan

  • pnpm --filter @workflow/errors test (26 tests pass)
  • pnpm --filter @workflow/core test (820 unit tests pass; e2e excluded)
  • CI green
  • Reproduce: install latest tarballs in a v2-flow consumer (vade) and confirm step registration no longer throws ReferenceError: require is not defined

🤖 Generated with Claude Code

`@workflow/errors/ansi` is reachable from the workflow-VM bundle via
`@workflow/core/workflow` → `context-errors` → `context-violation-error`
→ here. The real `chalk` package pulls in `supports-color`, which calls
`require('os')` at module load — crashing every workflow with
`ReferenceError: require is not defined` in the sandboxed VM the moment
any user step is registered:
./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)
Replace `chalk` with a 25-line inline SGR helper exposing the same call
surface (`chalk.red`, `chalk.bold`, …). Color detection mirrors chalk's
default at a coarse level — `FORCE_COLOR` on, `NO_COLOR` off, otherwise
gated on `process.stdout.isTTY` — so non-TTY logs and the workflow VM
(no `process`) get plain text, matching previous behavior in tests and
in production log drains.
Drops the `chalk` runtime dependency from `@workflow/errors` and removes
the now-unused `__mocks__/chalk.ts` Vitest mock.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 47be549

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

This PR includes changesets to release 22 packages
NameType
@workflow/errorsPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/webPatch
workflowPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-vercelPatch
tarballsPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/world-testingPatch
@workflow/web-sharedPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

@github-actions

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.033s (-24.6% 🟢)1.005s (~)0.973s101.00x
💻 LocalExpress0.035s (-20.1% 🟢)1.006s (~)0.970s101.09x
🐘 PostgresExpress0.046s (-19.8% 🟢)1.011s (~)0.964s101.43x
💻 LocalNext.js (Turbopack)0.049s1.005s0.956s101.50x
🐘 PostgresNitro0.052s (-45.5% 🟢)1.011s (-3.0%)0.959s101.60x
🌐 RedisNext.js (Turbopack)0.054s1.005s0.951s101.65x
🐘 PostgresNext.js (Turbopack)0.057s1.011s0.953s101.76x
🌐 MongoDBNext.js (Turbopack)0.091s1.007s0.916s102.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.295s (+25.2% 🔺)2.353s (+10.2% 🔺)2.058s101.00x
▲ VercelNitro0.366s (-10.6% 🟢)2.033s (-19.0% 🟢)1.667s101.24x
▲ VercelNext.js (Turbopack)0.558s (+122.1% 🔺)2.742s (+17.5% 🔺)2.183s101.90x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.070s (-5.4% 🟢)2.007s (~)0.937s101.00x
💻 LocalExpress1.072s (-4.7%)2.007s (~)0.935s101.00x
🐘 PostgresExpress1.082s (-5.6% 🟢)2.009s (~)0.927s101.01x
🐘 PostgresNitro1.084s (-4.9%)2.009s (~)0.925s101.01x
💻 LocalNext.js (Turbopack)1.110s2.006s0.896s101.04x
🌐 RedisNext.js (Turbopack)1.111s2.008s0.897s101.04x
🐘 PostgresNext.js (Turbopack)1.114s2.010s0.897s101.04x
🌐 MongoDBNext.js (Turbopack)1.148s2.009s0.861s101.07x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.586s (-15.4% 🟢)3.902s (+2.5%)2.317s101.00x
▲ VercelNitro1.641s (-57.8% 🟢)4.080s (-30.9% 🟢)2.439s101.04x
▲ VercelNext.js (Turbopack)2.861s (+40.6% 🔺)4.792s (+25.1% 🔺)1.931s101.80x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express10.400s (-5.1% 🟢)11.014s (~)0.614s31.00x
🐘 PostgresNitro10.402s (-4.3%)11.015s (~)0.613s31.00x
💻 LocalExpress10.409s (-4.7%)11.022s (~)0.613s31.00x
💻 LocalNitro10.422s (-4.8%)11.023s (~)0.601s31.00x
🌐 MongoDBNext.js (Turbopack)10.655s11.023s0.368s31.02x
🐘 PostgresNext.js (Turbopack)10.667s11.015s0.348s31.03x
🌐 RedisNext.js (Turbopack)10.669s11.023s0.354s31.03x
💻 LocalNext.js (Turbopack)10.682s11.020s0.338s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.559s (-42.9% 🟢)15.361s (-38.8% 🟢)1.802s21.00x
▲ VercelExpress13.917s (-18.0% 🟢)15.853s (-20.8% 🟢)1.936s21.03x
▲ VercelNext.js (Turbopack)14.322s (-17.3% 🟢)16.622s (-14.3% 🟢)2.299s21.06x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.471s (-7.6% 🟢)14.017s (-6.7% 🟢)0.546s51.00x
💻 LocalExpress13.477s (-10.0% 🟢)14.027s (-6.7% 🟢)0.550s51.00x
🐘 PostgresNitro13.481s (-7.6% 🟢)14.021s (-6.7% 🟢)0.540s51.00x
💻 LocalNitro13.483s (-10.5% 🟢)14.027s (-12.5% 🟢)0.544s51.00x
🌐 MongoDBNext.js (Turbopack)13.835s14.025s0.190s51.03x
🌐 RedisNext.js (Turbopack)14.050s15.030s0.980s41.04x
💻 LocalNext.js (Turbopack)14.060s14.628s0.569s51.04x
🐘 PostgresNext.js (Turbopack)14.069s15.020s0.951s41.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.544s (-66.6% 🟢)23.739s (-64.4% 🟢)2.195s31.00x
▲ VercelExpress22.795s (-54.7% 🟢)25.107s (-52.2% 🟢)2.312s31.06x
▲ VercelNext.js (Turbopack)23.551s (-55.2% 🟢)26.276s (-51.9% 🟢)2.725s31.09x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express11.836s (-15.5% 🟢)12.015s (-17.7% 🟢)0.179s81.00x
💻 LocalNitro11.946s (-28.8% 🟢)12.272s (-27.9% 🟢)0.326s81.01x
💻 LocalExpress11.949s (-28.0% 🟢)12.146s (-28.7% 🟢)0.197s81.01x
🐘 PostgresNitro11.970s (-14.3% 🟢)12.394s (-13.4% 🟢)0.424s81.01x
🌐 MongoDBNext.js (Turbopack)12.590s13.024s0.433s71.06x
💻 LocalNext.js (Turbopack)12.972s13.452s0.480s71.10x
🌐 RedisNext.js (Turbopack)13.100s13.884s0.784s71.11x
🐘 PostgresNext.js (Turbopack)13.185s14.020s0.835s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express34.548s (-71.5% 🟢)36.366s (-70.6% 🟢)1.818s31.00x
▲ VercelNitro35.156s (-91.7% 🟢)37.155s (-91.2% 🟢)1.999s31.02x
▲ VercelNext.js (Turbopack)35.694s (-90.9% 🟢)37.608s (-90.5% 🟢)1.913s31.03x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.147s (-9.0% 🟢)2.007s (~)0.860s151.00x
🐘 PostgresNitro1.156s (-9.3% 🟢)2.007s (~)0.851s151.01x
💻 LocalNitro1.188s (-27.2% 🟢)2.006s (-3.3%)0.818s151.04x
💻 LocalExpress1.197s (-19.6% 🟢)2.006s (~)0.808s151.04x
🐘 PostgresNext.js (Turbopack)1.213s2.008s0.794s151.06x
🌐 RedisNext.js (Turbopack)1.268s2.006s0.738s151.10x
💻 LocalNext.js (Turbopack)1.378s2.073s0.695s151.20x
🌐 MongoDBNext.js (Turbopack)2.011s2.591s0.580s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.918s (+3.6%)4.656s (+7.7% 🔺)1.738s71.00x
▲ VercelNext.js (Turbopack)4.329s (+27.4% 🔺)6.218s (+26.1% 🔺)1.889s51.48x
▲ VercelExpress4.519s (+58.0% 🔺)6.298s (+36.2% 🔺)1.779s51.55x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.226s (-48.1% 🟢)2.007s (-33.3% 🟢)0.780s151.00x
🐘 PostgresNitro1.234s (-47.5% 🟢)2.007s (-33.3% 🟢)0.773s151.01x
🐘 PostgresNext.js (Turbopack)1.356s2.007s0.651s151.11x
💻 LocalNitro1.791s (-43.0% 🟢)2.007s (-48.4% 🟢)0.216s151.46x
💻 LocalExpress1.792s (-39.3% 🟢)2.006s (-41.9% 🟢)0.214s151.46x
💻 LocalNext.js (Turbopack)1.806s2.073s0.267s151.47x
🌐 RedisNext.js (Turbopack)2.385s3.008s0.623s101.94x
🌐 MongoDBNext.js (Turbopack)3.564s4.008s0.444s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.458s (+23.2% 🔺)6.268s (+22.7% 🔺)1.811s51.00x
▲ VercelNext.js (Turbopack)5.613s (-20.9% 🟢)7.797s (-12.4% 🟢)2.184s41.26x
▲ VercelNitro6.080s (+50.1% 🔺)8.092s (+36.7% 🔺)2.012s41.36x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.364s (-60.9% 🟢)2.006s (-50.0% 🟢)0.642s151.00x
🐘 PostgresNitro1.374s (-60.5% 🟢)2.007s (-49.9% 🟢)0.633s151.01x
🐘 PostgresNext.js (Turbopack)1.627s2.007s0.380s151.19x
🌐 RedisNext.js (Turbopack)3.621s4.009s0.389s82.65x
💻 LocalNext.js (Turbopack)5.000s5.511s0.511s63.67x
💻 LocalNitro5.331s (-36.2% 🟢)5.847s (-35.2% 🟢)0.517s63.91x
💻 LocalExpress5.417s (-35.0% 🟢)5.679s (-37.1% 🟢)0.263s63.97x
🌐 MongoDBNext.js (Turbopack)6.223s7.012s0.788s54.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.257s (+47.6% 🔺)8.386s (+36.9% 🔺)2.129s41.00x
▲ VercelNext.js (Turbopack)7.288s (-18.3% 🟢)9.279s (-15.3% 🟢)1.991s41.16x
▲ VercelNitro8.700s (+146.8% 🔺)10.899s (+96.9% 🔺)2.199s31.39x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.153s (-8.3% 🟢)2.009s (~)0.857s151.00x
🐘 PostgresExpress1.154s (-8.2% 🟢)2.009s (~)0.855s151.00x
🐘 PostgresNext.js (Turbopack)1.210s2.007s0.797s151.05x
🌐 RedisNext.js (Turbopack)1.257s2.006s0.749s151.09x
💻 LocalNext.js (Turbopack)1.353s2.006s0.653s151.17x
💻 LocalExpress1.384s (-26.9% 🟢)2.006s (-15.1% 🟢)0.622s151.20x
💻 LocalNitro1.397s (-25.1% 🟢)2.007s (-14.3% 🟢)0.610s151.21x
🌐 MongoDBNext.js (Turbopack)2.013s2.590s0.577s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.363s (-8.5% 🟢)4.099s (-5.8% 🟢)1.736s81.00x
▲ VercelNitro3.169s (+28.9% 🔺)5.005s (+20.0% 🔺)1.836s71.34x
▲ VercelNext.js (Turbopack)3.909s (+33.3% 🔺)6.008s (+29.4% 🔺)2.100s51.65x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.231s (-47.4% 🟢)2.009s (-33.3% 🟢)0.778s151.00x
🐘 PostgresExpress1.232s (-47.4% 🟢)2.007s (-33.3% 🟢)0.776s151.00x
🐘 PostgresNext.js (Turbopack)1.354s2.008s0.654s151.10x
💻 LocalNitro2.049s (-33.1% 🟢)2.509s (-35.4% 🟢)0.460s121.67x
💻 LocalNext.js (Turbopack)2.074s2.735s0.661s111.69x
💻 LocalExpress2.135s (-31.8% 🟢)2.593s (-31.1% 🟢)0.457s121.74x
🌐 RedisNext.js (Turbopack)2.370s3.008s0.639s101.93x
🌐 MongoDBNext.js (Turbopack)3.577s4.009s0.432s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.907s (+20.8% 🔺)5.774s (+13.7% 🔺)1.867s61.00x
▲ VercelExpress4.205s (+31.7% 🔺)6.052s (+26.3% 🔺)1.847s51.08x
▲ VercelNext.js (Turbopack)4.751s (+51.2% 🔺)6.510s (+44.0% 🔺)1.759s51.22x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.358s (-61.2% 🟢)2.008s (-49.9% 🟢)0.650s151.00x
🐘 PostgresNitro1.366s (-60.7% 🟢)2.008s (-49.9% 🟢)0.642s151.01x
🐘 PostgresNext.js (Turbopack)1.651s2.009s0.358s151.22x
🌐 RedisNext.js (Turbopack)3.648s4.010s0.362s82.69x
💻 LocalExpress5.565s (-36.8% 🟢)6.015s (-35.1% 🟢)0.450s54.10x
💻 LocalNext.js (Turbopack)5.704s6.214s0.510s54.20x
💻 LocalNitro5.946s (-35.0% 🟢)6.348s (-36.7% 🟢)0.402s64.38x
🌐 MongoDBNext.js (Turbopack)6.250s7.013s0.763s54.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.891s (+15.7% 🔺)7.641s (+12.1% 🔺)1.750s41.00x
▲ VercelExpress6.208s (-3.2%)7.682s (-6.1% 🟢)1.474s41.05x
▲ VercelNext.js (Turbopack)6.830s (+1.1%)8.929s (+4.5%)2.099s41.16x

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

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.451s (-46.2% 🟢)1.006s (-1.7%)0.555s601.00x
🐘 PostgresNitro0.453s (-44.7% 🟢)1.006s (~)0.553s601.01x
💻 LocalNitro0.471s (-52.0% 🟢)1.004s (-8.2% 🟢)0.533s601.04x
💻 LocalExpress0.506s (-48.6% 🟢)1.022s (-5.1% 🟢)0.515s591.12x
🌐 MongoDBNext.js (Turbopack)0.597s1.006s0.409s601.32x
🌐 RedisNext.js (Turbopack)0.639s1.004s0.365s601.42x
🐘 PostgresNext.js (Turbopack)0.662s1.006s0.344s601.47x
💻 LocalNext.js (Turbopack)0.711s1.004s0.293s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.747s (-78.5% 🟢)6.670s (-72.2% 🟢)1.923s101.00x
▲ VercelExpress4.916s (-74.1% 🟢)6.991s (-67.2% 🟢)2.075s91.04x
▲ VercelNext.js (Turbopack)6.804s (-53.1% 🟢)8.802s (-45.3% 🟢)1.998s71.43x

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

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.032s (-47.8% 🟢)1.458s (-35.4% 🟢)0.426s621.00x
🐘 PostgresNitro1.047s (-45.7% 🟢)1.507s (-28.3% 🟢)0.459s601.01x
💻 LocalExpress1.184s (-60.7% 🟢)2.006s (-44.1% 🟢)0.821s451.15x
💻 LocalNitro1.191s (-60.8% 🟢)2.006s (-46.6% 🟢)0.815s451.15x
🌐 MongoDBNext.js (Turbopack)1.503s2.007s0.504s451.46x
🐘 PostgresNext.js (Turbopack)1.587s2.007s0.419s451.54x
🌐 RedisNext.js (Turbopack)1.611s2.028s0.417s451.56x
💻 LocalNext.js (Turbopack)1.741s2.005s0.265s451.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.285s (-64.4% 🟢)14.394s (-60.9% 🟢)2.108s71.00x
▲ VercelNitro12.810s (-67.6% 🟢)15.636s (-62.1% 🟢)2.825s61.04x
▲ VercelNext.js (Turbopack)15.611s (-68.7% 🟢)18.106s (-65.0% 🟢)2.494s61.27x

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

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.878s (-52.9% 🟢)2.076s (-52.5% 🟢)0.198s581.00x
🐘 PostgresNitro2.079s (-49.3% 🟢)2.550s (-44.6% 🟢)0.470s481.11x
💻 LocalNitro2.680s (-71.2% 🟢)3.008s (-70.0% 🟢)0.327s401.43x
💻 LocalExpress2.692s (-70.8% 🟢)3.008s (-70.0% 🟢)0.316s401.43x
🐘 PostgresNext.js (Turbopack)3.092s4.009s0.917s301.65x
🌐 RedisNext.js (Turbopack)3.096s3.880s0.783s311.65x
🌐 MongoDBNext.js (Turbopack)3.670s4.013s0.343s301.95x
💻 LocalNext.js (Turbopack)3.704s4.008s0.303s301.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro39.197s (-59.6% 🟢)42.019s (-57.3% 🟢)2.822s31.00x
▲ VercelExpress40.332s (-69.0% 🟢)42.693s (-67.7% 🟢)2.361s31.03x
▲ VercelNext.js (Turbopack)44.774s (-58.2% 🟢)48.551s (-55.4% 🟢)3.777s31.14x

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

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.176s (-37.5% 🟢)1.006s (~)0.829s601.00x
🐘 PostgresNitro0.182s (-35.8% 🟢)1.005s (~)0.824s601.03x
🐘 PostgresNext.js (Turbopack)0.232s1.006s0.774s601.32x
🌐 RedisNext.js (Turbopack)0.242s1.004s0.762s601.37x
💻 LocalExpress0.439s (-21.6% 🟢)1.004s (~)0.565s602.49x
💻 LocalNitro0.474s (-21.6% 🟢)1.021s (~)0.547s592.69x
💻 LocalNext.js (Turbopack)0.539s1.004s0.465s603.06x
🌐 MongoDBNext.js (Turbopack)1.032s1.825s0.793s335.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.231s (+34.3% 🔺)4.189s (+25.0% 🔺)1.958s151.00x
▲ VercelExpress2.259s (+15.6% 🔺)4.431s (+21.8% 🔺)2.172s141.01x
▲ VercelNext.js (Turbopack)4.335s (+114.4% 🔺)6.320s (+66.6% 🔺)1.984s101.94x

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

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.309s (-39.4% 🟢)1.006s (~)0.697s901.00x
🐘 PostgresNitro0.312s (-37.2% 🟢)1.006s (~)0.694s901.01x
🌐 RedisNext.js (Turbopack)0.412s1.004s0.592s901.33x
🐘 PostgresNext.js (Turbopack)0.450s1.006s0.555s901.46x
💻 LocalNitro2.179s (-14.2% 🟢)3.008s (~)0.829s307.05x
💻 LocalExpress2.268s (-9.8% 🟢)2.911s (-3.3%)0.644s317.34x
💻 LocalNext.js (Turbopack)2.382s2.977s0.595s317.71x
🌐 MongoDBNext.js (Turbopack)2.594s3.008s0.414s308.39x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express7.284s (+139.1% 🔺)9.227s (+91.9% 🔺)1.943s101.00x
▲ VercelNitro7.305s (+126.5% 🔺)9.647s (+100.1% 🔺)2.342s101.00x
▲ VercelNext.js (Turbopack)8.118s (+129.6% 🔺)10.545s (+103.1% 🔺)2.427s91.11x

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

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.618s (-24.5% 🟢)1.006s (-1.1%)0.388s1201.00x
🐘 PostgresNitro0.649s (-17.9% 🟢)1.006s (~)0.357s1201.05x
🌐 RedisNext.js (Turbopack)0.770s1.004s0.233s1201.25x
🐘 PostgresNext.js (Turbopack)0.933s1.219s0.286s991.51x
🌐 MongoDBNext.js (Turbopack)5.361s6.012s0.651s208.67x
💻 LocalExpress10.227s (-8.6% 🟢)10.863s (-9.0% 🟢)0.636s1216.54x
💻 LocalNitro10.251s (-8.4% 🟢)10.781s (-7.6% 🟢)0.530s1216.58x
💻 LocalNext.js (Turbopack)10.950s11.757s0.807s1117.71x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express20.578s (+177.3% 🔺)23.078s (+149.6% 🔺)2.500s61.00x
▲ VercelNext.js (Turbopack)21.742s (+110.5% 🔺)24.590s (+100.1% 🔺)2.848s51.06x
▲ VercelNitro25.208s (+226.4% 🔺)27.039s (+187.6% 🔺)1.831s51.22x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.129s (+450.3% 🔺)1.997s (+99.9% 🔺)0.001s (-18.8% 🟢)2.009s (+98.7% 🔺)0.881s101.00x
💻 LocalExpress1.131s (+468.1% 🔺)2.005s (+99.6% 🔺)0.012s (+2.5%)2.019s (+98.4% 🔺)0.888s101.00x
💻 LocalNitro1.132s (+429.6% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.021s (+98.3% 🔺)0.889s101.00x
🐘 PostgresNitro1.149s (+460.7% 🔺)1.994s (+99.5% 🔺)0.002s (+6.7% 🔺)2.012s (+99.0% 🔺)0.863s101.02x
💻 LocalNext.js (Turbopack)1.178s2.003s0.012s2.019s0.840s101.04x
🐘 PostgresNext.js (Turbopack)1.192s2.002s0.001s2.010s0.818s101.06x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.184s (-43.0% 🟢)3.612s (-31.5% 🟢)1.614s (+117.5% 🔺)5.719s (-11.8% 🟢)3.535s101.00x
▲ VercelExpress2.198s (-12.3% 🟢)3.538s (-13.5% 🟢)1.751s (+82.3% 🔺)5.836s (+4.4%)3.637s101.01x
▲ VercelNext.js (Turbopack)4.942s (-27.9% 🟢)5.438s (-37.1% 🟢)1.145s (+81.2% 🔺)8.364s (-14.6% 🟢)3.421s102.26x

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

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.496s (+137.4% 🔺)2.000s (+98.7% 🔺)0.003s (-9.5% 🟢)2.026s (+98.1% 🔺)0.531s301.00x
🐘 PostgresNitro1.507s (+141.5% 🔺)2.002s (+98.8% 🔺)0.004s (-8.9% 🟢)2.024s (+97.9% 🔺)0.517s301.01x
💻 LocalNitro1.538s (+83.4% 🔺)2.012s (+98.8% 🔺)0.010s (+11.7% 🔺)2.025s (+81.4% 🔺)0.486s301.03x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.004s2.025s0.367s301.11x
💻 LocalExpress1.716s (+126.6% 🔺)2.012s (+95.5% 🔺)0.009s (-2.1%)2.202s (+111.8% 🔺)0.486s281.15x
💻 LocalNext.js (Turbopack)2.055s2.009s0.010s2.423s0.368s251.37x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.789s (-80.3% 🟢)7.537s (-75.5% 🟢)0.268s (+139.0% 🔺)8.404s (-73.6% 🟢)2.615s81.00x
▲ VercelExpress6.045s (-7.1% 🟢)7.653s (-4.5%)0.313s (-23.5% 🟢)8.503s (-3.8%)2.457s81.04x
▲ VercelNext.js (Turbopack)12.326s (-27.1% 🟢)13.810s (-24.3% 🟢)0.429s (+103.2% 🔺)15.364s (-18.9% 🟢)3.038s42.13x

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

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.660s (-31.8% 🟢)1.031s (-17.4% 🟢)0.000s (+24.1% 🔺)1.048s (-16.7% 🟢)0.387s581.00x
🐘 PostgresExpress0.688s (-28.4% 🟢)1.061s (-17.0% 🟢)0.000s (+105.4% 🔺)1.079s (-17.4% 🟢)0.391s561.04x
🐘 PostgresNext.js (Turbopack)0.783s1.090s0.000s1.097s0.314s561.19x
💻 LocalNitro1.345s (+10.0% 🔺)2.016s (~)0.000s (+333.3% 🔺)2.018s (~)0.674s302.04x
💻 LocalNext.js (Turbopack)1.400s2.014s0.000s2.017s0.617s302.12x
💻 LocalExpress1.515s (+23.7% 🔺)2.015s (~)0.000s (-35.7% 🟢)2.196s (+8.6% 🔺)0.681s282.29x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.737s (+22.5% 🔺)5.067s (+15.4% 🔺)0.000s (+18.2% 🔺)5.709s (+18.7% 🔺)1.972s111.00x
▲ VercelExpress4.218s (+12.8% 🔺)5.770s (+13.1% 🔺)0.000s (+50.0% 🔺)6.218s (+12.4% 🔺)2.000s111.13x
▲ VercelNext.js (Turbopack)6.160s (-39.5% 🟢)6.979s (-39.4% 🟢)0.000s (NaN%)8.458s (-29.8% 🟢)2.298s81.65x

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

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.286s (-28.2% 🟢)2.001s (-6.6% 🟢)0.000s (-100.0% 🟢)2.020s (-7.1% 🟢)0.734s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.067s (-5.1% 🟢)0.000s (+Infinity% 🔺)2.115s (-3.8%)0.730s291.08x
🐘 PostgresNext.js (Turbopack)1.629s2.225s0.000s2.239s0.610s271.27x
💻 LocalNext.js (Turbopack)2.539s3.125s0.000s3.128s0.589s201.97x
💻 LocalExpress3.054s (-11.9% 🟢)3.903s (-3.2%)0.000s (-45.3% 🟢)3.905s (-3.2%)0.852s162.37x
💻 LocalNitro3.180s (-6.1% 🟢)3.966s (-1.6%)0.000s (-41.4% 🟢)3.971s (-1.6%)0.791s162.47x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.004s (+30.9% 🔺)7.517s (+24.8% 🔺)0.000s (+Infinity% 🔺)8.103s (+25.5% 🔺)2.099s81.00x
▲ VercelNitro6.750s (+64.9% 🔺)8.558s (+59.2% 🔺)0.000s (-100.0% 🟢)9.120s (+57.4% 🔺)2.370s71.12x
▲ VercelNext.js (Turbopack)8.561s (+52.4% 🔺)9.916s (+42.0% 🔺)0.000s (-100.0% 🟢)11.240s (+49.1% 🔺)2.679s61.43x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro11/21
🐘 PostgresExpress17/21
▲ VercelNitro11/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres19/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres17/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

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production85902191078
✅ 💻 Local Development95702191176
✅ 📦 Local Production95702191176
✅ 🐘 Local Postgres95702191176
✅ 🪟 Windows980098
✅ 📋 Other5100176686
Total4338010525390

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro72026
✅ example72026
✅ express72026
✅ fastify72026
✅ hono72026
✅ nextjs-turbopack9602
✅ nextjs-webpack9602
✅ nitro72026
✅ nuxt72026
✅ sveltekit9107
✅ vite72026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9800
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable73025
✅ e2e-local-dev-tanstack-start-stable73025
✅ e2e-local-postgres-nest-stable73025
✅ e2e-local-postgres-tanstack-start-stable73025
✅ e2e-local-prod-nest-stable73025
✅ e2e-local-prod-tanstack-start-stable73025
✅ e2e-vercel-prod-tanstack-start72026

📋 View full workflow run

Move the inline ANSI shim out of `ansi.ts` into its own
`./internal-chalk.ts` so tests can swap it for a tag-emitting mock via
`vi.mock('./internal-chalk.js', …)`. Snapshots for `Ansi.code` /
`Ansi.hint` / `Ansi.note` / `Ansi.help` / `Ansi.docs` go back to the
readable HTML-like form (`<blue><b>hint:</b> try reloading</blue>`),
making it obvious from the test file which fragments are colored and
how — the same review affordance the previous `__mocks__/chalk.ts`
mock provided before the chalk dep was dropped.
`Ansi.frame` and `Ansi.inline` remain plain in snapshots because they
don't apply any styling themselves; that matches the pre-fix behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. The diagnosis is correct, the fix is appropriately surgical, and the shim is well-written.

Verified

Reachability claim — confirmed: packages/core/src/workflow/index.tscontext-errors.tscontext-violation-error.tsimport * as Ansi from '@workflow/errors/ansi'. So any consumer that uses workflow-context-checked APIs (createHook, createWebhook, getWorkflowMetadata, etc.) ends up dragging the entire ansi/chalk transitive graph into the workflow VM bundle.

Output parity — for the only nested call in ansi.ts (code(): chalk.italic(\${chalk.dim('`')}${str}${chalk.dim('`')}`)), the shim produces byte-for-byte the same SGR sequence as chalk@5.6.2`:

\x1b[3m\x1b[2m`\x1b[22mfoo\x1b[2m`\x1b[22m\x1b[23m

The 22 (normal intensity) close after each dim correctly leaves italic (3) on, since 22 only resets bold/dim — same as chalk's behavior.

Bundle effect — built workbench/example from this branch and grepped the workflow VM bundle (the string passed to workflowEntrypoint):

$ grep -cE "supports-color|require\(['\"](node:)?os['\"]|chalk|internal_chalk" /tmp/vm-bundle.txt
0

No chalk, no supports-color, no os anywhere. (The example workbench's VM bundle didn't actually pull in context-violation-error due to tree-shaking in this particular workflow set, so the chalk references were already absent on main for this specific bundle — but the change is still correct, and downstream consumers like vade that exercise the workflow-context paths will get the fix.)

Testspnpm --filter @workflow/errors test passes (4 files, 26 tests including all 11 ansi.test.ts snapshots), pnpm --filter @workflow/core test passes (820 tests).

CI failures are pre-existing flakes

The two red checks (Unit Tests (ubuntu-latest), E2E Required Check) are both from the same flaky world-postgres test:

FAIL test/spec.test.ts > sequential steps with stream complete in a single flow invocation
AssertionError: expected +0 to be 1 // Object.is equality

This same test fails on origin/main HEAD (run 25343680860 job 74307194029) and is unrelated to this change.

Minor nits (non-blocking)

  • colorEnabled is evaluated once at module-load time, whereas chalk re-evaluates lazily on each call. If a host process programmatically flips FORCE_COLOR/NO_COLOR after @workflow/errors is imported, the shim won't pick up the change. In practice no one in this codebase does that, and the JSDoc on internal-chalk.ts is implicit about it — but if you wanted to be belt-and-braces, wrapping the env-var check inside sgr() would keep parity. Totally fine to leave as-is.

  • The __mocks__/chalk.ts deletion is correct, but worth noting that the module-graph entrypoint for the test mock changed from "the chalk package as resolved by node" to "this sibling file at a literal relative path". That's fine for this codebase but means the mock now relies on vi.mock('./internal-chalk.js', ...)paths rather than package identity — slightly more fragile if ansi.ts is ever moved to a different directory. Fine for now.

Approving.

@VaguelySerious
VaguelySerious merged commit 540a2ef into mainMay 5, 2026
175 of 177 checks passed
@VaguelySerious
VaguelySerious deleted the peter/errors-no-chalk branch May 5, 2026 00:47
@github-actionsgithub-actionsBot mentioned this pull request May 5, 2026
pranaygp added a commit that referenced this pull request May 5, 2026
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
pranaygp added a commit that referenced this pull request May 5, 2026
…ignal
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
Allow disabling step sourcemap with new `sourcemap` option in builders (#1842)
[ci] Enable Vercel-prod e2e for tanstack-start (#1904)
web: configure vercelPreset() for Vercel deployments (#1815)
[core] Combine flow+step bundle and process steps eagerly (#1338)
[world-vercel] Revert stream close control framing (#1891)
[tarballs] Use turbo to build workspace deps before packing (#1908)
# Conflicts:
#	packages/core/src/runtime/step-handler.test.ts
#	packages/core/src/runtime/step-handler.ts
#	packages/core/src/runtime/suspension-handler.ts
#	packages/core/src/step.test.ts
#	packages/world-local/src/storage/events-storage.ts
#	packages/world-postgres/src/drizzle/migrations/meta/_journal.json
Sign up for freeto 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.

2 participants

@VaguelySerious@TooTallNate
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

[errors] Replace chalk import with inline ANSI shim - #1915

Merged
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk
May 5, 2026
Merged

[errors] Replace chalk import with inline ANSI shim#1915
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

@workflow/errors/ansi imports chalk, which transitively require('os')s via supports-color. That module is reachable from the workflow-VM bundle through:

workflow → @workflow/core/workflow/index.ts
→ context-errors.ts
→ context-violation-error.ts
→ @workflow/errors/ansi ← imports chalk

The workflow VM has no require(), so importing chalk crashes every workflow at registration time:

./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)

Fix

Replace chalk with a 25-line inline SGR helper that exposes the same call surface (chalk.red, chalk.bold, …). Color detection mirrors chalk's defaults at a coarse level — FORCE_COLOR on, NO_COLOR off, otherwise gated on process.stdout.isTTY — so non-TTY logs and the workflow VM (no process) get plain text, matching the prior behavior in tests and in production log drains.

Drops the chalk runtime dependency from @workflow/errors and removes the now-unused __mocks__/chalk.ts Vitest mock. All 820 @workflow/core unit tests + 11 @workflow/errors snapshot tests pass.

Test plan

  • pnpm --filter @workflow/errors test (26 tests pass)
  • pnpm --filter @workflow/core test (820 unit tests pass; e2e excluded)
  • CI green
  • Reproduce: install latest tarballs in a v2-flow consumer (vade) and confirm step registration no longer throws ReferenceError: require is not defined

🤖 Generated with Claude Code

`@workflow/errors/ansi` is reachable from the workflow-VM bundle via
`@workflow/core/workflow` → `context-errors` → `context-violation-error`
→ here. The real `chalk` package pulls in `supports-color`, which calls
`require('os')` at module load — crashing every workflow with
`ReferenceError: require is not defined` in the sandboxed VM the moment
any user step is registered:
./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)
Replace `chalk` with a 25-line inline SGR helper exposing the same call
surface (`chalk.red`, `chalk.bold`, …). Color detection mirrors chalk's
default at a coarse level — `FORCE_COLOR` on, `NO_COLOR` off, otherwise
gated on `process.stdout.isTTY` — so non-TTY logs and the workflow VM
(no `process`) get plain text, matching previous behavior in tests and
in production log drains.
Drops the `chalk` runtime dependency from `@workflow/errors` and removes
the now-unused `__mocks__/chalk.ts` Vitest mock.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 47be549

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

This PR includes changesets to release 22 packages
NameType
@workflow/errorsPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/webPatch
workflowPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-vercelPatch
tarballsPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/world-testingPatch
@workflow/web-sharedPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

@github-actions

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.033s (-24.6% 🟢)1.005s (~)0.973s101.00x
💻 LocalExpress0.035s (-20.1% 🟢)1.006s (~)0.970s101.09x
🐘 PostgresExpress0.046s (-19.8% 🟢)1.011s (~)0.964s101.43x
💻 LocalNext.js (Turbopack)0.049s1.005s0.956s101.50x
🐘 PostgresNitro0.052s (-45.5% 🟢)1.011s (-3.0%)0.959s101.60x
🌐 RedisNext.js (Turbopack)0.054s1.005s0.951s101.65x
🐘 PostgresNext.js (Turbopack)0.057s1.011s0.953s101.76x
🌐 MongoDBNext.js (Turbopack)0.091s1.007s0.916s102.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.295s (+25.2% 🔺)2.353s (+10.2% 🔺)2.058s101.00x
▲ VercelNitro0.366s (-10.6% 🟢)2.033s (-19.0% 🟢)1.667s101.24x
▲ VercelNext.js (Turbopack)0.558s (+122.1% 🔺)2.742s (+17.5% 🔺)2.183s101.90x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.070s (-5.4% 🟢)2.007s (~)0.937s101.00x
💻 LocalExpress1.072s (-4.7%)2.007s (~)0.935s101.00x
🐘 PostgresExpress1.082s (-5.6% 🟢)2.009s (~)0.927s101.01x
🐘 PostgresNitro1.084s (-4.9%)2.009s (~)0.925s101.01x
💻 LocalNext.js (Turbopack)1.110s2.006s0.896s101.04x
🌐 RedisNext.js (Turbopack)1.111s2.008s0.897s101.04x
🐘 PostgresNext.js (Turbopack)1.114s2.010s0.897s101.04x
🌐 MongoDBNext.js (Turbopack)1.148s2.009s0.861s101.07x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.586s (-15.4% 🟢)3.902s (+2.5%)2.317s101.00x
▲ VercelNitro1.641s (-57.8% 🟢)4.080s (-30.9% 🟢)2.439s101.04x
▲ VercelNext.js (Turbopack)2.861s (+40.6% 🔺)4.792s (+25.1% 🔺)1.931s101.80x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express10.400s (-5.1% 🟢)11.014s (~)0.614s31.00x
🐘 PostgresNitro10.402s (-4.3%)11.015s (~)0.613s31.00x
💻 LocalExpress10.409s (-4.7%)11.022s (~)0.613s31.00x
💻 LocalNitro10.422s (-4.8%)11.023s (~)0.601s31.00x
🌐 MongoDBNext.js (Turbopack)10.655s11.023s0.368s31.02x
🐘 PostgresNext.js (Turbopack)10.667s11.015s0.348s31.03x
🌐 RedisNext.js (Turbopack)10.669s11.023s0.354s31.03x
💻 LocalNext.js (Turbopack)10.682s11.020s0.338s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.559s (-42.9% 🟢)15.361s (-38.8% 🟢)1.802s21.00x
▲ VercelExpress13.917s (-18.0% 🟢)15.853s (-20.8% 🟢)1.936s21.03x
▲ VercelNext.js (Turbopack)14.322s (-17.3% 🟢)16.622s (-14.3% 🟢)2.299s21.06x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.471s (-7.6% 🟢)14.017s (-6.7% 🟢)0.546s51.00x
💻 LocalExpress13.477s (-10.0% 🟢)14.027s (-6.7% 🟢)0.550s51.00x
🐘 PostgresNitro13.481s (-7.6% 🟢)14.021s (-6.7% 🟢)0.540s51.00x
💻 LocalNitro13.483s (-10.5% 🟢)14.027s (-12.5% 🟢)0.544s51.00x
🌐 MongoDBNext.js (Turbopack)13.835s14.025s0.190s51.03x
🌐 RedisNext.js (Turbopack)14.050s15.030s0.980s41.04x
💻 LocalNext.js (Turbopack)14.060s14.628s0.569s51.04x
🐘 PostgresNext.js (Turbopack)14.069s15.020s0.951s41.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.544s (-66.6% 🟢)23.739s (-64.4% 🟢)2.195s31.00x
▲ VercelExpress22.795s (-54.7% 🟢)25.107s (-52.2% 🟢)2.312s31.06x
▲ VercelNext.js (Turbopack)23.551s (-55.2% 🟢)26.276s (-51.9% 🟢)2.725s31.09x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express11.836s (-15.5% 🟢)12.015s (-17.7% 🟢)0.179s81.00x
💻 LocalNitro11.946s (-28.8% 🟢)12.272s (-27.9% 🟢)0.326s81.01x
💻 LocalExpress11.949s (-28.0% 🟢)12.146s (-28.7% 🟢)0.197s81.01x
🐘 PostgresNitro11.970s (-14.3% 🟢)12.394s (-13.4% 🟢)0.424s81.01x
🌐 MongoDBNext.js (Turbopack)12.590s13.024s0.433s71.06x
💻 LocalNext.js (Turbopack)12.972s13.452s0.480s71.10x
🌐 RedisNext.js (Turbopack)13.100s13.884s0.784s71.11x
🐘 PostgresNext.js (Turbopack)13.185s14.020s0.835s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express34.548s (-71.5% 🟢)36.366s (-70.6% 🟢)1.818s31.00x
▲ VercelNitro35.156s (-91.7% 🟢)37.155s (-91.2% 🟢)1.999s31.02x
▲ VercelNext.js (Turbopack)35.694s (-90.9% 🟢)37.608s (-90.5% 🟢)1.913s31.03x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.147s (-9.0% 🟢)2.007s (~)0.860s151.00x
🐘 PostgresNitro1.156s (-9.3% 🟢)2.007s (~)0.851s151.01x
💻 LocalNitro1.188s (-27.2% 🟢)2.006s (-3.3%)0.818s151.04x
💻 LocalExpress1.197s (-19.6% 🟢)2.006s (~)0.808s151.04x
🐘 PostgresNext.js (Turbopack)1.213s2.008s0.794s151.06x
🌐 RedisNext.js (Turbopack)1.268s2.006s0.738s151.10x
💻 LocalNext.js (Turbopack)1.378s2.073s0.695s151.20x
🌐 MongoDBNext.js (Turbopack)2.011s2.591s0.580s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.918s (+3.6%)4.656s (+7.7% 🔺)1.738s71.00x
▲ VercelNext.js (Turbopack)4.329s (+27.4% 🔺)6.218s (+26.1% 🔺)1.889s51.48x
▲ VercelExpress4.519s (+58.0% 🔺)6.298s (+36.2% 🔺)1.779s51.55x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.226s (-48.1% 🟢)2.007s (-33.3% 🟢)0.780s151.00x
🐘 PostgresNitro1.234s (-47.5% 🟢)2.007s (-33.3% 🟢)0.773s151.01x
🐘 PostgresNext.js (Turbopack)1.356s2.007s0.651s151.11x
💻 LocalNitro1.791s (-43.0% 🟢)2.007s (-48.4% 🟢)0.216s151.46x
💻 LocalExpress1.792s (-39.3% 🟢)2.006s (-41.9% 🟢)0.214s151.46x
💻 LocalNext.js (Turbopack)1.806s2.073s0.267s151.47x
🌐 RedisNext.js (Turbopack)2.385s3.008s0.623s101.94x
🌐 MongoDBNext.js (Turbopack)3.564s4.008s0.444s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.458s (+23.2% 🔺)6.268s (+22.7% 🔺)1.811s51.00x
▲ VercelNext.js (Turbopack)5.613s (-20.9% 🟢)7.797s (-12.4% 🟢)2.184s41.26x
▲ VercelNitro6.080s (+50.1% 🔺)8.092s (+36.7% 🔺)2.012s41.36x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.364s (-60.9% 🟢)2.006s (-50.0% 🟢)0.642s151.00x
🐘 PostgresNitro1.374s (-60.5% 🟢)2.007s (-49.9% 🟢)0.633s151.01x
🐘 PostgresNext.js (Turbopack)1.627s2.007s0.380s151.19x
🌐 RedisNext.js (Turbopack)3.621s4.009s0.389s82.65x
💻 LocalNext.js (Turbopack)5.000s5.511s0.511s63.67x
💻 LocalNitro5.331s (-36.2% 🟢)5.847s (-35.2% 🟢)0.517s63.91x
💻 LocalExpress5.417s (-35.0% 🟢)5.679s (-37.1% 🟢)0.263s63.97x
🌐 MongoDBNext.js (Turbopack)6.223s7.012s0.788s54.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.257s (+47.6% 🔺)8.386s (+36.9% 🔺)2.129s41.00x
▲ VercelNext.js (Turbopack)7.288s (-18.3% 🟢)9.279s (-15.3% 🟢)1.991s41.16x
▲ VercelNitro8.700s (+146.8% 🔺)10.899s (+96.9% 🔺)2.199s31.39x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.153s (-8.3% 🟢)2.009s (~)0.857s151.00x
🐘 PostgresExpress1.154s (-8.2% 🟢)2.009s (~)0.855s151.00x
🐘 PostgresNext.js (Turbopack)1.210s2.007s0.797s151.05x
🌐 RedisNext.js (Turbopack)1.257s2.006s0.749s151.09x
💻 LocalNext.js (Turbopack)1.353s2.006s0.653s151.17x
💻 LocalExpress1.384s (-26.9% 🟢)2.006s (-15.1% 🟢)0.622s151.20x
💻 LocalNitro1.397s (-25.1% 🟢)2.007s (-14.3% 🟢)0.610s151.21x
🌐 MongoDBNext.js (Turbopack)2.013s2.590s0.577s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.363s (-8.5% 🟢)4.099s (-5.8% 🟢)1.736s81.00x
▲ VercelNitro3.169s (+28.9% 🔺)5.005s (+20.0% 🔺)1.836s71.34x
▲ VercelNext.js (Turbopack)3.909s (+33.3% 🔺)6.008s (+29.4% 🔺)2.100s51.65x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.231s (-47.4% 🟢)2.009s (-33.3% 🟢)0.778s151.00x
🐘 PostgresExpress1.232s (-47.4% 🟢)2.007s (-33.3% 🟢)0.776s151.00x
🐘 PostgresNext.js (Turbopack)1.354s2.008s0.654s151.10x
💻 LocalNitro2.049s (-33.1% 🟢)2.509s (-35.4% 🟢)0.460s121.67x
💻 LocalNext.js (Turbopack)2.074s2.735s0.661s111.69x
💻 LocalExpress2.135s (-31.8% 🟢)2.593s (-31.1% 🟢)0.457s121.74x
🌐 RedisNext.js (Turbopack)2.370s3.008s0.639s101.93x
🌐 MongoDBNext.js (Turbopack)3.577s4.009s0.432s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.907s (+20.8% 🔺)5.774s (+13.7% 🔺)1.867s61.00x
▲ VercelExpress4.205s (+31.7% 🔺)6.052s (+26.3% 🔺)1.847s51.08x
▲ VercelNext.js (Turbopack)4.751s (+51.2% 🔺)6.510s (+44.0% 🔺)1.759s51.22x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.358s (-61.2% 🟢)2.008s (-49.9% 🟢)0.650s151.00x
🐘 PostgresNitro1.366s (-60.7% 🟢)2.008s (-49.9% 🟢)0.642s151.01x
🐘 PostgresNext.js (Turbopack)1.651s2.009s0.358s151.22x
🌐 RedisNext.js (Turbopack)3.648s4.010s0.362s82.69x
💻 LocalExpress5.565s (-36.8% 🟢)6.015s (-35.1% 🟢)0.450s54.10x
💻 LocalNext.js (Turbopack)5.704s6.214s0.510s54.20x
💻 LocalNitro5.946s (-35.0% 🟢)6.348s (-36.7% 🟢)0.402s64.38x
🌐 MongoDBNext.js (Turbopack)6.250s7.013s0.763s54.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.891s (+15.7% 🔺)7.641s (+12.1% 🔺)1.750s41.00x
▲ VercelExpress6.208s (-3.2%)7.682s (-6.1% 🟢)1.474s41.05x
▲ VercelNext.js (Turbopack)6.830s (+1.1%)8.929s (+4.5%)2.099s41.16x

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

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.451s (-46.2% 🟢)1.006s (-1.7%)0.555s601.00x
🐘 PostgresNitro0.453s (-44.7% 🟢)1.006s (~)0.553s601.01x
💻 LocalNitro0.471s (-52.0% 🟢)1.004s (-8.2% 🟢)0.533s601.04x
💻 LocalExpress0.506s (-48.6% 🟢)1.022s (-5.1% 🟢)0.515s591.12x
🌐 MongoDBNext.js (Turbopack)0.597s1.006s0.409s601.32x
🌐 RedisNext.js (Turbopack)0.639s1.004s0.365s601.42x
🐘 PostgresNext.js (Turbopack)0.662s1.006s0.344s601.47x
💻 LocalNext.js (Turbopack)0.711s1.004s0.293s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.747s (-78.5% 🟢)6.670s (-72.2% 🟢)1.923s101.00x
▲ VercelExpress4.916s (-74.1% 🟢)6.991s (-67.2% 🟢)2.075s91.04x
▲ VercelNext.js (Turbopack)6.804s (-53.1% 🟢)8.802s (-45.3% 🟢)1.998s71.43x

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

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.032s (-47.8% 🟢)1.458s (-35.4% 🟢)0.426s621.00x
🐘 PostgresNitro1.047s (-45.7% 🟢)1.507s (-28.3% 🟢)0.459s601.01x
💻 LocalExpress1.184s (-60.7% 🟢)2.006s (-44.1% 🟢)0.821s451.15x
💻 LocalNitro1.191s (-60.8% 🟢)2.006s (-46.6% 🟢)0.815s451.15x
🌐 MongoDBNext.js (Turbopack)1.503s2.007s0.504s451.46x
🐘 PostgresNext.js (Turbopack)1.587s2.007s0.419s451.54x
🌐 RedisNext.js (Turbopack)1.611s2.028s0.417s451.56x
💻 LocalNext.js (Turbopack)1.741s2.005s0.265s451.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.285s (-64.4% 🟢)14.394s (-60.9% 🟢)2.108s71.00x
▲ VercelNitro12.810s (-67.6% 🟢)15.636s (-62.1% 🟢)2.825s61.04x
▲ VercelNext.js (Turbopack)15.611s (-68.7% 🟢)18.106s (-65.0% 🟢)2.494s61.27x

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

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.878s (-52.9% 🟢)2.076s (-52.5% 🟢)0.198s581.00x
🐘 PostgresNitro2.079s (-49.3% 🟢)2.550s (-44.6% 🟢)0.470s481.11x
💻 LocalNitro2.680s (-71.2% 🟢)3.008s (-70.0% 🟢)0.327s401.43x
💻 LocalExpress2.692s (-70.8% 🟢)3.008s (-70.0% 🟢)0.316s401.43x
🐘 PostgresNext.js (Turbopack)3.092s4.009s0.917s301.65x
🌐 RedisNext.js (Turbopack)3.096s3.880s0.783s311.65x
🌐 MongoDBNext.js (Turbopack)3.670s4.013s0.343s301.95x
💻 LocalNext.js (Turbopack)3.704s4.008s0.303s301.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro39.197s (-59.6% 🟢)42.019s (-57.3% 🟢)2.822s31.00x
▲ VercelExpress40.332s (-69.0% 🟢)42.693s (-67.7% 🟢)2.361s31.03x
▲ VercelNext.js (Turbopack)44.774s (-58.2% 🟢)48.551s (-55.4% 🟢)3.777s31.14x

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

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.176s (-37.5% 🟢)1.006s (~)0.829s601.00x
🐘 PostgresNitro0.182s (-35.8% 🟢)1.005s (~)0.824s601.03x
🐘 PostgresNext.js (Turbopack)0.232s1.006s0.774s601.32x
🌐 RedisNext.js (Turbopack)0.242s1.004s0.762s601.37x
💻 LocalExpress0.439s (-21.6% 🟢)1.004s (~)0.565s602.49x
💻 LocalNitro0.474s (-21.6% 🟢)1.021s (~)0.547s592.69x
💻 LocalNext.js (Turbopack)0.539s1.004s0.465s603.06x
🌐 MongoDBNext.js (Turbopack)1.032s1.825s0.793s335.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.231s (+34.3% 🔺)4.189s (+25.0% 🔺)1.958s151.00x
▲ VercelExpress2.259s (+15.6% 🔺)4.431s (+21.8% 🔺)2.172s141.01x
▲ VercelNext.js (Turbopack)4.335s (+114.4% 🔺)6.320s (+66.6% 🔺)1.984s101.94x

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

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.309s (-39.4% 🟢)1.006s (~)0.697s901.00x
🐘 PostgresNitro0.312s (-37.2% 🟢)1.006s (~)0.694s901.01x
🌐 RedisNext.js (Turbopack)0.412s1.004s0.592s901.33x
🐘 PostgresNext.js (Turbopack)0.450s1.006s0.555s901.46x
💻 LocalNitro2.179s (-14.2% 🟢)3.008s (~)0.829s307.05x
💻 LocalExpress2.268s (-9.8% 🟢)2.911s (-3.3%)0.644s317.34x
💻 LocalNext.js (Turbopack)2.382s2.977s0.595s317.71x
🌐 MongoDBNext.js (Turbopack)2.594s3.008s0.414s308.39x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express7.284s (+139.1% 🔺)9.227s (+91.9% 🔺)1.943s101.00x
▲ VercelNitro7.305s (+126.5% 🔺)9.647s (+100.1% 🔺)2.342s101.00x
▲ VercelNext.js (Turbopack)8.118s (+129.6% 🔺)10.545s (+103.1% 🔺)2.427s91.11x

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

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.618s (-24.5% 🟢)1.006s (-1.1%)0.388s1201.00x
🐘 PostgresNitro0.649s (-17.9% 🟢)1.006s (~)0.357s1201.05x
🌐 RedisNext.js (Turbopack)0.770s1.004s0.233s1201.25x
🐘 PostgresNext.js (Turbopack)0.933s1.219s0.286s991.51x
🌐 MongoDBNext.js (Turbopack)5.361s6.012s0.651s208.67x
💻 LocalExpress10.227s (-8.6% 🟢)10.863s (-9.0% 🟢)0.636s1216.54x
💻 LocalNitro10.251s (-8.4% 🟢)10.781s (-7.6% 🟢)0.530s1216.58x
💻 LocalNext.js (Turbopack)10.950s11.757s0.807s1117.71x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express20.578s (+177.3% 🔺)23.078s (+149.6% 🔺)2.500s61.00x
▲ VercelNext.js (Turbopack)21.742s (+110.5% 🔺)24.590s (+100.1% 🔺)2.848s51.06x
▲ VercelNitro25.208s (+226.4% 🔺)27.039s (+187.6% 🔺)1.831s51.22x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.129s (+450.3% 🔺)1.997s (+99.9% 🔺)0.001s (-18.8% 🟢)2.009s (+98.7% 🔺)0.881s101.00x
💻 LocalExpress1.131s (+468.1% 🔺)2.005s (+99.6% 🔺)0.012s (+2.5%)2.019s (+98.4% 🔺)0.888s101.00x
💻 LocalNitro1.132s (+429.6% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.021s (+98.3% 🔺)0.889s101.00x
🐘 PostgresNitro1.149s (+460.7% 🔺)1.994s (+99.5% 🔺)0.002s (+6.7% 🔺)2.012s (+99.0% 🔺)0.863s101.02x
💻 LocalNext.js (Turbopack)1.178s2.003s0.012s2.019s0.840s101.04x
🐘 PostgresNext.js (Turbopack)1.192s2.002s0.001s2.010s0.818s101.06x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.184s (-43.0% 🟢)3.612s (-31.5% 🟢)1.614s (+117.5% 🔺)5.719s (-11.8% 🟢)3.535s101.00x
▲ VercelExpress2.198s (-12.3% 🟢)3.538s (-13.5% 🟢)1.751s (+82.3% 🔺)5.836s (+4.4%)3.637s101.01x
▲ VercelNext.js (Turbopack)4.942s (-27.9% 🟢)5.438s (-37.1% 🟢)1.145s (+81.2% 🔺)8.364s (-14.6% 🟢)3.421s102.26x

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

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.496s (+137.4% 🔺)2.000s (+98.7% 🔺)0.003s (-9.5% 🟢)2.026s (+98.1% 🔺)0.531s301.00x
🐘 PostgresNitro1.507s (+141.5% 🔺)2.002s (+98.8% 🔺)0.004s (-8.9% 🟢)2.024s (+97.9% 🔺)0.517s301.01x
💻 LocalNitro1.538s (+83.4% 🔺)2.012s (+98.8% 🔺)0.010s (+11.7% 🔺)2.025s (+81.4% 🔺)0.486s301.03x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.004s2.025s0.367s301.11x
💻 LocalExpress1.716s (+126.6% 🔺)2.012s (+95.5% 🔺)0.009s (-2.1%)2.202s (+111.8% 🔺)0.486s281.15x
💻 LocalNext.js (Turbopack)2.055s2.009s0.010s2.423s0.368s251.37x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.789s (-80.3% 🟢)7.537s (-75.5% 🟢)0.268s (+139.0% 🔺)8.404s (-73.6% 🟢)2.615s81.00x
▲ VercelExpress6.045s (-7.1% 🟢)7.653s (-4.5%)0.313s (-23.5% 🟢)8.503s (-3.8%)2.457s81.04x
▲ VercelNext.js (Turbopack)12.326s (-27.1% 🟢)13.810s (-24.3% 🟢)0.429s (+103.2% 🔺)15.364s (-18.9% 🟢)3.038s42.13x

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

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.660s (-31.8% 🟢)1.031s (-17.4% 🟢)0.000s (+24.1% 🔺)1.048s (-16.7% 🟢)0.387s581.00x
🐘 PostgresExpress0.688s (-28.4% 🟢)1.061s (-17.0% 🟢)0.000s (+105.4% 🔺)1.079s (-17.4% 🟢)0.391s561.04x
🐘 PostgresNext.js (Turbopack)0.783s1.090s0.000s1.097s0.314s561.19x
💻 LocalNitro1.345s (+10.0% 🔺)2.016s (~)0.000s (+333.3% 🔺)2.018s (~)0.674s302.04x
💻 LocalNext.js (Turbopack)1.400s2.014s0.000s2.017s0.617s302.12x
💻 LocalExpress1.515s (+23.7% 🔺)2.015s (~)0.000s (-35.7% 🟢)2.196s (+8.6% 🔺)0.681s282.29x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.737s (+22.5% 🔺)5.067s (+15.4% 🔺)0.000s (+18.2% 🔺)5.709s (+18.7% 🔺)1.972s111.00x
▲ VercelExpress4.218s (+12.8% 🔺)5.770s (+13.1% 🔺)0.000s (+50.0% 🔺)6.218s (+12.4% 🔺)2.000s111.13x
▲ VercelNext.js (Turbopack)6.160s (-39.5% 🟢)6.979s (-39.4% 🟢)0.000s (NaN%)8.458s (-29.8% 🟢)2.298s81.65x

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

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.286s (-28.2% 🟢)2.001s (-6.6% 🟢)0.000s (-100.0% 🟢)2.020s (-7.1% 🟢)0.734s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.067s (-5.1% 🟢)0.000s (+Infinity% 🔺)2.115s (-3.8%)0.730s291.08x
🐘 PostgresNext.js (Turbopack)1.629s2.225s0.000s2.239s0.610s271.27x
💻 LocalNext.js (Turbopack)2.539s3.125s0.000s3.128s0.589s201.97x
💻 LocalExpress3.054s (-11.9% 🟢)3.903s (-3.2%)0.000s (-45.3% 🟢)3.905s (-3.2%)0.852s162.37x
💻 LocalNitro3.180s (-6.1% 🟢)3.966s (-1.6%)0.000s (-41.4% 🟢)3.971s (-1.6%)0.791s162.47x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.004s (+30.9% 🔺)7.517s (+24.8% 🔺)0.000s (+Infinity% 🔺)8.103s (+25.5% 🔺)2.099s81.00x
▲ VercelNitro6.750s (+64.9% 🔺)8.558s (+59.2% 🔺)0.000s (-100.0% 🟢)9.120s (+57.4% 🔺)2.370s71.12x
▲ VercelNext.js (Turbopack)8.561s (+52.4% 🔺)9.916s (+42.0% 🔺)0.000s (-100.0% 🟢)11.240s (+49.1% 🔺)2.679s61.43x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro11/21
🐘 PostgresExpress17/21
▲ VercelNitro11/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres19/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres17/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

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production85902191078
✅ 💻 Local Development95702191176
✅ 📦 Local Production95702191176
✅ 🐘 Local Postgres95702191176
✅ 🪟 Windows980098
✅ 📋 Other5100176686
Total4338010525390

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro72026
✅ example72026
✅ express72026
✅ fastify72026
✅ hono72026
✅ nextjs-turbopack9602
✅ nextjs-webpack9602
✅ nitro72026
✅ nuxt72026
✅ sveltekit9107
✅ vite72026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9800
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable73025
✅ e2e-local-dev-tanstack-start-stable73025
✅ e2e-local-postgres-nest-stable73025
✅ e2e-local-postgres-tanstack-start-stable73025
✅ e2e-local-prod-nest-stable73025
✅ e2e-local-prod-tanstack-start-stable73025
✅ e2e-vercel-prod-tanstack-start72026

📋 View full workflow run

Move the inline ANSI shim out of `ansi.ts` into its own
`./internal-chalk.ts` so tests can swap it for a tag-emitting mock via
`vi.mock('./internal-chalk.js', …)`. Snapshots for `Ansi.code` /
`Ansi.hint` / `Ansi.note` / `Ansi.help` / `Ansi.docs` go back to the
readable HTML-like form (`<blue><b>hint:</b> try reloading</blue>`),
making it obvious from the test file which fragments are colored and
how — the same review affordance the previous `__mocks__/chalk.ts`
mock provided before the chalk dep was dropped.
`Ansi.frame` and `Ansi.inline` remain plain in snapshots because they
don't apply any styling themselves; that matches the pre-fix behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. The diagnosis is correct, the fix is appropriately surgical, and the shim is well-written.

Verified

Reachability claim — confirmed: packages/core/src/workflow/index.tscontext-errors.tscontext-violation-error.tsimport * as Ansi from '@workflow/errors/ansi'. So any consumer that uses workflow-context-checked APIs (createHook, createWebhook, getWorkflowMetadata, etc.) ends up dragging the entire ansi/chalk transitive graph into the workflow VM bundle.

Output parity — for the only nested call in ansi.ts (code(): chalk.italic(\${chalk.dim('`')}${str}${chalk.dim('`')}`)), the shim produces byte-for-byte the same SGR sequence as chalk@5.6.2`:

\x1b[3m\x1b[2m`\x1b[22mfoo\x1b[2m`\x1b[22m\x1b[23m

The 22 (normal intensity) close after each dim correctly leaves italic (3) on, since 22 only resets bold/dim — same as chalk's behavior.

Bundle effect — built workbench/example from this branch and grepped the workflow VM bundle (the string passed to workflowEntrypoint):

$ grep -cE "supports-color|require\(['\"](node:)?os['\"]|chalk|internal_chalk" /tmp/vm-bundle.txt
0

No chalk, no supports-color, no os anywhere. (The example workbench's VM bundle didn't actually pull in context-violation-error due to tree-shaking in this particular workflow set, so the chalk references were already absent on main for this specific bundle — but the change is still correct, and downstream consumers like vade that exercise the workflow-context paths will get the fix.)

Testspnpm --filter @workflow/errors test passes (4 files, 26 tests including all 11 ansi.test.ts snapshots), pnpm --filter @workflow/core test passes (820 tests).

CI failures are pre-existing flakes

The two red checks (Unit Tests (ubuntu-latest), E2E Required Check) are both from the same flaky world-postgres test:

FAIL test/spec.test.ts > sequential steps with stream complete in a single flow invocation
AssertionError: expected +0 to be 1 // Object.is equality

This same test fails on origin/main HEAD (run 25343680860 job 74307194029) and is unrelated to this change.

Minor nits (non-blocking)

  • colorEnabled is evaluated once at module-load time, whereas chalk re-evaluates lazily on each call. If a host process programmatically flips FORCE_COLOR/NO_COLOR after @workflow/errors is imported, the shim won't pick up the change. In practice no one in this codebase does that, and the JSDoc on internal-chalk.ts is implicit about it — but if you wanted to be belt-and-braces, wrapping the env-var check inside sgr() would keep parity. Totally fine to leave as-is.

  • The __mocks__/chalk.ts deletion is correct, but worth noting that the module-graph entrypoint for the test mock changed from "the chalk package as resolved by node" to "this sibling file at a literal relative path". That's fine for this codebase but means the mock now relies on vi.mock('./internal-chalk.js', ...)paths rather than package identity — slightly more fragile if ansi.ts is ever moved to a different directory. Fine for now.

Approving.

@VaguelySerious
VaguelySerious merged commit 540a2ef into mainMay 5, 2026
175 of 177 checks passed
@VaguelySerious
VaguelySerious deleted the peter/errors-no-chalk branch May 5, 2026 00:47
@github-actionsgithub-actionsBot mentioned this pull request May 5, 2026
pranaygp added a commit that referenced this pull request May 5, 2026
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
pranaygp added a commit that referenced this pull request May 5, 2026
…ignal
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
Allow disabling step sourcemap with new `sourcemap` option in builders (#1842)
[ci] Enable Vercel-prod e2e for tanstack-start (#1904)
web: configure vercelPreset() for Vercel deployments (#1815)
[core] Combine flow+step bundle and process steps eagerly (#1338)
[world-vercel] Revert stream close control framing (#1891)
[tarballs] Use turbo to build workspace deps before packing (#1908)
# Conflicts:
#	packages/core/src/runtime/step-handler.test.ts
#	packages/core/src/runtime/step-handler.ts
#	packages/core/src/runtime/suspension-handler.ts
#	packages/core/src/step.test.ts
#	packages/world-local/src/storage/events-storage.ts
#	packages/world-postgres/src/drizzle/migrations/meta/_journal.json
Sign up for freeto 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.

2 participants

@VaguelySerious@TooTallNate
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

[errors] Replace chalk import with inline ANSI shim - #1915

Merged
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk
May 5, 2026
Merged

[errors] Replace chalk import with inline ANSI shim#1915
VaguelySerious merged 2 commits into
mainfrom
peter/errors-no-chalk

Conversation

@VaguelySerious

Copy link
Copy Markdown
Member

Summary

@workflow/errors/ansi imports chalk, which transitively require('os')s via supports-color. That module is reachable from the workflow-VM bundle through:

workflow → @workflow/core/workflow/index.ts
→ context-errors.ts
→ context-violation-error.ts
→ @workflow/errors/ansi ← imports chalk

The workflow VM has no require(), so importing chalk crashes every workflow at registration time:

./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)

Fix

Replace chalk with a 25-line inline SGR helper that exposes the same call surface (chalk.red, chalk.bold, …). Color detection mirrors chalk's defaults at a coarse level — FORCE_COLOR on, NO_COLOR off, otherwise gated on process.stdout.isTTY — so non-TTY logs and the workflow VM (no process) get plain text, matching the prior behavior in tests and in production log drains.

Drops the chalk runtime dependency from @workflow/errors and removes the now-unused __mocks__/chalk.ts Vitest mock. All 820 @workflow/core unit tests + 11 @workflow/errors snapshot tests pass.

Test plan

  • pnpm --filter @workflow/errors test (26 tests pass)
  • pnpm --filter @workflow/core test (820 unit tests pass; e2e excluded)
  • CI green
  • Reproduce: install latest tarballs in a v2-flow consumer (vade) and confirm step registration no longer throws ReferenceError: require is not defined

🤖 Generated with Claude Code

`@workflow/errors/ansi` is reachable from the workflow-VM bundle via
`@workflow/core/workflow` → `context-errors` → `context-violation-error`
→ here. The real `chalk` package pulls in `supports-color`, which calls
`require('os')` at module load — crashing every workflow with
`ReferenceError: require is not defined` in the sandboxed VM the moment
any user step is registered:
./src/workflows/v2/workflow:2131
var os = require("os");
^
ReferenceError: require is not defined
at .../node_modules/.pnpm/supports-color@7.2.0/.../index.js:2:11
at __require (./src/workflows/v2/workflow:11:50)
at .../node_modules/.pnpm/chalk@4.1.2/.../source/index.js:3:53
at __require (./src/workflows/v2/workflow:11:50)
at .../@workflow/errors/src/ansi.ts:1:18
at Script.runInContext (node:vm:149:12)
Replace `chalk` with a 25-line inline SGR helper exposing the same call
surface (`chalk.red`, `chalk.bold`, …). Color detection mirrors chalk's
default at a coarse level — `FORCE_COLOR` on, `NO_COLOR` off, otherwise
gated on `process.stdout.isTTY` — so non-TTY logs and the workflow VM
(no `process`) get plain text, matching previous behavior in tests and
in production log drains.
Drops the `chalk` runtime dependency from `@workflow/errors` and removes
the now-unused `__mocks__/chalk.ts` Vitest mock.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

@changeset-bot

changeset-botBot commented May 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 47be549

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

This PR includes changesets to release 22 packages
NameType
@workflow/errorsPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/corePatch
@workflow/webPatch
workflowPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-vercelPatch
tarballsPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/world-testingPatch
@workflow/web-sharedPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

@github-actions

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.033s (-24.6% 🟢)1.005s (~)0.973s101.00x
💻 LocalExpress0.035s (-20.1% 🟢)1.006s (~)0.970s101.09x
🐘 PostgresExpress0.046s (-19.8% 🟢)1.011s (~)0.964s101.43x
💻 LocalNext.js (Turbopack)0.049s1.005s0.956s101.50x
🐘 PostgresNitro0.052s (-45.5% 🟢)1.011s (-3.0%)0.959s101.60x
🌐 RedisNext.js (Turbopack)0.054s1.005s0.951s101.65x
🐘 PostgresNext.js (Turbopack)0.057s1.011s0.953s101.76x
🌐 MongoDBNext.js (Turbopack)0.091s1.007s0.916s102.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.295s (+25.2% 🔺)2.353s (+10.2% 🔺)2.058s101.00x
▲ VercelNitro0.366s (-10.6% 🟢)2.033s (-19.0% 🟢)1.667s101.24x
▲ VercelNext.js (Turbopack)0.558s (+122.1% 🔺)2.742s (+17.5% 🔺)2.183s101.90x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.070s (-5.4% 🟢)2.007s (~)0.937s101.00x
💻 LocalExpress1.072s (-4.7%)2.007s (~)0.935s101.00x
🐘 PostgresExpress1.082s (-5.6% 🟢)2.009s (~)0.927s101.01x
🐘 PostgresNitro1.084s (-4.9%)2.009s (~)0.925s101.01x
💻 LocalNext.js (Turbopack)1.110s2.006s0.896s101.04x
🌐 RedisNext.js (Turbopack)1.111s2.008s0.897s101.04x
🐘 PostgresNext.js (Turbopack)1.114s2.010s0.897s101.04x
🌐 MongoDBNext.js (Turbopack)1.148s2.009s0.861s101.07x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.586s (-15.4% 🟢)3.902s (+2.5%)2.317s101.00x
▲ VercelNitro1.641s (-57.8% 🟢)4.080s (-30.9% 🟢)2.439s101.04x
▲ VercelNext.js (Turbopack)2.861s (+40.6% 🔺)4.792s (+25.1% 🔺)1.931s101.80x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express10.400s (-5.1% 🟢)11.014s (~)0.614s31.00x
🐘 PostgresNitro10.402s (-4.3%)11.015s (~)0.613s31.00x
💻 LocalExpress10.409s (-4.7%)11.022s (~)0.613s31.00x
💻 LocalNitro10.422s (-4.8%)11.023s (~)0.601s31.00x
🌐 MongoDBNext.js (Turbopack)10.655s11.023s0.368s31.02x
🐘 PostgresNext.js (Turbopack)10.667s11.015s0.348s31.03x
🌐 RedisNext.js (Turbopack)10.669s11.023s0.354s31.03x
💻 LocalNext.js (Turbopack)10.682s11.020s0.338s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro13.559s (-42.9% 🟢)15.361s (-38.8% 🟢)1.802s21.00x
▲ VercelExpress13.917s (-18.0% 🟢)15.853s (-20.8% 🟢)1.936s21.03x
▲ VercelNext.js (Turbopack)14.322s (-17.3% 🟢)16.622s (-14.3% 🟢)2.299s21.06x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.471s (-7.6% 🟢)14.017s (-6.7% 🟢)0.546s51.00x
💻 LocalExpress13.477s (-10.0% 🟢)14.027s (-6.7% 🟢)0.550s51.00x
🐘 PostgresNitro13.481s (-7.6% 🟢)14.021s (-6.7% 🟢)0.540s51.00x
💻 LocalNitro13.483s (-10.5% 🟢)14.027s (-12.5% 🟢)0.544s51.00x
🌐 MongoDBNext.js (Turbopack)13.835s14.025s0.190s51.03x
🌐 RedisNext.js (Turbopack)14.050s15.030s0.980s41.04x
💻 LocalNext.js (Turbopack)14.060s14.628s0.569s51.04x
🐘 PostgresNext.js (Turbopack)14.069s15.020s0.951s41.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro21.544s (-66.6% 🟢)23.739s (-64.4% 🟢)2.195s31.00x
▲ VercelExpress22.795s (-54.7% 🟢)25.107s (-52.2% 🟢)2.312s31.06x
▲ VercelNext.js (Turbopack)23.551s (-55.2% 🟢)26.276s (-51.9% 🟢)2.725s31.09x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express11.836s (-15.5% 🟢)12.015s (-17.7% 🟢)0.179s81.00x
💻 LocalNitro11.946s (-28.8% 🟢)12.272s (-27.9% 🟢)0.326s81.01x
💻 LocalExpress11.949s (-28.0% 🟢)12.146s (-28.7% 🟢)0.197s81.01x
🐘 PostgresNitro11.970s (-14.3% 🟢)12.394s (-13.4% 🟢)0.424s81.01x
🌐 MongoDBNext.js (Turbopack)12.590s13.024s0.433s71.06x
💻 LocalNext.js (Turbopack)12.972s13.452s0.480s71.10x
🌐 RedisNext.js (Turbopack)13.100s13.884s0.784s71.11x
🐘 PostgresNext.js (Turbopack)13.185s14.020s0.835s71.11x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express34.548s (-71.5% 🟢)36.366s (-70.6% 🟢)1.818s31.00x
▲ VercelNitro35.156s (-91.7% 🟢)37.155s (-91.2% 🟢)1.999s31.02x
▲ VercelNext.js (Turbopack)35.694s (-90.9% 🟢)37.608s (-90.5% 🟢)1.913s31.03x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.147s (-9.0% 🟢)2.007s (~)0.860s151.00x
🐘 PostgresNitro1.156s (-9.3% 🟢)2.007s (~)0.851s151.01x
💻 LocalNitro1.188s (-27.2% 🟢)2.006s (-3.3%)0.818s151.04x
💻 LocalExpress1.197s (-19.6% 🟢)2.006s (~)0.808s151.04x
🐘 PostgresNext.js (Turbopack)1.213s2.008s0.794s151.06x
🌐 RedisNext.js (Turbopack)1.268s2.006s0.738s151.10x
💻 LocalNext.js (Turbopack)1.378s2.073s0.695s151.20x
🌐 MongoDBNext.js (Turbopack)2.011s2.591s0.580s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.918s (+3.6%)4.656s (+7.7% 🔺)1.738s71.00x
▲ VercelNext.js (Turbopack)4.329s (+27.4% 🔺)6.218s (+26.1% 🔺)1.889s51.48x
▲ VercelExpress4.519s (+58.0% 🔺)6.298s (+36.2% 🔺)1.779s51.55x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.226s (-48.1% 🟢)2.007s (-33.3% 🟢)0.780s151.00x
🐘 PostgresNitro1.234s (-47.5% 🟢)2.007s (-33.3% 🟢)0.773s151.01x
🐘 PostgresNext.js (Turbopack)1.356s2.007s0.651s151.11x
💻 LocalNitro1.791s (-43.0% 🟢)2.007s (-48.4% 🟢)0.216s151.46x
💻 LocalExpress1.792s (-39.3% 🟢)2.006s (-41.9% 🟢)0.214s151.46x
💻 LocalNext.js (Turbopack)1.806s2.073s0.267s151.47x
🌐 RedisNext.js (Turbopack)2.385s3.008s0.623s101.94x
🌐 MongoDBNext.js (Turbopack)3.564s4.008s0.444s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.458s (+23.2% 🔺)6.268s (+22.7% 🔺)1.811s51.00x
▲ VercelNext.js (Turbopack)5.613s (-20.9% 🟢)7.797s (-12.4% 🟢)2.184s41.26x
▲ VercelNitro6.080s (+50.1% 🔺)8.092s (+36.7% 🔺)2.012s41.36x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.364s (-60.9% 🟢)2.006s (-50.0% 🟢)0.642s151.00x
🐘 PostgresNitro1.374s (-60.5% 🟢)2.007s (-49.9% 🟢)0.633s151.01x
🐘 PostgresNext.js (Turbopack)1.627s2.007s0.380s151.19x
🌐 RedisNext.js (Turbopack)3.621s4.009s0.389s82.65x
💻 LocalNext.js (Turbopack)5.000s5.511s0.511s63.67x
💻 LocalNitro5.331s (-36.2% 🟢)5.847s (-35.2% 🟢)0.517s63.91x
💻 LocalExpress5.417s (-35.0% 🟢)5.679s (-37.1% 🟢)0.263s63.97x
🌐 MongoDBNext.js (Turbopack)6.223s7.012s0.788s54.56x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.257s (+47.6% 🔺)8.386s (+36.9% 🔺)2.129s41.00x
▲ VercelNext.js (Turbopack)7.288s (-18.3% 🟢)9.279s (-15.3% 🟢)1.991s41.16x
▲ VercelNitro8.700s (+146.8% 🔺)10.899s (+96.9% 🔺)2.199s31.39x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.153s (-8.3% 🟢)2.009s (~)0.857s151.00x
🐘 PostgresExpress1.154s (-8.2% 🟢)2.009s (~)0.855s151.00x
🐘 PostgresNext.js (Turbopack)1.210s2.007s0.797s151.05x
🌐 RedisNext.js (Turbopack)1.257s2.006s0.749s151.09x
💻 LocalNext.js (Turbopack)1.353s2.006s0.653s151.17x
💻 LocalExpress1.384s (-26.9% 🟢)2.006s (-15.1% 🟢)0.622s151.20x
💻 LocalNitro1.397s (-25.1% 🟢)2.007s (-14.3% 🟢)0.610s151.21x
🌐 MongoDBNext.js (Turbopack)2.013s2.590s0.577s121.75x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.363s (-8.5% 🟢)4.099s (-5.8% 🟢)1.736s81.00x
▲ VercelNitro3.169s (+28.9% 🔺)5.005s (+20.0% 🔺)1.836s71.34x
▲ VercelNext.js (Turbopack)3.909s (+33.3% 🔺)6.008s (+29.4% 🔺)2.100s51.65x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.231s (-47.4% 🟢)2.009s (-33.3% 🟢)0.778s151.00x
🐘 PostgresExpress1.232s (-47.4% 🟢)2.007s (-33.3% 🟢)0.776s151.00x
🐘 PostgresNext.js (Turbopack)1.354s2.008s0.654s151.10x
💻 LocalNitro2.049s (-33.1% 🟢)2.509s (-35.4% 🟢)0.460s121.67x
💻 LocalNext.js (Turbopack)2.074s2.735s0.661s111.69x
💻 LocalExpress2.135s (-31.8% 🟢)2.593s (-31.1% 🟢)0.457s121.74x
🌐 RedisNext.js (Turbopack)2.370s3.008s0.639s101.93x
🌐 MongoDBNext.js (Turbopack)3.577s4.009s0.432s82.91x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.907s (+20.8% 🔺)5.774s (+13.7% 🔺)1.867s61.00x
▲ VercelExpress4.205s (+31.7% 🔺)6.052s (+26.3% 🔺)1.847s51.08x
▲ VercelNext.js (Turbopack)4.751s (+51.2% 🔺)6.510s (+44.0% 🔺)1.759s51.22x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.358s (-61.2% 🟢)2.008s (-49.9% 🟢)0.650s151.00x
🐘 PostgresNitro1.366s (-60.7% 🟢)2.008s (-49.9% 🟢)0.642s151.01x
🐘 PostgresNext.js (Turbopack)1.651s2.009s0.358s151.22x
🌐 RedisNext.js (Turbopack)3.648s4.010s0.362s82.69x
💻 LocalExpress5.565s (-36.8% 🟢)6.015s (-35.1% 🟢)0.450s54.10x
💻 LocalNext.js (Turbopack)5.704s6.214s0.510s54.20x
💻 LocalNitro5.946s (-35.0% 🟢)6.348s (-36.7% 🟢)0.402s64.38x
🌐 MongoDBNext.js (Turbopack)6.250s7.013s0.763s54.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.891s (+15.7% 🔺)7.641s (+12.1% 🔺)1.750s41.00x
▲ VercelExpress6.208s (-3.2%)7.682s (-6.1% 🟢)1.474s41.05x
▲ VercelNext.js (Turbopack)6.830s (+1.1%)8.929s (+4.5%)2.099s41.16x

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

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.451s (-46.2% 🟢)1.006s (-1.7%)0.555s601.00x
🐘 PostgresNitro0.453s (-44.7% 🟢)1.006s (~)0.553s601.01x
💻 LocalNitro0.471s (-52.0% 🟢)1.004s (-8.2% 🟢)0.533s601.04x
💻 LocalExpress0.506s (-48.6% 🟢)1.022s (-5.1% 🟢)0.515s591.12x
🌐 MongoDBNext.js (Turbopack)0.597s1.006s0.409s601.32x
🌐 RedisNext.js (Turbopack)0.639s1.004s0.365s601.42x
🐘 PostgresNext.js (Turbopack)0.662s1.006s0.344s601.47x
💻 LocalNext.js (Turbopack)0.711s1.004s0.293s601.58x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro4.747s (-78.5% 🟢)6.670s (-72.2% 🟢)1.923s101.00x
▲ VercelExpress4.916s (-74.1% 🟢)6.991s (-67.2% 🟢)2.075s91.04x
▲ VercelNext.js (Turbopack)6.804s (-53.1% 🟢)8.802s (-45.3% 🟢)1.998s71.43x

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

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.032s (-47.8% 🟢)1.458s (-35.4% 🟢)0.426s621.00x
🐘 PostgresNitro1.047s (-45.7% 🟢)1.507s (-28.3% 🟢)0.459s601.01x
💻 LocalExpress1.184s (-60.7% 🟢)2.006s (-44.1% 🟢)0.821s451.15x
💻 LocalNitro1.191s (-60.8% 🟢)2.006s (-46.6% 🟢)0.815s451.15x
🌐 MongoDBNext.js (Turbopack)1.503s2.007s0.504s451.46x
🐘 PostgresNext.js (Turbopack)1.587s2.007s0.419s451.54x
🌐 RedisNext.js (Turbopack)1.611s2.028s0.417s451.56x
💻 LocalNext.js (Turbopack)1.741s2.005s0.265s451.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.285s (-64.4% 🟢)14.394s (-60.9% 🟢)2.108s71.00x
▲ VercelNitro12.810s (-67.6% 🟢)15.636s (-62.1% 🟢)2.825s61.04x
▲ VercelNext.js (Turbopack)15.611s (-68.7% 🟢)18.106s (-65.0% 🟢)2.494s61.27x

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

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.878s (-52.9% 🟢)2.076s (-52.5% 🟢)0.198s581.00x
🐘 PostgresNitro2.079s (-49.3% 🟢)2.550s (-44.6% 🟢)0.470s481.11x
💻 LocalNitro2.680s (-71.2% 🟢)3.008s (-70.0% 🟢)0.327s401.43x
💻 LocalExpress2.692s (-70.8% 🟢)3.008s (-70.0% 🟢)0.316s401.43x
🐘 PostgresNext.js (Turbopack)3.092s4.009s0.917s301.65x
🌐 RedisNext.js (Turbopack)3.096s3.880s0.783s311.65x
🌐 MongoDBNext.js (Turbopack)3.670s4.013s0.343s301.95x
💻 LocalNext.js (Turbopack)3.704s4.008s0.303s301.97x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro39.197s (-59.6% 🟢)42.019s (-57.3% 🟢)2.822s31.00x
▲ VercelExpress40.332s (-69.0% 🟢)42.693s (-67.7% 🟢)2.361s31.03x
▲ VercelNext.js (Turbopack)44.774s (-58.2% 🟢)48.551s (-55.4% 🟢)3.777s31.14x

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

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.176s (-37.5% 🟢)1.006s (~)0.829s601.00x
🐘 PostgresNitro0.182s (-35.8% 🟢)1.005s (~)0.824s601.03x
🐘 PostgresNext.js (Turbopack)0.232s1.006s0.774s601.32x
🌐 RedisNext.js (Turbopack)0.242s1.004s0.762s601.37x
💻 LocalExpress0.439s (-21.6% 🟢)1.004s (~)0.565s602.49x
💻 LocalNitro0.474s (-21.6% 🟢)1.021s (~)0.547s592.69x
💻 LocalNext.js (Turbopack)0.539s1.004s0.465s603.06x
🌐 MongoDBNext.js (Turbopack)1.032s1.825s0.793s335.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.231s (+34.3% 🔺)4.189s (+25.0% 🔺)1.958s151.00x
▲ VercelExpress2.259s (+15.6% 🔺)4.431s (+21.8% 🔺)2.172s141.01x
▲ VercelNext.js (Turbopack)4.335s (+114.4% 🔺)6.320s (+66.6% 🔺)1.984s101.94x

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

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.309s (-39.4% 🟢)1.006s (~)0.697s901.00x
🐘 PostgresNitro0.312s (-37.2% 🟢)1.006s (~)0.694s901.01x
🌐 RedisNext.js (Turbopack)0.412s1.004s0.592s901.33x
🐘 PostgresNext.js (Turbopack)0.450s1.006s0.555s901.46x
💻 LocalNitro2.179s (-14.2% 🟢)3.008s (~)0.829s307.05x
💻 LocalExpress2.268s (-9.8% 🟢)2.911s (-3.3%)0.644s317.34x
💻 LocalNext.js (Turbopack)2.382s2.977s0.595s317.71x
🌐 MongoDBNext.js (Turbopack)2.594s3.008s0.414s308.39x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express7.284s (+139.1% 🔺)9.227s (+91.9% 🔺)1.943s101.00x
▲ VercelNitro7.305s (+126.5% 🔺)9.647s (+100.1% 🔺)2.342s101.00x
▲ VercelNext.js (Turbopack)8.118s (+129.6% 🔺)10.545s (+103.1% 🔺)2.427s91.11x

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

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.618s (-24.5% 🟢)1.006s (-1.1%)0.388s1201.00x
🐘 PostgresNitro0.649s (-17.9% 🟢)1.006s (~)0.357s1201.05x
🌐 RedisNext.js (Turbopack)0.770s1.004s0.233s1201.25x
🐘 PostgresNext.js (Turbopack)0.933s1.219s0.286s991.51x
🌐 MongoDBNext.js (Turbopack)5.361s6.012s0.651s208.67x
💻 LocalExpress10.227s (-8.6% 🟢)10.863s (-9.0% 🟢)0.636s1216.54x
💻 LocalNitro10.251s (-8.4% 🟢)10.781s (-7.6% 🟢)0.530s1216.58x
💻 LocalNext.js (Turbopack)10.950s11.757s0.807s1117.71x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express20.578s (+177.3% 🔺)23.078s (+149.6% 🔺)2.500s61.00x
▲ VercelNext.js (Turbopack)21.742s (+110.5% 🔺)24.590s (+100.1% 🔺)2.848s51.06x
▲ VercelNitro25.208s (+226.4% 🔺)27.039s (+187.6% 🔺)1.831s51.22x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.129s (+450.3% 🔺)1.997s (+99.9% 🔺)0.001s (-18.8% 🟢)2.009s (+98.7% 🔺)0.881s101.00x
💻 LocalExpress1.131s (+468.1% 🔺)2.005s (+99.6% 🔺)0.012s (+2.5%)2.019s (+98.4% 🔺)0.888s101.00x
💻 LocalNitro1.132s (+429.6% 🔺)2.005s (+99.6% 🔺)0.013s (+4.0%)2.021s (+98.3% 🔺)0.889s101.00x
🐘 PostgresNitro1.149s (+460.7% 🔺)1.994s (+99.5% 🔺)0.002s (+6.7% 🔺)2.012s (+99.0% 🔺)0.863s101.02x
💻 LocalNext.js (Turbopack)1.178s2.003s0.012s2.019s0.840s101.04x
🐘 PostgresNext.js (Turbopack)1.192s2.002s0.001s2.010s0.818s101.06x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.184s (-43.0% 🟢)3.612s (-31.5% 🟢)1.614s (+117.5% 🔺)5.719s (-11.8% 🟢)3.535s101.00x
▲ VercelExpress2.198s (-12.3% 🟢)3.538s (-13.5% 🟢)1.751s (+82.3% 🔺)5.836s (+4.4%)3.637s101.01x
▲ VercelNext.js (Turbopack)4.942s (-27.9% 🟢)5.438s (-37.1% 🟢)1.145s (+81.2% 🔺)8.364s (-14.6% 🟢)3.421s102.26x

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

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.496s (+137.4% 🔺)2.000s (+98.7% 🔺)0.003s (-9.5% 🟢)2.026s (+98.1% 🔺)0.531s301.00x
🐘 PostgresNitro1.507s (+141.5% 🔺)2.002s (+98.8% 🔺)0.004s (-8.9% 🟢)2.024s (+97.9% 🔺)0.517s301.01x
💻 LocalNitro1.538s (+83.4% 🔺)2.012s (+98.8% 🔺)0.010s (+11.7% 🔺)2.025s (+81.4% 🔺)0.486s301.03x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.004s2.025s0.367s301.11x
💻 LocalExpress1.716s (+126.6% 🔺)2.012s (+95.5% 🔺)0.009s (-2.1%)2.202s (+111.8% 🔺)0.486s281.15x
💻 LocalNext.js (Turbopack)2.055s2.009s0.010s2.423s0.368s251.37x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro5.789s (-80.3% 🟢)7.537s (-75.5% 🟢)0.268s (+139.0% 🔺)8.404s (-73.6% 🟢)2.615s81.00x
▲ VercelExpress6.045s (-7.1% 🟢)7.653s (-4.5%)0.313s (-23.5% 🟢)8.503s (-3.8%)2.457s81.04x
▲ VercelNext.js (Turbopack)12.326s (-27.1% 🟢)13.810s (-24.3% 🟢)0.429s (+103.2% 🔺)15.364s (-18.9% 🟢)3.038s42.13x

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

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.660s (-31.8% 🟢)1.031s (-17.4% 🟢)0.000s (+24.1% 🔺)1.048s (-16.7% 🟢)0.387s581.00x
🐘 PostgresExpress0.688s (-28.4% 🟢)1.061s (-17.0% 🟢)0.000s (+105.4% 🔺)1.079s (-17.4% 🟢)0.391s561.04x
🐘 PostgresNext.js (Turbopack)0.783s1.090s0.000s1.097s0.314s561.19x
💻 LocalNitro1.345s (+10.0% 🔺)2.016s (~)0.000s (+333.3% 🔺)2.018s (~)0.674s302.04x
💻 LocalNext.js (Turbopack)1.400s2.014s0.000s2.017s0.617s302.12x
💻 LocalExpress1.515s (+23.7% 🔺)2.015s (~)0.000s (-35.7% 🟢)2.196s (+8.6% 🔺)0.681s282.29x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.737s (+22.5% 🔺)5.067s (+15.4% 🔺)0.000s (+18.2% 🔺)5.709s (+18.7% 🔺)1.972s111.00x
▲ VercelExpress4.218s (+12.8% 🔺)5.770s (+13.1% 🔺)0.000s (+50.0% 🔺)6.218s (+12.4% 🔺)2.000s111.13x
▲ VercelNext.js (Turbopack)6.160s (-39.5% 🟢)6.979s (-39.4% 🟢)0.000s (NaN%)8.458s (-29.8% 🟢)2.298s81.65x

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

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.286s (-28.2% 🟢)2.001s (-6.6% 🟢)0.000s (-100.0% 🟢)2.020s (-7.1% 🟢)0.734s301.00x
🐘 PostgresExpress1.384s (-21.9% 🟢)2.067s (-5.1% 🟢)0.000s (+Infinity% 🔺)2.115s (-3.8%)0.730s291.08x
🐘 PostgresNext.js (Turbopack)1.629s2.225s0.000s2.239s0.610s271.27x
💻 LocalNext.js (Turbopack)2.539s3.125s0.000s3.128s0.589s201.97x
💻 LocalExpress3.054s (-11.9% 🟢)3.903s (-3.2%)0.000s (-45.3% 🟢)3.905s (-3.2%)0.852s162.37x
💻 LocalNitro3.180s (-6.1% 🟢)3.966s (-1.6%)0.000s (-41.4% 🟢)3.971s (-1.6%)0.791s162.47x
🌐 MongoDBNext.js (Turbopack)⚠️missing-----
🌐 RedisNext.js (Turbopack)⚠️missing-----

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.004s (+30.9% 🔺)7.517s (+24.8% 🔺)0.000s (+Infinity% 🔺)8.103s (+25.5% 🔺)2.099s81.00x
▲ VercelNitro6.750s (+64.9% 🔺)8.558s (+59.2% 🔺)0.000s (-100.0% 🟢)9.120s (+57.4% 🔺)2.370s71.12x
▲ VercelNext.js (Turbopack)8.561s (+52.4% 🔺)9.916s (+42.0% 🔺)0.000s (-100.0% 🟢)11.240s (+49.1% 🔺)2.679s61.43x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro11/21
🐘 PostgresExpress17/21
▲ VercelNitro11/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres19/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres17/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

github-actionsBot commented May 4, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production85902191078
✅ 💻 Local Development95702191176
✅ 📦 Local Production95702191176
✅ 🐘 Local Postgres95702191176
✅ 🪟 Windows980098
✅ 📋 Other5100176686
Total4338010525390

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro72026
✅ example72026
✅ express72026
✅ fastify72026
✅ hono72026
✅ nextjs-turbopack9602
✅ nextjs-webpack9602
✅ nitro72026
✅ nuxt72026
✅ sveltekit9107
✅ vite72026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable73025
✅ express-stable73025
✅ fastify-stable73025
✅ hono-stable73025
✅ nextjs-turbopack-canary79019
✅ nextjs-turbopack-stable9800
✅ nextjs-webpack-canary79019
✅ nextjs-webpack-stable9800
✅ nitro-stable73025
✅ nuxt-stable73025
✅ sveltekit-stable9206
✅ vite-stable73025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9800
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable73025
✅ e2e-local-dev-tanstack-start-stable73025
✅ e2e-local-postgres-nest-stable73025
✅ e2e-local-postgres-tanstack-start-stable73025
✅ e2e-local-prod-nest-stable73025
✅ e2e-local-prod-tanstack-start-stable73025
✅ e2e-vercel-prod-tanstack-start72026

📋 View full workflow run

Move the inline ANSI shim out of `ansi.ts` into its own
`./internal-chalk.ts` so tests can swap it for a tag-emitting mock via
`vi.mock('./internal-chalk.js', …)`. Snapshots for `Ansi.code` /
`Ansi.hint` / `Ansi.note` / `Ansi.help` / `Ansi.docs` go back to the
readable HTML-like form (`<blue><b>hint:</b> try reloading</blue>`),
making it obvious from the test file which fragments are colored and
how — the same review affordance the previous `__mocks__/chalk.ts`
mock provided before the chalk dep was dropped.
`Ansi.frame` and `Ansi.inline` remain plain in snapshots because they
don't apply any styling themselves; that matches the pre-fix behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. The diagnosis is correct, the fix is appropriately surgical, and the shim is well-written.

Verified

Reachability claim — confirmed: packages/core/src/workflow/index.tscontext-errors.tscontext-violation-error.tsimport * as Ansi from '@workflow/errors/ansi'. So any consumer that uses workflow-context-checked APIs (createHook, createWebhook, getWorkflowMetadata, etc.) ends up dragging the entire ansi/chalk transitive graph into the workflow VM bundle.

Output parity — for the only nested call in ansi.ts (code(): chalk.italic(\${chalk.dim('`')}${str}${chalk.dim('`')}`)), the shim produces byte-for-byte the same SGR sequence as chalk@5.6.2`:

\x1b[3m\x1b[2m`\x1b[22mfoo\x1b[2m`\x1b[22m\x1b[23m

The 22 (normal intensity) close after each dim correctly leaves italic (3) on, since 22 only resets bold/dim — same as chalk's behavior.

Bundle effect — built workbench/example from this branch and grepped the workflow VM bundle (the string passed to workflowEntrypoint):

$ grep -cE "supports-color|require\(['\"](node:)?os['\"]|chalk|internal_chalk" /tmp/vm-bundle.txt
0

No chalk, no supports-color, no os anywhere. (The example workbench's VM bundle didn't actually pull in context-violation-error due to tree-shaking in this particular workflow set, so the chalk references were already absent on main for this specific bundle — but the change is still correct, and downstream consumers like vade that exercise the workflow-context paths will get the fix.)

Testspnpm --filter @workflow/errors test passes (4 files, 26 tests including all 11 ansi.test.ts snapshots), pnpm --filter @workflow/core test passes (820 tests).

CI failures are pre-existing flakes

The two red checks (Unit Tests (ubuntu-latest), E2E Required Check) are both from the same flaky world-postgres test:

FAIL test/spec.test.ts > sequential steps with stream complete in a single flow invocation
AssertionError: expected +0 to be 1 // Object.is equality

This same test fails on origin/main HEAD (run 25343680860 job 74307194029) and is unrelated to this change.

Minor nits (non-blocking)

  • colorEnabled is evaluated once at module-load time, whereas chalk re-evaluates lazily on each call. If a host process programmatically flips FORCE_COLOR/NO_COLOR after @workflow/errors is imported, the shim won't pick up the change. In practice no one in this codebase does that, and the JSDoc on internal-chalk.ts is implicit about it — but if you wanted to be belt-and-braces, wrapping the env-var check inside sgr() would keep parity. Totally fine to leave as-is.

  • The __mocks__/chalk.ts deletion is correct, but worth noting that the module-graph entrypoint for the test mock changed from "the chalk package as resolved by node" to "this sibling file at a literal relative path". That's fine for this codebase but means the mock now relies on vi.mock('./internal-chalk.js', ...)paths rather than package identity — slightly more fragile if ansi.ts is ever moved to a different directory. Fine for now.

Approving.

@VaguelySerious
VaguelySerious merged commit 540a2ef into mainMay 5, 2026
175 of 177 checks passed
@VaguelySerious
VaguelySerious deleted the peter/errors-no-chalk branch May 5, 2026 00:47
@github-actionsgithub-actionsBot mentioned this pull request May 5, 2026
pranaygp added a commit that referenced this pull request May 5, 2026
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
pranaygp added a commit that referenced this pull request May 5, 2026
…ignal
* origin/main:
[core] Skip inline step execution when suspension also has a wait (#1924)
[errors] Replace chalk import in @workfow/errors with inline ANSI shim (#1915)
Fix compatibility with Zod 4.4.x (#1902)
Serialize `run_failed`/`step_failed` errors through serialization pipeline (#1851)
tarballs: redesign preview tarballs index page (#1911)
Remove extra changeset (#1922)
Add stable Next.js eager and lazy test coverage (#1747)
Enforce per-(run, correlation) uniqueness for entity-creating events in world-postgres (#1878)
fix(world-vercel): add default request timeout to workflow-server HTTP calls (#1807)
Allow disabling step sourcemap with new `sourcemap` option in builders (#1842)
[ci] Enable Vercel-prod e2e for tanstack-start (#1904)
web: configure vercelPreset() for Vercel deployments (#1815)
[core] Combine flow+step bundle and process steps eagerly (#1338)
[world-vercel] Revert stream close control framing (#1891)
[tarballs] Use turbo to build workspace deps before packing (#1908)
# Conflicts:
#	packages/core/src/runtime/step-handler.test.ts
#	packages/core/src/runtime/step-handler.ts
#	packages/core/src/runtime/suspension-handler.ts
#	packages/core/src/step.test.ts
#	packages/world-local/src/storage/events-storage.ts
#	packages/world-postgres/src/drizzle/migrations/meta/_journal.json
Sign up for freeto 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.

2 participants

@VaguelySerious@TooTallNate