Skip to content

Show precise (non-rounded) durations in the new trace viewer#1983

Draft
mitul-s wants to merge 3 commits into
mainfrom
c+ms/fix-trace-hover-duration-rounding-1fa5
Draft

Show precise (non-rounded) durations in the new trace viewer#1983
mitul-s wants to merge 3 commits into
mainfrom
c+ms/fix-trace-hover-duration-rounding-1fa5

Conversation

@mitul-s
Copy link
Copy Markdown
Contributor

@mitul-s mitul-s commented May 14, 2026

Description

The new trace viewer was formatting span durations with formatDuration(), which rounds to whole seconds once the value crosses 1s. That means a 1.5s span renders as 2s on hover — overstating the real duration and being confusing when the bar visually shows ~1.5 grid units.

This PR adds a new precise option to formatDuration (no separate function — the second arg now accepts either an options object { compact?, precise? } or the original boolean for backwards compatibility) and switches the new trace viewer to { precise: true } everywhere a duration / offset is shown to the user as an exact figure:

  • Timeline bar + segment hover labels
  • Gap delta indicators (Alt-held)
  • Event list row durations
  • Detail pane Duration and Offset fields

The precise branch keeps up to two decimals of seconds (e.g. 1.5s, 12.34s), one decimal inside Xm Y.Zs form, and truncates rather than rounds — so 999.5ms / 59.999s / 1m 59.95s never roll over into the next-larger unit at the boundary. Default and compact (true / { compact: true }) behavior is unchanged, so all existing call sites — the old trace viewer, the workflow graph execution viewer, the status badge, and the new viewer's own timeline tick / ruler labels — keep working without changes.

How did you test your changes?

  • Added packages/web-shared/test/format-duration.test.ts covering sub-second / sub-minute / sub-hour / multi-day cases for the precise mode, an explicit "never rounds up" guard, the three boundary cases identified by the upstream fix (999.5ms → 999ms, 59 999ms → 59.99s, 119 950ms → 1m 59.9s), and the unchanged default / compact behavior including the legacy two-arg formatDuration(ms, true) form.
  • pnpm --filter @workflow/web-shared run test (46 / 46 pass)
  • pnpm --filter @workflow/web-shared run build and pnpm --filter @workflow/web run build both succeed.

PR Checklist - Required to merge

  • 📦 pnpm changeset was run to create a changelog for this PR
  • 🔒 DCO sign-off passes (run git commit --signoff on your commits)
  • 📝 Ping @vercel/workflow in a comment once the PR is ready, and the above checklist is complete

Slack Thread

Open in Web Open in Cursor 

The new trace viewer was using formatDuration() for hover labels, the
event list, and the detail pane. formatDuration() rounds to whole
seconds for any value >= 1s, so a 1.5s span renders as '2s' — which
overstates the duration and is confusing on hover.

Add formatDurationPrecise(), which preserves up to two decimals of
seconds (and one decimal in minute/second form) without ever rounding
up to the next-larger unit. Switch the new trace viewer's bar/segment
hover labels, gap delta indicators, event list rows, and span detail
pane (duration + offset) to use it.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 14, 2026

🦋 Changeset detected

Latest commit: 1c8867e

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

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

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 14, 2026

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

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview, Comment May 14, 2026 8:02pm
example-nextjs-workflow-webpack Ready Ready Preview, Comment May 14, 2026 8:02pm
example-workflow Ready Ready Preview, Comment May 14, 2026 8:02pm
workbench-astro-workflow Ready Ready Preview, Comment May 14, 2026 8:02pm
workbench-express-workflow Ready Ready Preview, Comment May 14, 2026 8:02pm
workbench-fastify-workflow Ready Ready Preview, Comment May 14, 2026 8:02pm
workbench-hono-workflow Ready Ready Preview, Comment May 14, 2026 8:02pm
workbench-nitro-workflow Ready Ready Preview, Comment May 14, 2026 8:02pm
workbench-nuxt-workflow Ready Ready Preview, Comment May 14, 2026 8:02pm
workbench-sveltekit-workflow Ready Ready Preview, Comment May 14, 2026 8:02pm
workbench-tanstack-start-workflow Ready Ready Preview, Comment May 14, 2026 8:02pm
workbench-vite-workflow Ready Ready Preview, Comment May 14, 2026 8:02pm
workflow-docs Ready Ready Preview, Comment, Open in v0 May 14, 2026 8:02pm
workflow-swc-playground Building Building Preview, Comment May 14, 2026 8:02pm
workflow-tarballs Ready Ready Preview, Comment May 14, 2026 8:02pm
workflow-web Ready Ready Preview, Comment May 14, 2026 8:02pm

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 14, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 1200 0 219 1419
✅ 💻 Local Development 1587 0 219 1806
✅ 📦 Local Production 1587 0 219 1806
❌ 🐘 Local Postgres 1585 2 219 1806
✅ 🪟 Windows 129 0 0 129
✅ 📋 Other 727 0 176 903
Total 6815 2 1052 7869

