Skip to content

Polyfill TC39 Uint8Array base64/hex methods in workflow VM context#1547

Merged
TooTallNate merged 5 commits into
mainfrom
nrajlich/uint8array-base64-polyfill
Mar 30, 2026
Merged

Polyfill TC39 Uint8Array base64/hex methods in workflow VM context#1547
TooTallNate merged 5 commits into
mainfrom
nrajlich/uint8array-base64-polyfill

Conversation

@TooTallNate
Copy link
Copy Markdown
Member

@TooTallNate TooTallNate commented Mar 29, 2026

Summary

  • Adds a polyfill for the TC39 Stage 4 Uint8Array base64/hex proposal to the workflow VM context
  • Implements Uint8Array.prototype.toBase64(), Uint8Array.prototype.toHex(), Uint8Array.fromBase64(), Uint8Array.fromHex(), Uint8Array.prototype.setFromBase64(), and Uint8Array.prototype.setFromHex() per the spec
  • Supports all spec options: alphabet ("base64" / "base64url"), omitPadding, and lastChunkHandling ("loose" / "strict" / "stop-before-partial")
  • 47 new unit tests covering all methods, options, error cases, and roundtrip encoding/decoding

The polyfill is a no-op when the native methods already exist, so it will gracefully step aside once Node.js ships native support.

@TooTallNate TooTallNate requested a review from a team as a code owner March 29, 2026 15:42
Copilot AI review requested due to automatic review settings March 29, 2026 15:42
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 29, 2026

🦋 Changeset detected

Latest commit: 6268d0a

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

This PR includes changesets to release 16 packages
Name Type
@workflow/core Patch
@workflow/builders Patch
@workflow/cli Patch
@workflow/next Patch
@workflow/nitro Patch
@workflow/vitest Patch
@workflow/web-shared Patch
workflow Patch
@workflow/world-testing Patch
@workflow/astro Patch
@workflow/nest Patch
@workflow/rollup Patch
@workflow/sveltekit Patch
@workflow/vite Patch
@workflow/nuxt Patch
@workflow/ai 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 Mar 29, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 29, 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 🥇 Express 0.037s (-17.5% 🟢) 1.006s (~) 0.969s 10 1.00x
💻 Local Nitro 0.046s (-12.5% 🟢) 1.005s (-1.0%) 0.959s 10 1.26x
💻 Local Next.js (Turbopack) 0.048s 1.005s 0.957s 10 1.31x
🌐 Redis Next.js (Turbopack) 0.054s 1.005s 0.951s 10 1.48x
🐘 Postgres Next.js (Turbopack) 0.063s 1.011s 0.948s 10 1.71x
🐘 Postgres Express 0.065s (~) 1.012s (~) 0.947s 10 1.77x
🐘 Postgres Nitro 0.066s (+2.7%) 1.012s (~) 0.946s 10 1.79x
🌐 MongoDB Next.js (Turbopack) 0.100s 1.007s 0.907s 10 2.72x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 0.479s (-23.9% 🟢) 2.672s (-3.9%) 2.193s 10 1.00x
▲ Vercel Express 0.606s (-23.6% 🟢) 2.537s (-17.3% 🟢) 1.931s 10 1.26x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Express

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 1.102s (-2.4%) 2.006s (~) 0.904s 10 1.00x
💻 Local Next.js (Turbopack) 1.119s 2.005s 0.886s 10 1.01x
🌐 Redis Next.js (Turbopack) 1.123s 2.006s 0.884s 10 1.02x
💻 Local Nitro 1.138s (~) 2.006s (~) 0.868s 10 1.03x
🐘 Postgres Next.js (Turbopack) 1.139s 2.012s 0.872s 10 1.03x
🐘 Postgres Nitro 1.151s (~) 2.013s (~) 0.862s 10 1.04x
🐘 Postgres Express 1.173s (+2.2%) 2.012s (~) 0.839s 10 1.06x
🌐 MongoDB Next.js (Turbopack) 1.306s 2.008s 0.702s 10 1.19x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.404s (+9.4% 🔺) 4.014s (-2.0%) 1.611s 10 1.00x
▲ Vercel Express 3.124s (+33.9% 🔺) 4.730s (+7.1% 🔺) 1.606s 10 1.30x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Express

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 10.653s (-2.4%) 11.023s (~) 0.370s 3 1.00x
🌐 Redis Next.js (Turbopack) 10.734s 11.023s 0.290s 3 1.01x
💻 Local Next.js (Turbopack) 10.765s 11.023s 0.258s 3 1.01x
🐘 Postgres Next.js (Turbopack) 10.900s 11.021s 0.121s 3 1.02x
🐘 Postgres Express 10.982s (+1.1%) 11.361s (+3.1%) 0.379s 3 1.03x
🐘 Postgres Nitro 10.994s (+0.8%) 11.355s (+3.0%) 0.362s 3 1.03x
💻 Local Nitro 11.012s (+0.7%) 11.693s (+6.1% 🔺) 0.681s 3 1.03x
🌐 MongoDB Next.js (Turbopack) 12.230s 13.022s 0.792s 3 1.15x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 20.933s (+15.6% 🔺) 22.769s (+10.5% 🔺) 1.837s 2 1.00x
▲ Vercel Express 29.033s (+30.1% 🔺) 30.826s (+27.4% 🔺) 1.793s 1 1.39x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Express

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 14.229s (-4.9%) 15.030s (~) 0.801s 4 1.00x
🌐 Redis Next.js (Turbopack) 14.288s 15.028s 0.740s 4 1.00x
🐘 Postgres Next.js (Turbopack) 14.475s 15.029s 0.554s 4 1.02x
🐘 Postgres Nitro 14.527s (~) 15.023s (~) 0.496s 4 1.02x
💻 Local Next.js (Turbopack) 14.621s 15.029s 0.408s 4 1.03x
🐘 Postgres Express 14.773s (+1.8%) 15.277s (+1.7%) 0.504s 4 1.04x
💻 Local Nitro 15.082s (+1.1%) 15.781s (+5.0% 🔺) 0.700s 4 1.06x
🌐 MongoDB Next.js (Turbopack) 17.870s 18.030s 0.160s 4 1.26x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 44.041s (+30.4% 🔺) 45.610s (+25.6% 🔺) 1.568s 2 1.00x
▲ Vercel Next.js (Turbopack) 46.995s (+34.3% 🔺) 48.958s (+29.8% 🔺) 1.963s 2 1.07x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 13.482s 14.025s 0.544s 7 1.00x
🐘 Postgres Next.js (Turbopack) 13.885s 14.023s 0.138s 7 1.03x
🐘 Postgres Nitro 14.165s (+1.3%) 15.025s (+4.0%) 0.860s 6 1.05x
🐘 Postgres Express 14.484s (+4.2%) 15.035s (+7.2% 🔺) 0.552s 6 1.07x
💻 Local Express 14.804s (-11.0% 🟢) 15.028s (-11.8% 🟢) 0.224s 6 1.10x
💻 Local Next.js (Turbopack) 15.571s 16.029s 0.458s 6 1.15x
💻 Local Nitro 17.087s (+4.3%) 17.365s (+2.0%) 0.279s 6 1.27x
🌐 MongoDB Next.js (Turbopack) 20.386s 21.027s 0.641s 5 1.51x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 73.892s (+27.2% 🔺) 76.755s (+27.5% 🔺) 2.863s 2 1.00x
▲ Vercel Next.js (Turbopack) 75.921s (+15.2% 🔺) 77.900s (+15.2% 🔺) 1.978s 2 1.03x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.250s 2.012s 0.762s 15 1.00x
🐘 Postgres Nitro 1.274s (~) 2.012s (~) 0.738s 15 1.02x
🐘 Postgres Express 1.291s (+1.3%) 2.011s (~) 0.720s 15 1.03x
🌐 Redis Next.js (Turbopack) 1.355s 2.007s 0.651s 15 1.08x
💻 Local Express 1.469s (-5.3% 🟢) 2.006s (~) 0.536s 15 1.18x
💻 Local Nitro 1.501s (-1.4%) 2.006s (~) 0.506s 15 1.20x
💻 Local Next.js (Turbopack) 1.502s 2.006s 0.504s 15 1.20x
🌐 MongoDB Next.js (Turbopack) 2.148s 3.009s 0.861s 10 1.72x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.419s (-12.8% 🟢) 3.791s (-16.0% 🟢) 1.372s 9 1.00x
▲ Vercel Express 2.643s (+10.3% 🔺) 4.289s (+3.4%) 1.646s 7 1.09x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Express

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 2.357s (+1.6%) 3.010s (~) 0.652s 10 1.00x
🐘 Postgres Express 2.370s (+0.7%) 3.013s (~) 0.643s 10 1.01x
🐘 Postgres Next.js (Turbopack) 2.443s 3.011s 0.568s 10 1.04x
🌐 Redis Next.js (Turbopack) 2.564s 3.008s 0.445s 10 1.09x
💻 Local Express 2.694s (-7.8% 🟢) 3.108s (-7.0% 🟢) 0.414s 10 1.14x
💻 Local Next.js (Turbopack) 2.749s 3.008s 0.258s 10 1.17x
💻 Local Nitro 3.051s (+4.8%) 3.676s (+14.6% 🔺) 0.626s 9 1.29x
🌐 MongoDB Next.js (Turbopack) 4.686s 5.178s 0.492s 6 1.99x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.655s (~) 4.141s (-3.4%) 1.486s 8 1.00x
▲ Vercel Express 3.020s (-2.5%) 4.590s (-5.1% 🟢) 1.570s 7 1.14x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Express

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 3.497s (+0.5%) 4.011s (~) 0.514s 8 1.00x
🐘 Postgres Express 3.517s (+0.5%) 4.012s (~) 0.496s 8 1.01x
🐘 Postgres Next.js (Turbopack) 3.660s 4.137s 0.477s 8 1.05x
🌐 Redis Next.js (Turbopack) 4.199s 4.868s 0.669s 7 1.20x
💻 Local Next.js (Turbopack) 6.704s 7.767s 1.063s 4 1.92x
💻 Local Express 7.344s (-8.7% 🟢) 8.019s (-8.6% 🟢) 0.675s 4 2.10x
💻 Local Nitro 8.343s (+10.2% 🔺) 9.020s (+12.5% 🔺) 0.678s 4 2.39x
🌐 MongoDB Next.js (Turbopack) 9.976s 10.685s 0.710s 3 2.85x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.319s (-2.9%) 5.424s (+5.4% 🔺) 2.105s 6 1.00x
▲ Vercel Next.js (Turbopack) 3.369s (-13.7% 🟢) 4.766s (-13.4% 🟢) 1.397s 7 1.02x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Express | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.242s 2.010s 0.768s 15 1.00x
🐘 Postgres Nitro 1.257s (-0.8%) 2.010s (~) 0.753s 15 1.01x
🐘 Postgres Express 1.283s (+2.3%) 2.012s (~) 0.729s 15 1.03x
🌐 Redis Next.js (Turbopack) 1.328s 2.006s 0.678s 15 1.07x
💻 Local Next.js (Turbopack) 1.468s 2.006s 0.538s 15 1.18x
💻 Local Express 1.514s (-0.7%) 2.006s (~) 0.492s 15 1.22x
💻 Local Nitro 1.542s (+0.9%) 2.005s (~) 0.464s 15 1.24x
🌐 MongoDB Next.js (Turbopack) 2.166s 3.009s 0.843s 10 1.74x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.330s (-10.6% 🟢) 4.134s (-6.9% 🟢) 1.803s 8 1.00x
▲ Vercel Next.js (Turbopack) 4.256s (+29.3% 🔺) 5.722s (+12.5% 🔺) 1.466s 6 1.83x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Express | Next.js (Turbopack)

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 2.327s (-1.5%) 3.012s (~) 0.685s 10 1.00x
🐘 Postgres Express 2.383s (+0.9%) 3.013s (~) 0.630s 10 1.02x
🐘 Postgres Next.js (Turbopack) 2.437s 3.008s 0.571s 10 1.05x
🌐 Redis Next.js (Turbopack) 2.550s 3.008s 0.458s 10 1.10x
💻 Local Express 2.731s (-10.3% 🟢) 3.007s (-22.6% 🟢) 0.276s 10 1.17x
💻 Local Next.js (Turbopack) 2.742s 3.007s 0.265s 10 1.18x
💻 Local Nitro 2.982s (+6.0% 🔺) 3.886s (+21.2% 🔺) 0.905s 8 1.28x
🌐 MongoDB Next.js (Turbopack) 4.676s 5.177s 0.501s 6 2.01x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.968s (-40.9% 🟢) 6.068s (-28.6% 🟢) 2.100s 5 1.00x
▲ Vercel Next.js (Turbopack) 4.066s (+51.2% 🔺) 5.828s (+33.3% 🔺) 1.762s 6 1.02x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Express | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 3.509s (+1.8%) 4.011s (~) 0.502s 8 1.00x
🐘 Postgres Express 3.518s (+1.8%) 4.015s (~) 0.498s 8 1.00x
🐘 Postgres Next.js (Turbopack) 3.641s 4.012s 0.371s 8 1.04x
🌐 Redis Next.js (Turbopack) 4.255s 5.011s 0.756s 6 1.21x
💻 Local Express 7.631s (-11.9% 🟢) 8.015s (-11.2% 🟢) 0.384s 4 2.17x
💻 Local Next.js (Turbopack) 8.134s 8.518s 0.384s 4 2.32x
💻 Local Nitro 9.073s (+15.2% 🔺) 9.272s (+8.9% 🔺) 0.199s 4 2.59x
🌐 MongoDB Next.js (Turbopack) 10.074s 10.689s 0.616s 3 2.87x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.880s (-22.7% 🟢) 5.737s (-14.0% 🟢) 1.857s 6 1.00x
▲ Vercel Next.js (Turbopack) 4.571s (+10.8% 🔺) 6.014s (+1.7%) 1.443s 5 1.18x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 0.686s 1.005s 0.318s 60 1.00x
💻 Local Express 0.690s (-29.9% 🟢) 1.004s (-10.0% 🟢) 0.314s 60 1.01x
🐘 Postgres Next.js (Turbopack) 0.753s 1.007s 0.254s 60 1.10x
💻 Local Next.js (Turbopack) 0.845s 1.039s 0.194s 58 1.23x
🐘 Postgres Nitro 0.863s (+6.1% 🔺) 1.008s (~) 0.145s 60 1.26x
🐘 Postgres Express 0.938s (+13.6% 🔺) 1.229s (+22.0% 🔺) 0.291s 50 1.37x
💻 Local Nitro 0.998s (-1.6%) 1.424s (-17.2% 🟢) 0.426s 43 1.45x
🌐 MongoDB Next.js (Turbopack) 2.161s 3.009s 0.848s 20 3.15x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 13.918s (+26.5% 🔺) 16.175s (+23.8% 🔺) 2.258s 4 1.00x
▲ Vercel Next.js (Turbopack) 16.336s (+59.5% 🔺) 18.396s (+53.0% 🔺) 2.059s 4 1.17x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 1.657s 2.028s 0.371s 45 1.00x
🐘 Postgres Next.js (Turbopack) 1.860s 2.054s 0.194s 44 1.12x
🐘 Postgres Nitro 2.082s (+9.4% 🔺) 2.979s (+46.8% 🔺) 0.897s 31 1.26x
🐘 Postgres Express 2.148s (+7.4% 🔺) 2.737s (+18.2% 🔺) 0.589s 33 1.30x
💻 Local Express 2.233s (-26.0% 🟢) 3.007s (-20.9% 🟢) 0.774s 30 1.35x
💻 Local Next.js (Turbopack) 2.616s 3.008s 0.392s 30 1.58x
💻 Local Nitro 3.079s (+1.0%) 3.966s (+6.4% 🔺) 0.888s 23 1.86x
🌐 MongoDB Next.js (Turbopack) 5.234s 6.012s 0.778s 15 3.16x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 40.608s (+20.1% 🔺) 42.326s (+18.3% 🔺) 1.718s 3 1.00x
▲ Vercel Next.js (Turbopack) 40.665s (+17.6% 🔺) 42.276s (+14.4% 🔺) 1.611s 3 1.00x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 3.282s 4.009s 0.726s 30 1.00x
🐘 Postgres Next.js (Turbopack) 3.716s 4.010s 0.294s 30 1.13x
🐘 Postgres Nitro 4.216s (+9.7% 🔺) 5.013s (+23.0% 🔺) 0.796s 24 1.28x
🐘 Postgres Express 4.491s (+10.6% 🔺) 4.973s (+8.9% 🔺) 0.483s 25 1.37x
💻 Local Express 7.338s (-19.7% 🟢) 7.953s (-18.7% 🟢) 0.615s 16 2.24x
💻 Local Next.js (Turbopack) 8.432s 9.017s 0.585s 14 2.57x
💻 Local Nitro 9.173s (+2.9%) 10.019s (+7.4% 🔺) 0.846s 12 2.79x
🌐 MongoDB Next.js (Turbopack) 10.642s 11.021s 0.379s 11 3.24x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 86.640s (-4.2%) 88.497s (-4.7%) 1.857s 2 1.00x
▲ Vercel Next.js (Turbopack) 90.525s (-4.2%) 92.098s (-5.5% 🟢) 1.573s 2 1.04x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.255s 1.008s 0.753s 60 1.00x
🐘 Postgres Nitro 0.287s (~) 1.008s (~) 0.721s 60 1.13x
🐘 Postgres Express 0.308s (+9.3% 🔺) 1.009s (~) 0.701s 60 1.21x
🌐 Redis Next.js (Turbopack) 0.382s 1.004s 0.623s 60 1.50x
💻 Local Next.js (Turbopack) 0.532s 1.004s 0.472s 60 2.09x
💻 Local Nitro 0.562s (-0.5%) 1.005s (~) 0.442s 60 2.20x
💻 Local Express 0.576s (-1.9%) 1.004s (~) 0.428s 60 2.26x
🌐 MongoDB Next.js (Turbopack) 1.626s 2.150s 0.524s 28 6.38x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 1.975s (-3.4%) 3.812s (-5.6% 🟢) 1.837s 16 1.00x
▲ Vercel Express 2.010s (+0.7%) 3.972s (+3.7%) 1.962s 16 1.02x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.480s 1.008s 0.527s 90 1.00x
🐘 Postgres Nitro 0.527s (+3.7%) 1.008s (~) 0.480s 90 1.10x
🐘 Postgres Express 0.537s (+7.0% 🔺) 1.008s (~) 0.472s 90 1.12x
🌐 Redis Next.js (Turbopack) 1.169s 2.006s 0.837s 45 2.43x
💻 Local Next.js (Turbopack) 2.424s 3.009s 0.585s 30 5.04x
💻 Local Express 2.448s (-4.6%) 3.008s (~) 0.560s 30 5.09x
💻 Local Nitro 2.504s (+8.0% 🔺) 3.009s (~) 0.505s 30 5.21x
🌐 MongoDB Next.js (Turbopack) 4.703s 5.177s 0.473s 18 9.79x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.118s (+8.1% 🔺) 4.878s (+5.9% 🔺) 1.760s 19 1.00x
▲ Vercel Next.js (Turbopack) 3.732s (-3.5%) 5.303s (-4.7%) 1.571s 17 1.20x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.773s 1.008s 0.235s 120 1.00x
🐘 Postgres Nitro 0.811s (+4.7%) 1.010s (~) 0.198s 119 1.05x
🐘 Postgres Express 0.827s (+4.2%) 1.102s (+9.3% 🔺) 0.275s 109 1.07x
🌐 Redis Next.js (Turbopack) 2.743s 3.084s 0.341s 39 3.55x
💻 Local Next.js (Turbopack) 9.967s 10.610s 0.643s 12 12.90x
🌐 MongoDB Next.js (Turbopack) 9.993s 10.515s 0.522s 12 12.93x
💻 Local Express 10.516s (-6.2% 🟢) 11.025s (-7.6% 🟢) 0.508s 11 13.61x
💻 Local Nitro 11.340s (+9.5% 🔺) 12.031s (+10.8% 🔺) 0.691s 10 14.68x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 7.186s (+4.1%) 8.725s (-0.7%) 1.539s 14 1.00x
▲ Vercel Express 7.962s (-7.4% 🟢) 9.627s (-10.3% 🟢) 1.666s 14 1.11x
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Express

Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 0.147s (-28.7% 🟢) 1.003s (~) 0.010s (-13.5% 🟢) 1.015s (~) 0.868s 10 1.00x
💻 Local Next.js (Turbopack) 0.169s 1.001s 0.010s 1.016s 0.847s 10 1.15x
🌐 Redis Next.js (Turbopack) 0.188s 1.001s 0.002s 1.008s 0.819s 10 1.28x
🐘 Postgres Express 0.201s (-0.9%) 0.994s (~) 0.001s (+16.7% 🔺) 1.010s (~) 0.809s 10 1.37x
💻 Local Nitro 0.202s (-3.0%) 1.003s (~) 0.012s (+24.5% 🔺) 1.018s (~) 0.816s 10 1.37x
🐘 Postgres Next.js (Turbopack) 0.204s 1.000s 0.001s 1.011s 0.808s 10 1.39x
🐘 Postgres Nitro 0.219s (+6.2% 🔺) 0.995s (~) 0.001s (~) 1.010s (~) 0.791s 10 1.49x
🌐 MongoDB Next.js (Turbopack) 0.489s 0.965s 0.002s 1.009s 0.520s 10 3.33x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 1.744s (-1.0%) 2.982s (+0.5%) 0.387s (+13.8% 🔺) 4.088s (+0.5%) 2.344s 10 1.00x
▲ Vercel Next.js (Turbopack) 2.014s (+22.4% 🔺) 3.339s (+14.7% 🔺) 0.229s (-31.9% 🟢) 4.147s (+5.1% 🔺) 2.133s 10 1.15x
▲ Vercel Nitro ⚠️ missing - - - - -