❌ Failed Tests

🐘 Local Postgres (2 failed)

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • DurableAgent e2e core basic text response

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • addTenWorkflow | wrun_01KRM1ASHQE15XW64KXBYJCNGF

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 103 0 26
✅ example 103 0 26
✅ express 103 0 26
✅ fastify 103 0 26
✅ hono 103 0 26
✅ nextjs-turbopack 127 0 2
✅ nextjs-webpack 127 0 2
✅ nitro 103 0 26
✅ nuxt 103 0 26
✅ sveltekit 122 0 7
✅ vite 103 0 26
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 104 0 25
✅ express-stable 104 0 25
✅ fastify-stable 104 0 25
✅ hono-stable 104 0 25
✅ nextjs-turbopack-canary 110 0 19
✅ nextjs-turbopack-stable-lazy-discovery-disabled 129 0 0
✅ nextjs-turbopack-stable-lazy-discovery-enabled 129 0 0
✅ nextjs-webpack-canary 110 0 19
✅ nextjs-webpack-stable-lazy-discovery-disabled 129 0 0
✅ nextjs-webpack-stable-lazy-discovery-enabled 129 0 0
✅ nitro-stable 104 0 25
✅ nuxt-stable 104 0 25
✅ sveltekit-stable 123 0 6
✅ vite-stable 104 0 25
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 104 0 25
✅ express-stable 104 0 25
✅ fastify-stable 104 0 25
✅ hono-stable 104 0 25
✅ nextjs-turbopack-canary 110 0 19
✅ nextjs-turbopack-stable-lazy-discovery-disabled 129 0 0
✅ nextjs-turbopack-stable-lazy-discovery-enabled 129 0 0
✅ nextjs-webpack-canary 110 0 19
✅ nextjs-webpack-stable-lazy-discovery-disabled 129 0 0
✅ nextjs-webpack-stable-lazy-discovery-enabled 129 0 0
✅ nitro-stable 104 0 25
✅ nuxt-stable 104 0 25
✅ sveltekit-stable 123 0 6
✅ vite-stable 104 0 25
❌ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 104 0 25
✅ express-stable 104 0 25
✅ fastify-stable 104 0 25
✅ hono-stable 104 0 25
✅ nextjs-turbopack-canary 110 0 19
✅ nextjs-turbopack-stable-lazy-discovery-disabled 129 0 0
✅ nextjs-turbopack-stable-lazy-discovery-enabled 129 0 0
✅ nextjs-webpack-canary 110 0 19
❌ nextjs-webpack-stable-lazy-discovery-disabled 128 1 0
❌ nextjs-webpack-stable-lazy-discovery-enabled 128 1 0
✅ nitro-stable 104 0 25
✅ nuxt-stable 104 0 25
✅ sveltekit-stable 123 0 6
✅ vite-stable 104 0 25
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 129 0 0
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 104 0 25
✅ e2e-local-dev-tanstack-start- 104 0 25
✅ e2e-local-postgres-nest-stable 104 0 25
✅ e2e-local-postgres-tanstack-start- 104 0 25
✅ e2e-local-prod-nest-stable 104 0 25
✅ e2e-local-prod-tanstack-start- 104 0 25
✅ e2e-vercel-prod-tanstack-start 103 0 26

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 14, 2026

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 0.032s (-24.8% 🟢) 1.006s (~) 0.973s 10 1.00x
💻 Local Express 0.034s (-23.0% 🟢) 1.006s (~) 0.971s 10 1.05x
🐘 Postgres Express 0.048s (-16.6% 🟢) 1.012s (~) 0.964s 10 1.49x
💻 Local Next.js (Turbopack) 0.050s 1.005s 0.955s 10 1.56x
🐘 Postgres Nitro 0.051s (-46.2% 🟢) 1.011s (-3.0%) 0.960s 10 1.58x
🐘 Postgres Next.js (Turbopack) 0.057s 1.011s 0.955s 10 1.74x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 0.272s (+8.2% 🔺) 2.558s (+9.6% 🔺) 2.285s 10 1.00x
▲ Vercel Nitro 0.325s (-20.6% 🟢) 2.316s (-7.7% 🟢) 1.990s 10 1.19x
▲ Vercel Express 0.374s (+58.8% 🔺) 2.356s (+10.3% 🔺) 1.982s 10 1.37x

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

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 1.070s (-5.4% 🟢) 2.006s (~) 0.936s 10 1.00x
💻 Local Express 1.071s (-4.9%) 2.006s (~) 0.936s 10 1.00x
🐘 Postgres Nitro 1.082s (-5.1% 🟢) 2.009s (~) 0.926s 10 1.01x
🐘 Postgres Express 1.084s (-5.5% 🟢) 2.011s (~) 0.927s 10 1.01x
💻 Local Next.js (Turbopack) 1.109s 2.006s 0.897s 10 1.04x
🐘 Postgres Next.js (Turbopack) 1.121s 2.011s 0.889s 10 1.05x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 1.652s (-57.6% 🟢) 3.619s (-38.7% 🟢) 1.968s 10 1.00x
▲ Vercel Next.js (Turbopack) 1.718s (-15.6% 🟢) 4.134s (+7.9% 🔺) 2.416s 10 1.04x
▲ Vercel Express 1.794s (-4.3%) 3.677s (-3.4%) 1.883s 10 1.09x

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

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 10.420s (-4.1%) 11.017s (~) 0.597s 3 1.00x
💻 Local Nitro 10.427s (-4.7%) 11.022s (~) 0.596s 3 1.00x
💻 Local Express 10.430s (-4.5%) 11.022s (~) 0.592s 3 1.00x
🐘 Postgres Express 10.457s (-4.6%) 11.018s (~) 0.560s 3 1.00x
💻 Local Next.js (Turbopack) 10.733s 11.023s 0.290s 3 1.03x
🐘 Postgres Next.js (Turbopack) 10.740s 11.018s 0.279s 3 1.03x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 13.665s (-19.5% 🟢) 15.397s (-23.1% 🟢) 1.732s 2 1.00x
▲ Vercel Next.js (Turbopack) 13.727s (-20.7% 🟢) 15.562s (-19.8% 🟢) 1.835s 2 1.00x
▲ Vercel Nitro 14.493s (-38.9% 🟢) 15.720s (-37.4% 🟢) 1.227s 2 1.06x

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

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 13.468s (-7.7% 🟢) 14.017s (-6.7% 🟢) 0.550s 5 1.00x
💻 Local Nitro 13.471s (-10.6% 🟢) 14.026s (-12.5% 🟢) 0.555s 5 1.00x
🐘 Postgres Express 13.477s (-7.6% 🟢) 14.017s (-6.7% 🟢) 0.540s 5 1.00x
💻 Local Express 13.487s (-9.9% 🟢) 14.027s (-6.7% 🟢) 0.540s 5 1.00x
💻 Local Next.js (Turbopack) 14.086s 15.029s 0.942s 4 1.05x
🐘 Postgres Next.js (Turbopack) 14.139s 15.016s 0.877s 4 1.05x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 22.067s (-56.1% 🟢) 23.753s (-54.8% 🟢) 1.686s 3 1.00x
▲ Vercel Next.js (Turbopack) 23.998s (-54.3% 🟢) 25.769s (-52.8% 🟢) 1.771s 3 1.09x
▲ Vercel Nitro 24.091s (-62.6% 🟢) 25.848s (-61.2% 🟢) 1.757s 3 1.09x

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

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 11.917s (-14.9% 🟢) 12.017s (-17.6% 🟢) 0.101s 8 1.00x
💻 Local Express 11.944s (-28.1% 🟢) 12.272s (-27.9% 🟢) 0.328s 8 1.00x
🐘 Postgres Nitro 11.962s (-14.4% 🟢) 12.144s (-15.1% 🟢) 0.182s 8 1.00x
💻 Local Nitro 12.236s (-27.1% 🟢) 12.775s (-25.0% 🟢) 0.538s 8 1.03x
🐘 Postgres Next.js (Turbopack) 13.254s 14.014s 0.760s 7 1.11x
💻 Local Next.js (Turbopack) 13.332s 14.170s 0.837s 7 1.12x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 35.186s (-71.0% 🟢) 36.689s (-70.3% 🟢) 1.503s 3 1.00x
▲ Vercel Next.js (Turbopack) 36.199s (-90.8% 🟢) 38.475s (-90.3% 🟢) 2.276s 3 1.03x
▲ Vercel Nitro 37.399s (-91.2% 🟢) 39.430s (-90.7% 🟢) 2.032s 3 1.06x

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

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.164s (-7.7% 🟢) 2.007s (~) 0.843s 15 1.00x
🐘 Postgres Nitro 1.164s (-8.6% 🟢) 2.007s (~) 0.843s 15 1.00x
💻 Local Nitro 1.189s (-27.1% 🟢) 2.007s (-3.2%) 0.818s 15 1.02x
💻 Local Express 1.200s (-19.4% 🟢) 2.005s (~) 0.806s 15 1.03x
🐘 Postgres Next.js (Turbopack) 1.230s 2.007s 0.777s 15 1.06x
💻 Local Next.js (Turbopack) 1.259s 2.005s 0.746s 15 1.08x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.702s (-4.1%) 4.033s (-6.7% 🟢) 1.332s 8 1.00x
▲ Vercel Next.js (Turbopack) 2.715s (-20.1% 🟢) 4.403s (-10.7% 🟢) 1.687s 7 1.01x
▲ Vercel Express 2.750s (-3.9%) 4.379s (-5.3% 🟢) 1.629s 7 1.02x

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

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.227s (-47.8% 🟢) 2.006s (-33.3% 🟢) 0.780s 15 1.00x
🐘 Postgres Express 1.231s (-47.9% 🟢) 2.008s (-33.3% 🟢) 0.778s 15 1.00x
🐘 Postgres Next.js (Turbopack) 1.358s 2.008s 0.649s 15 1.11x
💻 Local Next.js (Turbopack) 1.757s 2.006s 0.249s 15 1.43x
💻 Local Nitro 1.880s (-40.2% 🟢) 2.140s (-44.9% 🟢) 0.261s 15 1.53x
💻 Local Express 2.109s (-28.6% 🟢) 2.391s (-30.8% 🟢) 0.282s 13 1.72x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.828s (-5.5% 🟢) 5.287s (-10.7% 🟢) 1.460s 6 1.00x
▲ Vercel Express 3.857s (+6.6% 🔺) 5.392s (+5.5% 🔺) 1.536s 6 1.01x
▲ Vercel Next.js (Turbopack) 4.538s (-36.1% 🟢) 6.530s (-26.7% 🟢) 1.992s 5 1.19x

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

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.375s (-60.5% 🟢) 2.007s (-49.9% 🟢) 0.632s 15 1.00x
🐘 Postgres Express 1.376s (-60.5% 🟢) 2.008s (-49.9% 🟢) 0.632s 15 1.00x
🐘 Postgres Next.js (Turbopack) 1.651s 2.009s 0.358s 15 1.20x
💻 Local Next.js (Turbopack) 5.403s 6.212s 0.810s 5 3.93x
💻 Local Express 5.511s (-33.9% 🟢) 5.845s (-35.2% 🟢) 0.334s 6 4.01x
💻 Local Nitro 5.538s (-33.7% 🟢) 6.180s (-31.5% 🟢) 0.642s 6 4.03x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 6.215s (+76.3% 🔺) 7.778s (+40.6% 🔺) 1.564s 4 1.00x
▲ Vercel Next.js (Turbopack) 6.754s (-24.2% 🟢) 8.871s (-19.1% 🟢) 2.117s 4 1.09x
▲ Vercel Express 7.251s (+71.0% 🔺) 8.755s (+42.9% 🔺) 1.504s 4 1.17x

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

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.152s (-8.4% 🟢) 2.008s (~) 0.856s 15 1.00x
🐘 Postgres Express 1.179s (-6.2% 🟢) 2.008s (~) 0.830s 15 1.02x
🐘 Postgres Next.js (Turbopack) 1.214s 2.007s 0.794s 15 1.05x
💻 Local Next.js (Turbopack) 1.331s 2.006s 0.675s 15 1.16x
💻 Local Express 1.392s (-26.5% 🟢) 2.006s (-15.1% 🟢) 0.614s 15 1.21x
💻 Local Nitro 1.431s (-23.3% 🟢) 2.006s (-14.3% 🟢) 0.576s 15 1.24x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.853s (-2.7%) 4.457s (-4.0%) 1.604s 7 1.00x
▲ Vercel Express 2.889s (+11.9% 🔺) 4.300s (-1.1%) 1.411s 7 1.01x
▲ Vercel Nitro 3.114s (+26.6% 🔺) 4.927s (+18.2% 🔺) 1.813s 7 1.09x

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

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.235s (-47.2% 🟢) 2.007s (-33.3% 🟢) 0.772s 15 1.00x
🐘 Postgres Express 1.242s (-47.0% 🟢) 2.009s (-33.3% 🟢) 0.767s 15 1.01x
🐘 Postgres Next.js (Turbopack) 1.348s 2.008s 0.660s 15 1.09x
💻 Local Next.js (Turbopack) 1.972s 2.591s 0.618s 12 1.60x
💻 Local Express 2.056s (-34.4% 🟢) 2.508s (-33.3% 🟢) 0.452s 12 1.66x
💻 Local Nitro 2.137s (-30.3% 🟢) 2.507s (-35.5% 🟢) 0.371s 12 1.73x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.800s (+19.0% 🔺) 5.339s (+11.4% 🔺) 1.539s 6 1.00x
▲ Vercel Nitro 3.844s (+18.9% 🔺) 5.218s (+2.8%) 1.374s 6 1.01x
▲ Vercel Next.js (Turbopack) 4.046s (+28.8% 🔺) 5.958s (+31.7% 🔺) 1.912s 6 1.06x

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

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.373s (-60.5% 🟢) 2.006s (-50.0% 🟢) 0.633s 15 1.00x
🐘 Postgres Express 1.380s (-60.6% 🟢) 2.008s (-49.9% 🟢) 0.628s 15 1.01x
🐘 Postgres Next.js (Turbopack) 1.645s 2.007s 0.362s 15 1.20x
💻 Local Express 5.748s (-34.7% 🟢) 6.213s (-33.0% 🟢) 0.465s 5 4.19x
💻 Local Next.js (Turbopack) 5.867s 6.413s 0.546s 5 4.27x
💻 Local Nitro 6.071s (-33.6% 🟢) 6.414s (-36.0% 🟢) 0.343s 5 4.42x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 5.807s (-9.5% 🟢) 7.154s (-12.5% 🟢) 1.348s 5 1.00x
▲ Vercel Nitro 6.314s (+24.0% 🔺) 7.900s (+15.9% 🔺) 1.586s 4 1.09x
▲ Vercel Next.js (Turbopack) 6.320s (-6.5% 🟢) 8.288s (-3.0%) 1.968s 4 1.09x

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

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.445s (-46.9% 🟢) 1.007s (-1.6%) 0.562s 60 1.00x
🐘 Postgres Nitro 0.454s (-44.7% 🟢) 1.007s (~) 0.553s 60 1.02x
💻 Local Express 0.467s (-52.5% 🟢) 1.004s (-6.7% 🟢) 0.537s 60 1.05x
💻 Local Nitro 0.484s (-50.6% 🟢) 1.004s (-8.2% 🟢) 0.520s 60 1.09x
🐘 Postgres Next.js (Turbopack) 0.682s 1.006s 0.324s 60 1.53x
💻 Local Next.js (Turbopack) 0.722s 1.005s 0.283s 60 1.62x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 5.120s (-76.8% 🟢) 6.498s (-73.0% 🟢) 1.378s 10 1.00x
▲ Vercel Express 5.435s (-71.4% 🟢) 7.028s (-67.1% 🟢) 1.592s 9 1.06x
▲ Vercel Next.js (Turbopack) 5.606s (-61.3% 🟢) 7.350s (-54.3% 🟢) 1.744s 9 1.10x

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

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.080s (-44.0% 🟢) 1.772s (-15.6% 🟢) 0.692s 51 1.00x
🐘 Postgres Express 1.084s (-45.2% 🟢) 1.758s (-22.2% 🟢) 0.674s 52 1.00x
💻 Local Nitro 1.204s (-60.3% 🟢) 2.006s (-46.6% 🟢) 0.802s 45 1.11x
💻 Local Express 1.209s (-59.9% 🟢) 2.006s (-44.1% 🟢) 0.796s 45 1.12x
🐘 Postgres Next.js (Turbopack) 1.617s 2.007s 0.390s 45 1.50x
💻 Local Next.js (Turbopack) 1.771s 2.006s 0.235s 45 1.64x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 13.821s (-65.0% 🟢) 15.729s (-61.9% 🟢) 1.908s 6 1.00x
▲ Vercel Express 14.767s (-57.2% 🟢) 16.301s (-55.7% 🟢) 1.534s 6 1.07x
▲ Vercel Next.js (Turbopack) 15.580s (-68.7% 🟢) 17.541s (-66.1% 🟢) 1.961s 6 1.13x

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

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 2.125s (-46.7% 🟢) 2.798s (-36.0% 🟢) 0.673s 43 1.00x
🐘 Postgres Nitro 2.136s (-47.9% 🟢) 2.603s (-43.5% 🟢) 0.467s 47 1.01x
💻 Local Express 2.658s (-71.1% 🟢) 3.007s (-70.0% 🟢) 0.350s 40 1.25x
💻 Local Nitro 2.761s (-70.3% 🟢) 3.032s (-69.7% 🟢) 0.272s 40 1.30x
🐘 Postgres Next.js (Turbopack) 3.169s 4.009s 0.840s 30 1.49x
💻 Local Next.js (Turbopack) 3.782s 4.009s 0.227s 30 1.78x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 39.996s (-69.2% 🟢) 41.771s (-68.4% 🟢) 1.775s 3 1.00x
▲ Vercel Nitro 40.016s (-58.7% 🟢) 41.529s (-57.8% 🟢) 1.513s 3 1.00x
▲ Vercel Next.js (Turbopack) 43.928s (-59.0% 🟢) 46.238s (-57.5% 🟢) 2.310s 3 1.10x

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

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.185s (-34.6% 🟢) 1.006s (~) 0.821s 60 1.00x
🐘 Postgres Express 0.196s (-30.7% 🟢) 1.006s (~) 0.810s 60 1.06x
🐘 Postgres Next.js (Turbopack) 0.232s 1.006s 0.774s 60 1.25x
💻 Local Express 0.437s (-22.0% 🟢) 1.004s (~) 0.567s 60 2.36x
💻 Local Nitro 0.460s (-24.0% 🟢) 1.005s (-1.6%) 0.545s 60 2.48x
💻 Local Next.js (Turbopack) 0.566s 1.005s 0.438s 60 3.06x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.284s (+16.9% 🔺) 3.596s (-1.1%) 1.313s 17 1.00x
▲ Vercel Next.js (Turbopack) 2.291s (+13.3% 🔺) 3.898s (+2.8%) 1.607s 16 1.00x
▲ Vercel Nitro 2.436s (+46.6% 🔺) 3.847s (+14.8% 🔺) 1.411s 16 1.07x

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

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.316s (-38.0% 🟢) 1.006s (~) 0.690s 90 1.00x
🐘 Postgres Nitro 0.320s (-35.5% 🟢) 1.006s (~) 0.686s 90 1.01x
🐘 Postgres Next.js (Turbopack) 0.447s 1.006s 0.559s 90 1.41x
💻 Local Express 2.123s (-15.5% 🟢) 2.883s (-4.2%) 0.761s 32 6.71x
💻 Local Nitro 2.280s (-10.2% 🟢) 3.009s (~) 0.729s 30 7.21x
💻 Local Next.js (Turbopack) 2.433s 3.042s 0.609s 30 7.69x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 8.789s (+188.5% 🔺) 10.232s (+112.8% 🔺) 1.443s 10 1.00x
▲ Vercel Nitro 9.049s (+180.5% 🔺) 10.550s (+118.8% 🔺) 1.501s 9 1.03x
▲ Vercel Next.js (Turbopack) 10.148s (+187.0% 🔺) 11.869s (+128.6% 🔺) 1.721s 8 1.15x

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

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.647s (-21.0% 🟢) 1.006s (-1.1%) 0.359s 120 1.00x
🐘 Postgres Nitro 0.662s (-16.3% 🟢) 1.006s (~) 0.345s 120 1.02x
🐘 Postgres Next.js (Turbopack) 0.933s 1.208s 0.275s 100 1.44x
💻 Local Express 10.579s (-5.5% 🟢) 10.935s (-8.4% 🟢) 0.356s 11 16.35x
💻 Local Nitro 10.591s (-5.4% 🟢) 11.120s (-4.7%) 0.529s 11 16.37x
💻 Local Next.js (Turbopack) 11.705s 12.430s 0.725s 10 18.09x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 23.293s (+201.6% 🔺) 24.834s (+164.2% 🔺) 1.541s 6 1.00x
▲ Vercel Express 24.551s (+230.9% 🔺) 25.994s (+181.2% 🔺) 1.443s 5 1.05x
▲ Vercel Next.js (Turbopack) 27.378s (+165.1% 🔺) 29.607s (+141.0% 🔺) 2.229s 5 1.18x

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

Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 1.133s (+469.3% 🔺) 2.005s (+99.6% 🔺) 0.013s (+5.0%) 2.020s (+98.4% 🔺) 0.886s 10 1.00x
🐘 Postgres Express 1.138s (+455.0% 🔺) 1.996s (+99.9% 🔺) 0.001s (-18.8% 🟢) 2.011s (+98.9% 🔺) 0.873s 10 1.00x
🐘 Postgres Nitro 1.139s (+455.5% 🔺) 1.997s (+99.7% 🔺) 0.001s (-6.7% 🟢) 2.010s (+98.7% 🔺) 0.871s 10 1.00x
💻 Local Nitro 1.155s (+440.6% 🔺) 2.006s (+99.7% 🔺) 0.014s (+11.2% 🔺) 2.022s (+98.5% 🔺) 0.867s 10 1.02x
💻 Local Next.js (Turbopack) 1.196s 2.003s 0.012s 2.019s 0.823s 10 1.06x
🐘 Postgres Next.js (Turbopack) 1.197s 2.002s 0.001s 2.010s 0.813s 10 1.06x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.314s (-66.2% 🟢) 3.530s (-59.2% 🟢) 2.139s (+238.5% 🔺) 6.124s (-37.4% 🟢) 3.810s 10 1.00x
▲ Vercel Express 2.361s (-5.8% 🟢) 3.243s (-20.7% 🟢) 2.242s (+133.4% 🔺) 5.923s (+5.9% 🔺) 3.562s 10 1.02x
▲ Vercel Nitro 2.444s (-36.2% 🟢) 3.552s (-32.7% 🟢) 1.872s (+152.2% 🔺) 5.842s (-9.9% 🟢) 3.398s 10 1.06x

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

stream pipeline with 5 transform steps (1MB)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.499s (+140.2% 🔺) 2.004s (+99.1% 🔺) 0.003s (-23.4% 🟢) 2.021s (+97.7% 🔺) 0.522s 30 1.00x
🐘 Postgres Express 1.506s (+139.0% 🔺) 2.004s (+99.1% 🔺) 0.004s (+5.3% 🔺) 2.024s (+97.9% 🔺) 0.519s 30 1.00x
🐘 Postgres Next.js (Turbopack) 1.662s 2.009s 0.004s 2.025s 0.363s 30 1.11x
💻 Local Next.js (Turbopack) 1.682s 2.010s 0.011s 2.024s 0.342s 30 1.12x
💻 Local Nitro 1.725s (+105.6% 🔺) 2.012s (+98.8% 🔺) 0.009s (+0.6%) 2.202s (+97.4% 🔺) 0.478s 28 1.15x
💻 Local Express 1.731s (+128.6% 🔺) 2.012s (+95.5% 🔺) 0.010s (+10.0% 🔺) 2.203s (+111.9% 🔺) 0.472s 28 1.15x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 6.171s (-79.0% 🟢) 7.472s (-75.7% 🟢) 0.199s (+77.3% 🔺) 8.116s (-74.5% 🟢) 1.946s 8 1.00x
▲ Vercel Express 6.258s (-3.8%) 7.342s (-8.3% 🟢) 0.209s (-48.9% 🟢) 7.959s (-9.9% 🟢) 1.701s 8 1.01x
▲ Vercel Next.js (Turbopack) 6.441s (-61.9% 🟢) 7.955s (-56.4% 🟢) 0.226s (+7.0% 🔺) 8.652s (-54.3% 🟢) 2.211s 8 1.04x

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