🔍 Observability: Express | Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 0.484s 1.000s 0.003s 1.011s 0.527s 60 1.00x
💻 Local Express 0.581s (-19.6% 🟢) 1.009s (~) 0.010s (+8.4% 🔺) 1.023s (~) 0.442s 59 1.20x
🐘 Postgres Next.js (Turbopack) 0.613s 1.008s 0.004s 1.024s 0.410s 59 1.27x
🐘 Postgres Nitro 0.622s (+3.1%) 1.004s (~) 0.004s (-8.6% 🟢) 1.024s (~) 0.402s 59 1.28x
🐘 Postgres Express 0.648s (+5.1% 🔺) 1.000s (~) 0.008s (+71.3% 🔺) 1.029s (+0.5%) 0.381s 59 1.34x
💻 Local Next.js (Turbopack) 0.665s 1.008s 0.008s 1.022s 0.358s 59 1.37x
💻 Local Nitro 0.737s (-2.2%) 1.009s (~) 0.010s (-0.7%) 1.023s (~) 0.286s 59 1.52x
🌐 MongoDB Next.js (Turbopack) 1.323s 1.954s 0.003s 2.013s 0.690s 30 2.73x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 5.652s (+34.0% 🔺) 7.347s (+30.9% 🔺) 0.379s (+5.4% 🔺) 8.338s (+23.7% 🔺) 2.686s 8 1.00x
▲ Vercel Next.js (Turbopack) 11.442s (+138.4% 🔺) 12.423s (+104.2% 🔺) 0.191s (-64.6% 🟢) 13.359s (+80.4% 🔺) 1.916s 5 2.02x
▲ Vercel Nitro ⚠️ missing - - - - -