10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.633s (-34.2% 🟢) 1.012s (-20.8% 🟢) 0.000s (-22.0% 🟢) 1.024s (-21.6% 🟢) 0.391s 59 1.00x
🐘 Postgres Nitro 0.682s (-29.6% 🟢) 1.047s (-16.1% 🟢) 0.000s (+68.4% 🔺) 1.060s (-15.7% 🟢) 0.379s 57 1.08x
🐘 Postgres Next.js (Turbopack) 0.816s 1.072s 0.000s 1.079s 0.263s 56 1.29x
💻 Local Nitro 1.343s (+9.8% 🔺) 2.015s (~) 0.000s (+133.3% 🔺) 2.017s (~) 0.674s 30 2.12x
💻 Local Express 1.390s (+13.5% 🔺) 2.016s (~) 0.001s (+60.0% 🔺) 2.019s (~) 0.629s 30 2.20x
💻 Local Next.js (Turbopack) 1.445s 2.014s 0.000s 2.017s 0.572s 30 2.28x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.893s (+4.1%) 4.975s (-2.5%) 0.000s (-54.2% 🟢) 5.432s (-1.8%) 1.539s 12 1.00x
▲ Vercel Nitro 3.951s (+29.5% 🔺) 5.074s (+15.5% 🔺) 0.000s (+18.2% 🔺) 5.524s (+14.9% 🔺) 1.573s 11 1.01x
▲ Vercel Next.js (Turbopack) 4.298s (-57.8% 🟢) 5.730s (-50.3% 🟢) 0.001s (+Infinity% 🔺) 6.196s (-48.6% 🟢) 1.898s 10 1.10x

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

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

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.329s (-25.8% 🟢) 2.026s (-5.4% 🟢) 0.000s (-6.7% 🟢) 2.046s (-5.9% 🟢) 0.717s 30 1.00x
🐘 Postgres Express 1.366s (-22.9% 🟢) 2.030s (-6.8% 🟢) 0.000s (+Infinity% 🔺) 2.072s (-5.8% 🟢) 0.706s 29 1.03x
🐘 Postgres Next.js (Turbopack) 1.672s 2.311s 0.000s 2.318s 0.646s 26 1.26x
💻 Local Next.js (Turbopack) 2.688s 3.184s 0.001s 3.192s 0.503s 19 2.02x
💻 Local Nitro 3.192s (-5.8% 🟢) 4.029s (~) 0.000s (-50.0% 🟢) 4.031s (~) 0.839s 15 2.40x
💻 Local Express 3.215s (-7.3% 🟢) 4.027s (~) 0.001s (-8.3% 🟢) 4.029s (~) 0.814s 15 2.42x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 5.992s (+6.7% 🔺) 7.458s (+6.8% 🔺) 0.000s (-100.0% 🟢) 7.919s (+5.0% 🔺) 1.927s 8 1.00x
▲ Vercel Express 58.169s (+1168.0% 🔺) 59.592s (+889.5% 🔺) 0.000s (+Infinity% 🔺) 59.986s (+829.0% 🔺) 1.817s 6 9.71x
▲ Vercel Nitro 82.550s (+1916.5% 🔺) 83.675s (+1457.0% 🔺) 0.001s (+358.3% 🔺) 84.133s (+1352.1% 🔺) 1.583s 4 13.78x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

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

Winner determined by most benchmark wins

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

Comment thread packages/web-shared/src/lib/utils.ts Outdated
…daries (e.g., "1000ms", "60s", "1m 60s") due to rounding overflow, violating its documented guarantee of never overstating duration.

This commit fixes the issue reported at packages/web-shared/src/lib/utils.ts:106

## Bug Analysis

The `formatDurationPrecise` function in `packages/web-shared/src/lib/utils.ts` has three rounding overflow bugs at unit boundaries:

1. **`ms = 999.5`**: The `ms < MS_IN_SECOND` branch uses `Math.round(ms)` which yields `1000`, producing `"1000ms"` — a value that should have been formatted as seconds. The original `formatDuration` function has a guard for this exact case, but `formatDurationPrecise` lacks it.

2. **`ms = 59999`** (59.999s): The `ms < MS_IN_MINUTE` branch computes `s = 59.999` then calls `s.toFixed(2)` which rounds to `"60.00"`. After trimming trailing zeros this becomes `"60s"` — overstating the value and producing an output that belongs in the next unit bracket.

3. **`ms = 119950`** (1m 59.95s): The `ms < MS_IN_HOUR` branch computes `s = 59.95` then calls `s.toFixed(1)` which rounds to `"60.0"`, producing `"1m 60s"` — a nonsensical time representation.