🔍 Observability: Express | Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 0.896s 1.017s 0.000s 1.021s 0.126s 59 1.00x
🐘 Postgres Nitro 0.931s (-3.7%) 1.124s (-3.4%) 0.000s (-100.0% 🟢) 1.142s (-4.8%) 0.211s 53 1.04x
🐘 Postgres Next.js (Turbopack) 0.987s 1.305s 0.000s 1.314s 0.327s 46 1.10x
🐘 Postgres Express 1.015s (+3.2%) 1.388s (+11.8% 🔺) 0.000s (+11.6% 🔺) 1.406s (+11.6% 🔺) 0.391s 43 1.13x
💻 Local Express 1.176s (-6.6% 🟢) 2.017s (~) 0.000s (-33.3% 🟢) 2.020s (~) 0.845s 30 1.31x
💻 Local Next.js (Turbopack) 1.249s 2.019s 0.000s 2.023s 0.774s 30 1.39x
💻 Local Nitro 1.288s (+5.4% 🔺) 2.022s (~) 0.001s (+36.4% 🔺) 2.025s (~) 0.737s 30 1.44x
🌐 MongoDB Next.js (Turbopack) 2.348s 2.957s 0.000s 3.008s 0.660s 20 2.62x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.842s (-15.5% 🟢) 4.547s (+2.5%) 0.000s (-100.0% 🟢) 5.208s (~) 2.366s 12 1.00x
▲ Vercel Next.js (Turbopack) 3.912s (+17.5% 🔺) 4.962s (+10.4% 🔺) 0.000s (-100.0% 🟢) 5.718s (+9.9% 🔺) 1.805s 11 1.38x
▲ Vercel Nitro ⚠️ missing - - - - -

🔍 Observability: Express | Next.js (Turbopack)

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

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 1.600s 2.001s 0.000s 2.006s 0.406s 30 1.00x
🐘 Postgres Nitro 1.788s (+2.2%) 2.140s (+2.0%) 0.000s (-65.5% 🟢) 2.153s (+1.9%) 0.364s 28 1.12x
🐘 Postgres Express 1.880s (+3.9%) 2.302s (+7.8% 🔺) 0.000s (+11.5% 🔺) 2.320s (+6.8% 🔺) 0.440s 26 1.18x
🐘 Postgres Next.js (Turbopack) 1.923s 2.181s 0.000s 2.190s 0.267s 28 1.20x
💻 Local Next.js (Turbopack) 3.522s 4.100s 0.001s 4.105s 0.584s 15 2.20x
💻 Local Nitro 3.575s (+5.2% 🔺) 4.032s (+1.6%) 0.001s (-5.2% 🟢) 4.037s (+1.5%) 0.462s 15 2.23x
💻 Local Express 3.587s (-1.6%) 4.097s (-1.6%) 0.000s (-44.4% 🟢) 4.101s (-1.6%) 0.514s 15 2.24x
🌐 MongoDB Next.js (Turbopack) 4.381s 4.967s 0.000s 5.010s 0.629s 12 2.74x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 4.269s (-10.8% 🟢) 5.344s (-10.4% 🟢) 0.000s (NaN%) 6.047s (-10.2% 🟢) 1.778s 10 1.00x
▲ Vercel Next.js (Turbopack) 4.553s (-7.4% 🟢) 5.777s (-5.0% 🟢) 0.000s (-85.0% 🟢) 6.358s (-7.5% 🟢) 1.806s 10 1.07x
▲ Vercel Nitro ⚠️ missing - - - - -

🔍 Observability: Express | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

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

Winner determined by most benchmark wins

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


Some benchmark jobs failed:

  • Local: success
  • Postgres: success
  • Vercel: failure

Check the workflow run for details.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 29, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 846 0 67 913
✅ 💻 Local Development 818 0 178 996
✅ 📦 Local Production 818 0 178 996
✅ 🐘 Local Postgres 818 0 178 996
✅ 🪟 Windows 75 0 8 83
❌ 🌍 Community Worlds 130 59 24 213
✅ 📋 Other 207 0 42 249
Total 3712 59 675 4446

❌ Failed Tests

🌍 Community Worlds (59 failed)

mongodb (3 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KN0018C39PVS2QACS66W650P
  • webhookWorkflow | wrun_01KN001JCXXXJAWGSH8SJVZ4AE
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KN00ADCQ43HBWN28DVK4N1E0

redis (2 failed):

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

turso (54 failed):

  • addTenWorkflow | wrun_01KMZZZSW6PFQ1TTM316MMWV4K
  • addTenWorkflow | wrun_01KMZZZSW6PFQ1TTM316MMWV4K
  • wellKnownAgentWorkflow (.well-known/agent) | wrun_01KN001QJ879GZDNYVZPB808S4
  • should work with react rendering in step
  • promiseAllWorkflow | wrun_01KN000590PRYXNHBTG0QJQMRP
  • promiseRaceWorkflow | wrun_01KN000C5BGW8MWHQ5M2FSDZNZ
  • promiseAnyWorkflow | wrun_01KN000EN386WYFZMTD772PG8A
  • importedStepOnlyWorkflow | wrun_01KN0023DC4ZW6QKPT3VSC343K
  • hookWorkflow | wrun_01KN000WX75Q0CD4CW5GQ3Q5MC
  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KN0018C39PVS2QACS66W650P
  • webhookWorkflow | wrun_01KN001JCXXXJAWGSH8SJVZ4AE
  • sleepingWorkflow | wrun_01KN001Z6QJMCE283KSDZ21CQ7
  • parallelSleepWorkflow | wrun_01KN002CBN1FVSMAVGDPQR9EC3
  • nullByteWorkflow | wrun_01KN002FY74EJ584PYP8G5TYNN
  • workflowAndStepMetadataWorkflow | wrun_01KN002R226S2R7JXJ8MWVQJ4V
  • fetchWorkflow | wrun_01KN005TP50NQ330NMDNBJQZ3B
  • promiseRaceStressTestWorkflow | wrun_01KN005Y92Z084T5EWNVPQXASC
  • error handling error propagation workflow errors nested function calls preserve message and stack trace
  • error handling error propagation workflow errors cross-file imports preserve message and stack trace
  • error handling error propagation step errors basic step error preserves message and stack trace
  • error handling error propagation step errors cross-file step error preserves message and function names in stack
  • error handling retry behavior regular Error retries until success
  • error handling retry behavior FatalError fails immediately without retries
  • error handling retry behavior RetryableError respects custom retryAfter delay
  • error handling retry behavior maxRetries=0 disables retries
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • error handling not registered WorkflowNotRegisteredError fails the run when workflow does not exist
  • error handling not registered StepNotRegisteredError fails the step but workflow can catch it
  • error handling not registered StepNotRegisteredError fails the run when not caught in workflow
  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_01KN009RB331JC41S4ZTFK7S2R
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KN00ADCQ43HBWN28DVK4N1E0
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_01KN00B6WEEVA9KTZ27QZD332B
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_01KN00BVP9KC2RY4E7S5TRC4XY
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_01KN00C52CHFW0141BV5C4QANC
  • closureVariableWorkflow - nested step functions with closure variables | wrun_01KN00CCZXH6FN06MMYTCS6QZ7
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_01KN00CH68NCVHENWX1475ZNWJ
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_01KN00D10WWJM6AJ2AYRFZJK3R
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_01KN00D6YBE2YHCAM7EEGH3JPV
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_01KN00DEDBKSCTAQ7M8Y0D9T52
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_01KN00DNCMX8Z13DK0KE4JARXZ
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_01KN00DW54NBZJWEXZ6B2M8KKM
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_01KN00E2YTY9ZQ3MBNR3MW2EDK
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_01KN00EB172B048SPJNRCAK78V
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_01KN00EQ741KK7HEV6MYP11TM7
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_01KN00EZSPTACNHGGZ36KKBVZ7
  • cancelRun - cancelling a running workflow | wrun_01KN00FAED77FRCMY0JG5G796P
  • cancelRun via CLI - cancelling a running workflow | wrun_01KN00FKWCRKPNDP0C9CM306WK
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router
  • hookWithSleepWorkflow - hook payloads delivered correctly with concurrent sleep | wrun_01KN00G0N34182Y98JGZZ43657
  • sleepInLoopWorkflow - sleep inside loop with steps actually delays each iteration | wrun_01KN00GRA6BD29DEBMC2M2NSC9
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_01KN00H2P8YAE6BE7C8GE8RA4Z

Details by Category

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

📋 View full workflow run

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a TC39 Stage 4 Uint8Array base64/hex polyfill to the workflow VM sandbox and removes the Node-specific Buffer global from the VM context, aligning the sandbox with web-standard APIs while keeping encoding/decoding capabilities.

Changes:

  • Introduces installUint8ArrayBase64() to polyfill Uint8Array base64/hex encode/decode methods inside the VM context.
  • Updates VM context initialization to install the polyfill and stop injecting Buffer.
  • Adds a comprehensive unit test suite for the new methods/options and removes VM Buffer-specific tests.

Reviewed changes

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

Show a summary per file
File Description
packages/core/src/vm/uint8array-base64.ts Implements the base64/hex polyfill and installer for a provided VM Uint8Array constructor.
packages/core/src/vm/uint8array-base64.test.ts Adds unit tests covering encoding/decoding, options, and error cases in a VM context.
packages/core/src/vm/index.ts Installs the polyfill into the VM context and removes Buffer injection.
packages/core/src/vm/index.test.ts Removes tests asserting Buffer availability/usage inside the VM.
.changeset/uint8array-base64-polyfill.md Publishes a patch changeset describing the VM polyfill + Buffer removal.

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

Comment thread packages/core/src/vm/uint8array-base64.ts Outdated
Comment on lines +447 to +458
const result = fromBase64(
str,
alphabet,
lastChunkHandling,
this.length
);
const bytes = result.bytes;
const written = bytes.length;

for (let i = 0; i < written; i++) {
this[i] = bytes[i];
}
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

setFromBase64() currently decodes into an intermediate number[] (result.bytes) and then copies into the target array. This defeats the main benefit of setFromBase64 (decoding directly into an existing buffer) and can be very memory/GC heavy for large inputs. Consider changing the decoder to write directly into this (or into a preallocated Uint8Array) while tracking {read,written}, so the peak memory stays O(1) relative to the output size.

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

Choose a reason for hiding this comment

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

Acknowledged. The intermediate number[] is a fair tradeoff here — workflow functions are orchestration logic, not data processing pipelines, so the inputs to these methods will be small (auth tokens, config values, etc.). Rewriting fromBase64 to decode directly into a target buffer would add significant complexity to the spec-compliant state machine (whitespace skipping, lastChunkHandling, maxLength truncation at chunk boundaries) for negligible real-world benefit in this context. If we ever see large payloads going through the workflow VM, we can revisit.

Comment on lines +486 to +498
const result = fromHex(str, this.length);
const bytes = result.bytes;
const written = bytes.length;

for (let i = 0; i < written; i++) {
this[i] = bytes[i];
}

if (result.error) {
throw result.error;
}

return { read: result.read, written };
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

setFromHex() has the same issue as setFromBase64(): it builds a number[] and then copies into the target buffer. For larger hex payloads this adds significant overhead and undermines the purpose of a setFrom* API. Consider decoding directly into this while advancing read/written counters, avoiding the intermediate array.

Suggested change
const result = fromHex(str, this.length);
const bytes = result.bytes;
const written = bytes.length;
for (let i = 0; i < written; i++) {
this[i] = bytes[i];
}
if (result.error) {
throw result.error;
}
return { read: result.read, written };
const maxBytes = this.length;
const strLen = str.length;
let read = 0;
let written = 0;
// Decode two hex characters (one byte) at a time directly into `this`.
while (written < maxBytes && read + 1 < strLen) {
const hiCode = str.charCodeAt(read);
const loCode = str.charCodeAt(read + 1);
const hi =
hiCode >= 48 && hiCode <= 57
? hiCode - 48
: hiCode >= 65 && hiCode <= 70
? hiCode - 55
: hiCode >= 97 && hiCode <= 102
? hiCode - 87
: -1;
const lo =
loCode >= 48 && loCode <= 57
? loCode - 48
: loCode >= 65 && loCode <= 70
? loCode - 55
: loCode >= 97 && loCode <= 102
? loCode - 87
: -1;
if (hi === -1 || lo === -1) {
throw new TypeError('Invalid hex string');
}
this[written] = (hi << 4) | lo;
written++;
read += 2;
}
// If there's a trailing single hex digit, treat it as invalid.
if (read < strLen && (strLen - read) === 1) {
throw new TypeError('Invalid hex string');
}
return { read, written };

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

Choose a reason for hiding this comment

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

Same reasoning as the setFromBase64 comment above — the intermediate array is a reasonable tradeoff given the small payloads in workflow VM context. Also worth noting: the suggested replacement changes error types from SyntaxError (per TC39 spec) to TypeError, and drops the upfront odd-length check that the spec requires. Keeping the current implementation for spec fidelity.

Comment thread .changeset/uint8array-base64-polyfill.md
@ghost ghost mentioned this pull request Mar 30, 2026
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.

4 participants