All three bugs were confirmed by executing the actual JavaScript logic and observing the incorrect outputs.

## Fix

The fix uses truncation (floor) instead of rounding throughout the function, consistent with its documented purpose of "never overstating the underlying duration":

1. Changed `Math.round(ms)` to `Math.floor(ms)` in the sub-second branch.
2. Added a `truncateToFixed(value, decimals)` helper that uses `Math.floor(value * 10^decimals) / 10^decimals` to truncate to a fixed number of decimal places without rounding up.
3. Replaced `s.toFixed(2)` and `s.toFixed(1)` calls with `truncateToFixed(s, 2)` and `truncateToFixed(s, 1)` respectively.

After the fix:
- `ms=999.5` → `"999ms"` (truncated, not rounded)
- `ms=59999` → `"59.99s"` (truncated to 2 decimal places)
- `ms=119950` → `"1m 59.9s"` (truncated to 1 decimal place)
- All normal cases continue to work correctly.

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: mitul-s <mitulxshah@gmail.com>
Per review feedback, expand the existing formatDuration() with a new
`precise` option rather than introducing formatDurationPrecise() as a
parallel API. The second arg now accepts either an options object
({ compact?, precise? }) or the original boolean shorthand for
backwards compatibility — all existing call sites that pass `true`
keep working unchanged.

The precise branch carries over the truncation semantics from the
upstream boundary-overflow fix (e7c5f12): sub-second values floor to
integer ms, sub-minute and sub-hour values truncate seconds via a
`truncateToFixed` helper so 999.5ms / 59.999s / 119.95s never roll
over into the next-larger unit.

Update the new-trace-viewer call sites to pass { precise: true } and
drop the formatDurationPrecise export. Default and compact behavior is
unchanged; the test file now also covers the legacy compact form and
the three boundary cases identified by the upstream fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants