Skip to content

Remove "workflow/internal/serialization" export - #1082

Merged
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export
Feb 16, 2026
Merged

Remove "workflow/internal/serialization" export#1082
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export

Conversation

@TooTallNate

Copy link
Copy Markdown
Member

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.
@changeset-bot

changeset-botBot commented Feb 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: aa8c9bd

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

This PR includes changesets to release 14 packages
NameType
workflowPatch
@workflow/world-testingPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@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 Feb 16, 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🥇 Express0.032s (-6.9% 🟢)1.005s (~)0.973s101.00x
💻 LocalNitro0.032s (+2.5%)1.005s (~)0.972s101.00x
💻 LocalNext.js (Turbopack)0.043s1.005s0.962s101.33x
🌐 RedisNext.js (Turbopack)0.056s1.005s0.949s101.74x
🌐 MongoDBNext.js (Turbopack)0.103s1.007s0.904s103.21x
🐘 PostgresExpress0.155s (-23.4% 🟢)1.010s (-0.8%)0.854s104.82x
🐘 PostgresNitro0.251s (+10.8% 🔺)1.010s (-0.9%)0.758s107.81x
🐘 PostgresNext.js (Turbopack)0.370s1.009s0.639s1011.50x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.544s (-14.7% 🟢)1.950s (-9.5% 🟢)1.405s101.00x
▲ VercelNext.js (Turbopack)0.597s (-27.3% 🟢)1.924s (-10.9% 🟢)1.327s101.10x
▲ VercelExpress0.654s (+8.9% 🔺)2.105s (+2.0%)1.451s101.20x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.096s2.006s0.910s101.00x
💻 LocalExpress1.105s (~)2.006s (~)0.901s101.01x
💻 LocalNitro1.106s (~)2.006s (~)0.901s101.01x
🌐 RedisNext.js (Turbopack)1.130s2.007s0.877s101.03x
🌐 MongoDBNext.js (Turbopack)1.310s2.008s0.698s101.20x
🐘 PostgresNext.js (Turbopack)2.254s3.015s0.761s102.06x
🐘 PostgresExpress2.376s (-3.5%)3.014s (~)0.638s102.17x
🐘 PostgresNitro2.425s (+6.8% 🔺)3.014s (+7.1% 🔺)0.590s102.21x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.276s (-5.5% 🟢)3.134s (-10.2% 🟢)0.858s101.00x
▲ VercelNitro2.337s (+1.7%)3.339s (-8.6% 🟢)1.003s101.03x
▲ VercelExpress2.746s (+21.6% 🔺)3.740s (+11.4% 🔺)0.995s101.21x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.745s11.021s0.276s31.00x
💻 LocalExpress10.822s (-0.6%)11.021s (~)0.199s31.01x
🌐 RedisNext.js (Turbopack)10.831s11.026s0.195s31.01x
💻 LocalNitro10.838s (~)11.023s (~)0.185s31.01x
🌐 MongoDBNext.js (Turbopack)12.321s13.025s0.704s31.15x
🐘 PostgresNext.js (Turbopack)15.142s16.046s0.904s21.41x
🐘 PostgresNitro20.293s (+33.2% 🔺)21.058s (+31.2% 🔺)0.766s21.89x
🐘 PostgresExpress20.404s (+6.9% 🔺)21.059s (+7.7% 🔺)0.656s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)17.672s (-0.7%)18.305s (-3.7%)0.633s21.00x
▲ VercelExpress17.692s (~)18.568s (-1.8%)0.876s21.00x
▲ VercelNitro17.791s (+2.4%)18.766s (~)0.975s21.01x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.189s28.050s0.862s31.00x
🌐 RedisNext.js (Turbopack)27.233s28.058s0.825s31.00x
💻 LocalNitro27.534s (~)28.054s (~)0.520s31.01x
💻 LocalExpress27.549s (~)28.053s (~)0.504s31.01x
🌐 MongoDBNext.js (Turbopack)30.633s31.056s0.424s21.13x
🐘 PostgresNext.js (Turbopack)37.971s38.090s0.118s21.40x
🐘 PostgresExpress50.161s (+30.6% 🔺)50.629s (+29.5% 🔺)0.468s21.84x
🐘 PostgresNitro50.337s (+32.9% 🔺)51.131s (+34.2% 🔺)0.795s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express69.695s (+54.5% 🔺)70.938s (+53.0% 🔺)1.243s11.00x
▲ VercelNitro70.423s (+66.2% 🔺)71.351s (+63.6% 🔺)0.928s11.01x
▲ VercelNext.js (Turbopack)70.900s (+64.7% 🔺)72.015s (+63.7% 🔺)1.115s11.02x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)54.959s55.107s0.147s21.00x
💻 LocalNext.js (Turbopack)56.821s57.098s0.277s21.03x
💻 LocalNitro57.415s (~)58.105s (~)0.690s21.04x
💻 LocalExpress57.420s (~)58.102s (~)0.682s21.04x
🌐 MongoDBNext.js (Turbopack)61.261s62.089s0.828s21.11x
🐘 PostgresNext.js (Turbopack)70.379s70.653s0.274s21.28x
🐘 PostgresNitro100.268s (+33.2% 🔺)101.233s (+32.9% 🔺)0.965s11.82x
🐘 PostgresExpress100.368s (+32.8% 🔺)101.239s (+32.9% 🔺)0.871s11.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express88.970s (-4.8%)90.053s (-5.1% 🟢)1.083s11.00x
▲ VercelNext.js (Turbopack)92.487s (~)92.957s (-1.2%)0.470s11.04x
▲ VercelNitro92.749s (~)93.604s (~)0.855s11.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.294s2.007s0.713s151.00x
💻 LocalExpress1.417s (-0.6%)2.006s (~)0.588s151.10x
💻 LocalNext.js (Turbopack)1.423s2.006s0.583s151.10x
💻 LocalNitro1.437s (+3.1%)2.006s (~)0.569s151.11x
🐘 PostgresNext.js (Turbopack)1.993s2.473s0.480s131.54x
🌐 MongoDBNext.js (Turbopack)2.169s3.009s0.840s101.68x
🐘 PostgresNitro2.313s (+4.6%)3.014s (+12.5% 🔺)0.701s101.79x
🐘 PostgresExpress2.373s (+21.7% 🔺)3.013s (+35.4% 🔺)0.640s101.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.497s (-39.3% 🟢)3.294s (-37.6% 🟢)0.798s101.00x
▲ VercelExpress2.670s (-70.5% 🟢)3.634s (-68.7% 🟢)0.964s91.07x
▲ VercelNitro2.807s (-70.1% 🟢)3.832s (-68.6% 🟢)1.024s81.12x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.560s3.008s0.448s101.00x
💻 LocalExpress2.610s (-7.6% 🟢)3.007s (~)0.398s101.02x
🌐 RedisNext.js (Turbopack)2.612s3.009s0.397s101.02x
💻 LocalNitro2.708s (+5.2% 🔺)3.007s (~)0.299s101.06x
🌐 MongoDBNext.js (Turbopack)4.772s5.180s0.408s61.86x
🐘 PostgresExpress8.159s (-12.9% 🟢)8.527s (-12.8% 🟢)0.368s43.19x
🐘 PostgresNitro9.134s (-8.4% 🟢)9.536s (-7.3% 🟢)0.402s43.57x
🐘 PostgresNext.js (Turbopack)12.189s12.367s0.178s34.76x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.961s (-82.2% 🟢)3.854s (-78.2% 🟢)0.894s81.00x
▲ VercelExpress3.024s (-83.0% 🟢)4.186s (-78.4% 🟢)1.162s81.02x
▲ VercelNitro3.030s (-78.9% 🟢)3.872s (-75.1% 🟢)0.842s81.02x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.130s5.013s0.883s61.00x
💻 LocalExpress7.513s (-10.7% 🟢)8.017s (-11.1% 🟢)0.505s41.82x
💻 LocalNext.js (Turbopack)7.596s8.265s0.669s41.84x
💻 LocalNitro7.637s (+5.4% 🔺)8.020s (~)0.383s41.85x
🌐 MongoDBNext.js (Turbopack)9.989s10.683s0.694s32.42x
🐘 PostgresNitro41.518s (-16.0% 🟢)42.127s (-15.9% 🟢)0.609s110.05x
🐘 PostgresExpress49.459s (-3.1%)50.130s (-2.0%)0.671s111.98x
🐘 PostgresNext.js (Turbopack)56.245s57.121s0.876s113.62x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.822s (-83.1% 🟢)5.752s (-75.8% 🟢)1.929s61.00x
▲ VercelNext.js (Turbopack)3.886s (-80.6% 🟢)4.752s (-77.2% 🟢)0.866s71.02x
▲ VercelExpress6.036s (+45.8% 🔺)7.351s (+34.5% 🔺)1.315s51.58x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.260s2.007s0.746s151.00x
💻 LocalExpress1.413s (-4.4%)2.006s (~)0.593s151.12x
💻 LocalNext.js (Turbopack)1.435s2.005s0.570s151.14x
💻 LocalNitro1.442s (+1.0%)2.006s (~)0.563s151.14x
🐘 PostgresNext.js (Turbopack)1.628s2.011s0.383s151.29x
🐘 PostgresNitro2.034s (-5.5% 🟢)2.922s (+6.6% 🔺)0.888s111.61x
🐘 PostgresExpress2.054s (-3.4%)2.681s (+3.3%)0.627s121.63x
🌐 MongoDBNext.js (Turbopack)2.170s3.007s0.837s101.72x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.243s (-5.5% 🟢)3.190s (-5.1% 🟢)0.947s101.00x
▲ VercelNext.js (Turbopack)2.279s (-4.7%)3.202s (-2.6%)0.924s101.02x
▲ VercelExpress2.343s (+4.7%)3.500s (+8.7% 🔺)1.156s91.04x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)2.545s3.009s0.464s101.00x
💻 LocalNext.js (Turbopack)2.657s3.009s0.352s101.04x
💻 LocalExpress2.730s (-3.8%)3.008s (~)0.279s101.07x
💻 LocalNitro2.745s (+1.1%)3.008s (~)0.262s101.08x
🌐 MongoDBNext.js (Turbopack)4.737s5.175s0.438s61.86x
🐘 PostgresExpress10.086s (-17.6% 🟢)10.696s (-15.8% 🟢)0.610s33.96x
🐘 PostgresNitro10.218s (+2.1%)11.032s (+3.1%)0.815s34.02x
🐘 PostgresNext.js (Turbopack)13.493s13.703s0.210s35.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.400s (-22.6% 🟢)3.309s (-19.9% 🟢)0.909s101.00x
▲ VercelNext.js (Turbopack)2.418s (-9.1% 🟢)3.105s (-16.2% 🟢)0.687s101.01x
▲ VercelExpress2.790s (-9.9% 🟢)3.753s (-7.1% 🟢)0.963s81.16x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.166s5.012s0.846s61.00x
💻 LocalNext.js (Turbopack)7.619s8.016s0.398s41.83x
💻 LocalExpress8.076s (-6.4% 🟢)8.773s (-2.8%)0.697s41.94x
💻 LocalNitro8.131s (+5.2% 🔺)9.021s (+9.1% 🔺)0.890s41.95x
🌐 MongoDBNext.js (Turbopack)9.835s10.349s0.513s32.36x
🐘 PostgresExpress50.121s (-2.8%)51.108s (-1.9%)0.987s112.03x
🐘 PostgresNitro50.610s (+0.9%)51.124s (~)0.514s112.15x
🐘 PostgresNext.js (Turbopack)56.644s57.134s0.490s113.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.941s (-3.2%)4.317s (+10.5% 🔺)1.376s71.00x
▲ VercelNitro3.249s (+11.5% 🔺)4.335s (+6.0% 🔺)1.085s81.10x
▲ VercelNext.js (Turbopack)4.099s (+28.6% 🔺)5.068s (+25.3% 🔺)0.969s61.39x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.147s1.002s0.012s1.017s0.870s101.00x
🌐 RedisNext.js (Turbopack)0.170s1.000s0.002s1.008s0.838s101.16x
💻 LocalNitro0.173s (+2.2%)1.002s (~)0.011s (+0.9%)1.017s (~)0.844s101.17x
💻 LocalExpress0.175s (-3.2%)1.002s (~)0.011s (-5.1% 🟢)1.016s (~)0.842s101.19x
🌐 MongoDBNext.js (Turbopack)0.492s0.956s0.001s1.009s0.517s103.35x
🐘 PostgresNext.js (Turbopack)0.782s0.750s0.001s1.010s0.228s105.32x
🐘 PostgresNitro2.372s (+76.5% 🔺)2.672s (+57.6% 🔺)0.001s (+16.7% 🔺)3.015s (+49.8% 🔺)0.644s1016.12x
🐘 PostgresExpress2.473s (+69.1% 🔺)2.569s (+55.2% 🔺)0.002s (+25.0% 🔺)3.015s (+49.8% 🔺)0.542s1016.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.068s (-21.7% 🟢)2.618s (-27.6% 🟢)0.180s (-13.3% 🟢)3.394s (-22.4% 🟢)1.326s101.00x
▲ VercelNext.js (Turbopack)2.147s (+5.4% 🔺)2.583s (-1.2%)0.227s (-2.6%)3.457s (~)1.310s101.04x
▲ VercelExpress2.244s (-28.9% 🟢)2.818s (-45.6% 🟢)0.244s (+29.3% 🔺)3.719s (-39.2% 🟢)1.475s101.08x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)8/12
🐘 PostgresNext.js (Turbopack)7/12
▲ VercelNitro5/12
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local10/12
Next.js (Turbopack)💻 Local6/12
Nitro💻 Local9/12
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

@TooTallNate
TooTallNate marked this pull request as ready for review February 16, 2026 17:27
@github-actions

github-actionsBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production512038550
✅ 💻 Local Development532068600
✅ 📦 Local Production532068600
✅ 🐘 Local Postgres532068600
✅ 🪟 Windows470350
❌ 🌍 Community Worlds107439159
✅ 📋 Other129021150
Total2391432752709

❌ Failed Tests

🌍 Community Worlds (43 failed)

turso (43 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • parallelSleepWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • 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 retry behavior workflow completes despite transient 5xx on step_completed
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • closureVariableWorkflow - nested step functions with closure variables
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4604
✅ example4604
✅ express4604
✅ fastify4604
✅ hono4604
✅ nextjs-turbopack4901
✅ nextjs-webpack4901
✅ nitro4604
✅ nuxt4604
✅ sveltekit4604
✅ vite4604
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4703
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
✅ mongodb4703
✅ redis-dev300
✅ redis4703
✅ turso-dev300
❌ turso4433
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4307
✅ e2e-local-postgres-nest-stable4307
✅ e2e-local-prod-nest-stable4307

📋 View full workflow run

CopilotAI review requested due to automatic review settings February 16, 2026 17:27
@TooTallNateGraphite App

Copy link
Copy Markdown
MemberAuthor

This stack of pull requests is managed by Graphite. Learn more about stacking.

@vercel

vercelBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-astro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-express-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-fastify-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-hono-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nitro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nuxt-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-sveltekit-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-vite-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-nestReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-swc-playgroundReadyReadyPreview, CommentFeb 16, 2026 5:56pm
1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
workflow-docsSkippedSkippedFeb 16, 2026 5:56pm

@TooTallNate
TooTallNate requested review from a team and removed request for CopilotFebruary 16, 2026 17:27
Comment threadpackages/workflow/package.json
…ge.json breaks 5 files in `packages/world-testing` that still import `hydrateWorkflowReturnValue` from that path.
Co-authored-by: TooTallNate <n@n8.io>
CopilotAI review requested due to automatic review settings February 16, 2026 17:52
@TooTallNate
TooTallNate removed the request for review from CopilotFebruary 16, 2026 17:52
@vercel
vercelBottemporarily deployed to Preview – workflow-docs February 16, 2026 17:52 Inactive

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request removes the workflow/internal/serialization export which was only being used in e2e test files. The changes simplify the codebase by:

  • Removing the re-export wrapper in the workflow package
  • Migrating test files to use direct imports from @workflow/core/serialization
  • Simplifying NextJS Pages Router API endpoints to use JSON instead of binary serialization

Changes:

  • Removed workflow/internal/serialization export from the workflow package
  • Updated world-testing files to import from @workflow/core/serialization directly
  • Simplified NextJS test API endpoints to use JSON body parsing instead of binary serialization

Reviewed changes

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

Show a summary per file
FileDescription
packages/workflow/src/internal/serialization.tsDeleted re-export file
packages/workflow/package.jsonRemoved export entry for internal/serialization
workbench/nextjs-webpack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
workbench/nextjs-turbopack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
packages/world-testing/src/*.mtsUpdated imports to use @workflow/core/serialization
packages/world-testing/package.jsonAdded @workflow/core dependency
pnpm-lock.yamlUpdated lockfile for new dependency
.changeset/cyan-ravens-eat.mdAdded changeset documenting the removal
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

@TooTallNate
TooTallNate merged commit 0946dad into mainFeb 16, 2026
110 checks passed
@TooTallNate
TooTallNate deleted the 02-16-remove_workflow_internal_serialization_export branch February 16, 2026 19:50
pranaygp added a commit that referenced this pull request Feb 17, 2026
* origin/main:
Remove "workflow/internal/serialization" export (#1082)
TooTallNate added a commit that referenced this pull request Apr 19, 2026
…/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
TooTallNate added a commit that referenced this pull request Apr 30, 2026
…isites
Standardize on `Symbol.for('workflow-serialize')` /
`Symbol.for('workflow-deserialize')` everywhere — the parallel
`globalThis.__wdk_serialize` / `__wdk_deserialize` aliases have been
removed from `vm-bundle-entry.ts` and the snapshot runtime's inline
JS strings now use the symbol form directly. Single canonical name,
no duplication.
Drop the `?? Math.random` and `?? Date.now()` fallbacks from the
ULID generator setup. Both prerequisites
(`globalThis.__ulidTimestamp` and the host-replaced seeded
`Math.random`) are always set by `snapshot-runtime.ts` before the
serde bundle is evaluated; silently falling back to unseeded
`Math.random` or live `Date.now()` would re-introduce the
non-determinism we deliberately fixed (concurrent VM invocations of
the same resumption must produce identical correlationIds for the
world's EntityConflictError dedup to work). Now throws if
`__ulidTimestamp` isn't a number, and passes the seeded
`Math.random` reference explicitly to `monotonicFactory` so
upstream's `detectPRNG` never runs (it'd throw in QuickJS anyway,
since `crypto` is unavailable).
Drop the `URL` / `URLSearchParams` / `DOMException` availability
guards in `common-vm.ts`. quickjs-wasi's URL extension is always
loaded (`url.so`) and DOMException is always constructible — the
guards were dead code carried over from when those weren't reliably
available. The reducer/reviver code is now straightforward
`instanceof URL` / `new URL(...)` / `new DOMException(...)`.
Remove `packages/core/src/serialization/base64.ts` and its
sub-path exports (`./serialization/workflow`,
`./serialization/workflow-vm`). The pure-JS base64 helpers were
leftover from before `base64.so` shipped `btoa`/`atob` natively;
the VM-side reducers in `common-vm.ts` now build base64 strings via
the native ones. The sub-path exports had zero consumers in this
repo (the same cleanup landed on the `serialization-refactor`
branch in 05e0fee but never made it onto `snapshot-runtime`
because the branches diverged earlier).
Remove `packages/workflow/src/internal/serialization.ts` and its
`./internal/serialization` package.json export. Same story — zero
consumers, previously removed in #1082, then accidentally
reintroduced via `f04fd8e91`.
TooTallNate added a commit that referenced this pull request May 1, 2026
…to existing pipeline (#1299)
* Add serialization module foundation: types, codec interface, format prefix
Start of the serialization refactor (separate from snapshot-runtime).
New files:
- serialization/types.ts — SerializationFormat enum, SerializableSpecial
interface, Reducers/Revivers types
- serialization/codec.ts — Codec interface with formatPrefix, serialize,
deserialize, and optional deserializeLegacy
- serialization/format.ts — Format prefix encode/decode/peek, moved from
the monolithic serialization.ts
The Codec interface enables future alternative formats (CBOR, JSON) while
keeping the devalue implementation as the current default.
* Add reducers, devalue codec, encryption, and mode-specific modules
Serialization refactor Phase 1: create the new module structure alongside
the existing monolithic serialization.ts (which continues to work).
New files:
- serialization/reducers/common.ts — Date, Error, Map, Set, URL, BigInt,
typed arrays, Headers, Request, Response, RegExp, URLSearchParams
- serialization/reducers/class.ts — Class/Instance with WORKFLOW_SERIALIZE/
DESERIALIZE support
- serialization/reducers/step-function.ts — StepFunction with closure vars
- serialization/codec-devalue.ts — devalue Codec implementation
- serialization/encryption.ts — composable encrypt/decrypt layer
- serialization/workflow.ts — synchronous, no encryption, for VM use
- serialization/step.ts — async with encryption, for step handler
- serialization/client.ts — async with encryption, for start() API
- serialization/index.ts — re-exports all public API
- serialization/serialization.test.ts — 25 focused tests
All modes compose their reducer/reviver sets from the shared building blocks.
Cross-mode compatibility verified: data serialized in any mode can be
deserialized in any other mode (for common types).
Existing 108 serialization tests continue to pass unchanged.
* Add sub-path exports for workflow serialization module
- Add ./serialization/workflow export to @workflow/core package.json
- Add ./internal/serialization re-export to workflow meta-package
- The workflow bundle can now import serialize/deserialize via:
import { serialize, deserialize } from 'workflow/internal/serialization'
Full test suite passes: 493 tests across 22 files (including 25 new
serialization module tests).
* Address code review feedback
1. Fix reducer composition order: Class/Instance reducers now come BEFORE
common reducers in all three modes (workflow, step, client). This ensures
custom Error subclasses with WORKFLOW_SERIALIZE are handled by the
Instance reducer before the generic Error reducer (devalue uses
first-match-wins semantics).
2. Fix encryption decrypt() to fail fast when encrypted data is encountered
without a decryption key, instead of silently returning encrypted bytes
that would fail later with an unhelpful format error.
3. Remove Request/Response from common reducers — they don't have matching
common revivers, so including them caused asymmetric behavior (serialize
as Request, deserialize as plain object). Request/Response handling
belongs in mode-specific modules that can provide proper revivers.
4. Document Node.js dependency in the workflow serialization re-export.
The current implementation uses node:util and Buffer. For the QuickJS
VM (snapshot runtime), these will need polyfills — tracked separately.
* Move reducer/reviver composition into the devalue codec
The Codec interface now takes a SerializationMode ('workflow', 'step',
'client') instead of raw reducers/revivers. The reducer/reviver
composition is internal to the devalue codec implementation.
This is the right abstraction because reducers/revivers are devalue-
specific concepts. A future CBOR codec would handle Date, typed arrays,
Map, Set natively via the CBOR type system — it wouldn't use reducers
at all. A JSON codec would only support standard JSON types.
The mode-specific modules (workflow.ts, step.ts, client.ts) are now
simpler — they just pass the mode string to the codec.
* Replace SerializationFormatType enum with open-ended FormatPrefix type
The format prefix is now a branded string type validated by
isFormatPrefix() — any 4-character [a-z0-9] string is valid.
This removes the hard-coded enum of known formats, making the system
truly open for extension:
type FormatPrefix = string & { __brand: 'FormatPrefix' };
function isFormatPrefix(value: string): value is FormatPrefix;
The SerializationFormat object still provides well-known constants
('devl', 'encr') but they're now just typed constants, not an
exhaustive enum.
peekFormatPrefix() and decodeFormatPrefix() use isFormatPrefix() for
validation instead of checking against a known list. Unknown but valid
prefixes (e.g. 'cbor', 'json', 'v2b1') are accepted — the caller
decides whether they can handle the format.
6 new isFormatPrefix tests covering: valid strings, too short, too long,
uppercase, special characters. 1 new test for unknown-but-valid prefixes.
* Wire modular serialization modules into serialization.ts, add 138 unit tests
Replace duplicate format prefix, reducer/reviver, and encryption helper
code in the monolithic serialization.ts with imports from the modular
serialization/ directory. This completes the refactoring started in the
earlier additive-only commits.
Key changes:
- serialization.ts now imports types, format prefix, common/class/step-function
reducers and revivers, and encryption helpers from ./serialization/ modules
- Removed ~450 lines of duplicate code from serialization.ts
- Made encryption error messages consistent between old and new modules
- Added 138 comprehensive unit tests covering types, format prefix,
encryption, codec, all three reducer modules, all three mode modules,
cross-mode compatibility, and edge cases
- Updated one existing test assertion for new error message wording
* Address code review feedback
- encryption.ts: throw WorkflowRuntimeError instead of plain Error in
decrypt() to preserve the error contract from legacy maybeDecrypt()
- format.ts: document that open-ended prefix validation ([a-z0-9]{4})
is intentional for forward compatibility — callers check support
- errors.ts: extract duplicated formatSerializationError into shared
utility, remove 4 copies from workflow.ts, step.ts, client.ts
- codec-devalue.ts: document that globalThis default is a known
limitation; legacy dehydrate/hydrate path still supports custom global
* Fix codec-devalue.ts comment: clarify modular modules are not used in current runtime
The globalThis default is not a limitation for the current runtime —
all serialization goes through dehydrate*/hydrate* in serialization.ts
which passes the correct global. The modular modules are infrastructure
for the future snapshot runtime where serialization runs inside the VM.
* Wire dehydrate/hydrate functions through modular serialize/deserialize
The dehydrate*/hydrate* functions in serialization.ts now delegate to
the modular mode modules (workflowModule, stepModule, clientModule)
instead of directly calling devalue stringify/parse/unflatten.
Key changes:
- Extended Codec interface with CodecOptions (global, extraReducers,
extraRevivers) so the codec can receive VM globals and mode-specific
stream/Request/Response handlers
- devalueCodec threads global through to all reducer/reviver factories
so instanceof checks work across VM boundaries
- Mode modules (workflow.ts, step.ts, client.ts) accept CodecOptions
and pass them through to the codec
- dehydrate*/hydrate* functions now call module serialize/deserialize
with stream and Request/Response reducers/revivers passed as extras
- v1Compat path remains inline (pre-codec, uses stringify + revive)
- Error context strings preserved via try/catch re-wrapping
* Bump changeset from patch to minor for serialization refactor
Return types of public get*Reducers/get*Revivers functions narrowed
from Reducers/Revivers to Partial<Reducers>/Partial<Revivers>, which
is a TypeScript-level breaking change. Also adds new sub-path exports
(@workflow/core/serialization/workflow, workflow/internal/serialization)
which is additive. Minor bump is the appropriate semver for both.
* Remove unused workflow/internal/serialization re-export and @workflow/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
* Downgrade changeset from minor to patch
After auditing actual consumers of the narrowed return types
(getExternalReducers/getWorkflowReducers/getExternalRevivers/getWorkflowRevivers
now return Partial<Reducers>/Partial<Revivers>), no in-repo or external
consumer indexes specific keys on the returned object in a way that would
break. The only internal caller that did (runtime/run.ts) was updated in
this same PR. The narrowing is type-safer but effectively invisible at
runtime and for idiomatic callers that spread or forward the object.
Since the refactor is internally restructuring only, patch is the
appropriate semver bump.
* Trim serialization-refactor changeset
* Dedup formatSerializationError: import from serialization/errors.ts
The legacy serialization.ts had its own inlined copy of
formatSerializationError. Now that the helper is exported from
serialization/errors.ts (already consumed by workflow.ts/step.ts/client.ts),
import it here too to keep the single source of truth.
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.

4 participants

@TooTallNate@VaguelySerious@karthikscale3
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
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;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Remove "workflow/internal/serialization" export by TooTallNate · Pull Request #1082 · vercel/workflow · GitHub
Skip to content

Remove "workflow/internal/serialization" export - #1082

Merged
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export
Feb 16, 2026
Merged

Remove "workflow/internal/serialization" export#1082
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export

Conversation

@TooTallNate

Copy link
Copy Markdown
Member

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.
@changeset-bot

changeset-botBot commented Feb 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: aa8c9bd

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

This PR includes changesets to release 14 packages
NameType
workflowPatch
@workflow/world-testingPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@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 Feb 16, 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🥇 Express0.032s (-6.9% 🟢)1.005s (~)0.973s101.00x
💻 LocalNitro0.032s (+2.5%)1.005s (~)0.972s101.00x
💻 LocalNext.js (Turbopack)0.043s1.005s0.962s101.33x
🌐 RedisNext.js (Turbopack)0.056s1.005s0.949s101.74x
🌐 MongoDBNext.js (Turbopack)0.103s1.007s0.904s103.21x
🐘 PostgresExpress0.155s (-23.4% 🟢)1.010s (-0.8%)0.854s104.82x
🐘 PostgresNitro0.251s (+10.8% 🔺)1.010s (-0.9%)0.758s107.81x
🐘 PostgresNext.js (Turbopack)0.370s1.009s0.639s1011.50x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.544s (-14.7% 🟢)1.950s (-9.5% 🟢)1.405s101.00x
▲ VercelNext.js (Turbopack)0.597s (-27.3% 🟢)1.924s (-10.9% 🟢)1.327s101.10x
▲ VercelExpress0.654s (+8.9% 🔺)2.105s (+2.0%)1.451s101.20x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.096s2.006s0.910s101.00x
💻 LocalExpress1.105s (~)2.006s (~)0.901s101.01x
💻 LocalNitro1.106s (~)2.006s (~)0.901s101.01x
🌐 RedisNext.js (Turbopack)1.130s2.007s0.877s101.03x
🌐 MongoDBNext.js (Turbopack)1.310s2.008s0.698s101.20x
🐘 PostgresNext.js (Turbopack)2.254s3.015s0.761s102.06x
🐘 PostgresExpress2.376s (-3.5%)3.014s (~)0.638s102.17x
🐘 PostgresNitro2.425s (+6.8% 🔺)3.014s (+7.1% 🔺)0.590s102.21x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.276s (-5.5% 🟢)3.134s (-10.2% 🟢)0.858s101.00x
▲ VercelNitro2.337s (+1.7%)3.339s (-8.6% 🟢)1.003s101.03x
▲ VercelExpress2.746s (+21.6% 🔺)3.740s (+11.4% 🔺)0.995s101.21x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.745s11.021s0.276s31.00x
💻 LocalExpress10.822s (-0.6%)11.021s (~)0.199s31.01x
🌐 RedisNext.js (Turbopack)10.831s11.026s0.195s31.01x
💻 LocalNitro10.838s (~)11.023s (~)0.185s31.01x
🌐 MongoDBNext.js (Turbopack)12.321s13.025s0.704s31.15x
🐘 PostgresNext.js (Turbopack)15.142s16.046s0.904s21.41x
🐘 PostgresNitro20.293s (+33.2% 🔺)21.058s (+31.2% 🔺)0.766s21.89x
🐘 PostgresExpress20.404s (+6.9% 🔺)21.059s (+7.7% 🔺)0.656s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)17.672s (-0.7%)18.305s (-3.7%)0.633s21.00x
▲ VercelExpress17.692s (~)18.568s (-1.8%)0.876s21.00x
▲ VercelNitro17.791s (+2.4%)18.766s (~)0.975s21.01x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.189s28.050s0.862s31.00x
🌐 RedisNext.js (Turbopack)27.233s28.058s0.825s31.00x
💻 LocalNitro27.534s (~)28.054s (~)0.520s31.01x
💻 LocalExpress27.549s (~)28.053s (~)0.504s31.01x
🌐 MongoDBNext.js (Turbopack)30.633s31.056s0.424s21.13x
🐘 PostgresNext.js (Turbopack)37.971s38.090s0.118s21.40x
🐘 PostgresExpress50.161s (+30.6% 🔺)50.629s (+29.5% 🔺)0.468s21.84x
🐘 PostgresNitro50.337s (+32.9% 🔺)51.131s (+34.2% 🔺)0.795s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express69.695s (+54.5% 🔺)70.938s (+53.0% 🔺)1.243s11.00x
▲ VercelNitro70.423s (+66.2% 🔺)71.351s (+63.6% 🔺)0.928s11.01x
▲ VercelNext.js (Turbopack)70.900s (+64.7% 🔺)72.015s (+63.7% 🔺)1.115s11.02x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)54.959s55.107s0.147s21.00x
💻 LocalNext.js (Turbopack)56.821s57.098s0.277s21.03x
💻 LocalNitro57.415s (~)58.105s (~)0.690s21.04x
💻 LocalExpress57.420s (~)58.102s (~)0.682s21.04x
🌐 MongoDBNext.js (Turbopack)61.261s62.089s0.828s21.11x
🐘 PostgresNext.js (Turbopack)70.379s70.653s0.274s21.28x
🐘 PostgresNitro100.268s (+33.2% 🔺)101.233s (+32.9% 🔺)0.965s11.82x
🐘 PostgresExpress100.368s (+32.8% 🔺)101.239s (+32.9% 🔺)0.871s11.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express88.970s (-4.8%)90.053s (-5.1% 🟢)1.083s11.00x
▲ VercelNext.js (Turbopack)92.487s (~)92.957s (-1.2%)0.470s11.04x
▲ VercelNitro92.749s (~)93.604s (~)0.855s11.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.294s2.007s0.713s151.00x
💻 LocalExpress1.417s (-0.6%)2.006s (~)0.588s151.10x
💻 LocalNext.js (Turbopack)1.423s2.006s0.583s151.10x
💻 LocalNitro1.437s (+3.1%)2.006s (~)0.569s151.11x
🐘 PostgresNext.js (Turbopack)1.993s2.473s0.480s131.54x
🌐 MongoDBNext.js (Turbopack)2.169s3.009s0.840s101.68x
🐘 PostgresNitro2.313s (+4.6%)3.014s (+12.5% 🔺)0.701s101.79x
🐘 PostgresExpress2.373s (+21.7% 🔺)3.013s (+35.4% 🔺)0.640s101.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.497s (-39.3% 🟢)3.294s (-37.6% 🟢)0.798s101.00x
▲ VercelExpress2.670s (-70.5% 🟢)3.634s (-68.7% 🟢)0.964s91.07x
▲ VercelNitro2.807s (-70.1% 🟢)3.832s (-68.6% 🟢)1.024s81.12x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.560s3.008s0.448s101.00x
💻 LocalExpress2.610s (-7.6% 🟢)3.007s (~)0.398s101.02x
🌐 RedisNext.js (Turbopack)2.612s3.009s0.397s101.02x
💻 LocalNitro2.708s (+5.2% 🔺)3.007s (~)0.299s101.06x
🌐 MongoDBNext.js (Turbopack)4.772s5.180s0.408s61.86x
🐘 PostgresExpress8.159s (-12.9% 🟢)8.527s (-12.8% 🟢)0.368s43.19x
🐘 PostgresNitro9.134s (-8.4% 🟢)9.536s (-7.3% 🟢)0.402s43.57x
🐘 PostgresNext.js (Turbopack)12.189s12.367s0.178s34.76x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.961s (-82.2% 🟢)3.854s (-78.2% 🟢)0.894s81.00x
▲ VercelExpress3.024s (-83.0% 🟢)4.186s (-78.4% 🟢)1.162s81.02x
▲ VercelNitro3.030s (-78.9% 🟢)3.872s (-75.1% 🟢)0.842s81.02x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.130s5.013s0.883s61.00x
💻 LocalExpress7.513s (-10.7% 🟢)8.017s (-11.1% 🟢)0.505s41.82x
💻 LocalNext.js (Turbopack)7.596s8.265s0.669s41.84x
💻 LocalNitro7.637s (+5.4% 🔺)8.020s (~)0.383s41.85x
🌐 MongoDBNext.js (Turbopack)9.989s10.683s0.694s32.42x
🐘 PostgresNitro41.518s (-16.0% 🟢)42.127s (-15.9% 🟢)0.609s110.05x
🐘 PostgresExpress49.459s (-3.1%)50.130s (-2.0%)0.671s111.98x
🐘 PostgresNext.js (Turbopack)56.245s57.121s0.876s113.62x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.822s (-83.1% 🟢)5.752s (-75.8% 🟢)1.929s61.00x
▲ VercelNext.js (Turbopack)3.886s (-80.6% 🟢)4.752s (-77.2% 🟢)0.866s71.02x
▲ VercelExpress6.036s (+45.8% 🔺)7.351s (+34.5% 🔺)1.315s51.58x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.260s2.007s0.746s151.00x
💻 LocalExpress1.413s (-4.4%)2.006s (~)0.593s151.12x
💻 LocalNext.js (Turbopack)1.435s2.005s0.570s151.14x
💻 LocalNitro1.442s (+1.0%)2.006s (~)0.563s151.14x
🐘 PostgresNext.js (Turbopack)1.628s2.011s0.383s151.29x
🐘 PostgresNitro2.034s (-5.5% 🟢)2.922s (+6.6% 🔺)0.888s111.61x
🐘 PostgresExpress2.054s (-3.4%)2.681s (+3.3%)0.627s121.63x
🌐 MongoDBNext.js (Turbopack)2.170s3.007s0.837s101.72x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.243s (-5.5% 🟢)3.190s (-5.1% 🟢)0.947s101.00x
▲ VercelNext.js (Turbopack)2.279s (-4.7%)3.202s (-2.6%)0.924s101.02x
▲ VercelExpress2.343s (+4.7%)3.500s (+8.7% 🔺)1.156s91.04x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)2.545s3.009s0.464s101.00x
💻 LocalNext.js (Turbopack)2.657s3.009s0.352s101.04x
💻 LocalExpress2.730s (-3.8%)3.008s (~)0.279s101.07x
💻 LocalNitro2.745s (+1.1%)3.008s (~)0.262s101.08x
🌐 MongoDBNext.js (Turbopack)4.737s5.175s0.438s61.86x
🐘 PostgresExpress10.086s (-17.6% 🟢)10.696s (-15.8% 🟢)0.610s33.96x
🐘 PostgresNitro10.218s (+2.1%)11.032s (+3.1%)0.815s34.02x
🐘 PostgresNext.js (Turbopack)13.493s13.703s0.210s35.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.400s (-22.6% 🟢)3.309s (-19.9% 🟢)0.909s101.00x
▲ VercelNext.js (Turbopack)2.418s (-9.1% 🟢)3.105s (-16.2% 🟢)0.687s101.01x
▲ VercelExpress2.790s (-9.9% 🟢)3.753s (-7.1% 🟢)0.963s81.16x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.166s5.012s0.846s61.00x
💻 LocalNext.js (Turbopack)7.619s8.016s0.398s41.83x
💻 LocalExpress8.076s (-6.4% 🟢)8.773s (-2.8%)0.697s41.94x
💻 LocalNitro8.131s (+5.2% 🔺)9.021s (+9.1% 🔺)0.890s41.95x
🌐 MongoDBNext.js (Turbopack)9.835s10.349s0.513s32.36x
🐘 PostgresExpress50.121s (-2.8%)51.108s (-1.9%)0.987s112.03x
🐘 PostgresNitro50.610s (+0.9%)51.124s (~)0.514s112.15x
🐘 PostgresNext.js (Turbopack)56.644s57.134s0.490s113.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.941s (-3.2%)4.317s (+10.5% 🔺)1.376s71.00x
▲ VercelNitro3.249s (+11.5% 🔺)4.335s (+6.0% 🔺)1.085s81.10x
▲ VercelNext.js (Turbopack)4.099s (+28.6% 🔺)5.068s (+25.3% 🔺)0.969s61.39x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.147s1.002s0.012s1.017s0.870s101.00x
🌐 RedisNext.js (Turbopack)0.170s1.000s0.002s1.008s0.838s101.16x
💻 LocalNitro0.173s (+2.2%)1.002s (~)0.011s (+0.9%)1.017s (~)0.844s101.17x
💻 LocalExpress0.175s (-3.2%)1.002s (~)0.011s (-5.1% 🟢)1.016s (~)0.842s101.19x
🌐 MongoDBNext.js (Turbopack)0.492s0.956s0.001s1.009s0.517s103.35x
🐘 PostgresNext.js (Turbopack)0.782s0.750s0.001s1.010s0.228s105.32x
🐘 PostgresNitro2.372s (+76.5% 🔺)2.672s (+57.6% 🔺)0.001s (+16.7% 🔺)3.015s (+49.8% 🔺)0.644s1016.12x
🐘 PostgresExpress2.473s (+69.1% 🔺)2.569s (+55.2% 🔺)0.002s (+25.0% 🔺)3.015s (+49.8% 🔺)0.542s1016.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.068s (-21.7% 🟢)2.618s (-27.6% 🟢)0.180s (-13.3% 🟢)3.394s (-22.4% 🟢)1.326s101.00x
▲ VercelNext.js (Turbopack)2.147s (+5.4% 🔺)2.583s (-1.2%)0.227s (-2.6%)3.457s (~)1.310s101.04x
▲ VercelExpress2.244s (-28.9% 🟢)2.818s (-45.6% 🟢)0.244s (+29.3% 🔺)3.719s (-39.2% 🟢)1.475s101.08x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)8/12
🐘 PostgresNext.js (Turbopack)7/12
▲ VercelNitro5/12
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local10/12
Next.js (Turbopack)💻 Local6/12
Nitro💻 Local9/12
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

@TooTallNate
TooTallNate marked this pull request as ready for review February 16, 2026 17:27
@github-actions

github-actionsBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production512038550
✅ 💻 Local Development532068600
✅ 📦 Local Production532068600
✅ 🐘 Local Postgres532068600
✅ 🪟 Windows470350
❌ 🌍 Community Worlds107439159
✅ 📋 Other129021150
Total2391432752709

❌ Failed Tests

🌍 Community Worlds (43 failed)

turso (43 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • parallelSleepWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • 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 retry behavior workflow completes despite transient 5xx on step_completed
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • closureVariableWorkflow - nested step functions with closure variables
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4604
✅ example4604
✅ express4604
✅ fastify4604
✅ hono4604
✅ nextjs-turbopack4901
✅ nextjs-webpack4901
✅ nitro4604
✅ nuxt4604
✅ sveltekit4604
✅ vite4604
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4703
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
✅ mongodb4703
✅ redis-dev300
✅ redis4703
✅ turso-dev300
❌ turso4433
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4307
✅ e2e-local-postgres-nest-stable4307
✅ e2e-local-prod-nest-stable4307

📋 View full workflow run

CopilotAI review requested due to automatic review settings February 16, 2026 17:27
@TooTallNateGraphite App

Copy link
Copy Markdown
MemberAuthor

This stack of pull requests is managed by Graphite. Learn more about stacking.

@vercel

vercelBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-astro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-express-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-fastify-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-hono-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nitro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nuxt-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-sveltekit-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-vite-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-nestReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-swc-playgroundReadyReadyPreview, CommentFeb 16, 2026 5:56pm
1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
workflow-docsSkippedSkippedFeb 16, 2026 5:56pm

@TooTallNate
TooTallNate requested review from a team and removed request for CopilotFebruary 16, 2026 17:27
Comment threadpackages/workflow/package.json
…ge.json breaks 5 files in `packages/world-testing` that still import `hydrateWorkflowReturnValue` from that path.
Co-authored-by: TooTallNate <n@n8.io>
CopilotAI review requested due to automatic review settings February 16, 2026 17:52
@TooTallNate
TooTallNate removed the request for review from CopilotFebruary 16, 2026 17:52
@vercel
vercelBottemporarily deployed to Preview – workflow-docs February 16, 2026 17:52 Inactive

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request removes the workflow/internal/serialization export which was only being used in e2e test files. The changes simplify the codebase by:

  • Removing the re-export wrapper in the workflow package
  • Migrating test files to use direct imports from @workflow/core/serialization
  • Simplifying NextJS Pages Router API endpoints to use JSON instead of binary serialization

Changes:

  • Removed workflow/internal/serialization export from the workflow package
  • Updated world-testing files to import from @workflow/core/serialization directly
  • Simplified NextJS test API endpoints to use JSON body parsing instead of binary serialization

Reviewed changes

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

Show a summary per file
FileDescription
packages/workflow/src/internal/serialization.tsDeleted re-export file
packages/workflow/package.jsonRemoved export entry for internal/serialization
workbench/nextjs-webpack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
workbench/nextjs-turbopack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
packages/world-testing/src/*.mtsUpdated imports to use @workflow/core/serialization
packages/world-testing/package.jsonAdded @workflow/core dependency
pnpm-lock.yamlUpdated lockfile for new dependency
.changeset/cyan-ravens-eat.mdAdded changeset documenting the removal
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

@TooTallNate
TooTallNate merged commit 0946dad into mainFeb 16, 2026
110 checks passed
@TooTallNate
TooTallNate deleted the 02-16-remove_workflow_internal_serialization_export branch February 16, 2026 19:50
pranaygp added a commit that referenced this pull request Feb 17, 2026
* origin/main:
Remove "workflow/internal/serialization" export (#1082)
TooTallNate added a commit that referenced this pull request Apr 19, 2026
…/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
TooTallNate added a commit that referenced this pull request Apr 30, 2026
…isites
Standardize on `Symbol.for('workflow-serialize')` /
`Symbol.for('workflow-deserialize')` everywhere — the parallel
`globalThis.__wdk_serialize` / `__wdk_deserialize` aliases have been
removed from `vm-bundle-entry.ts` and the snapshot runtime's inline
JS strings now use the symbol form directly. Single canonical name,
no duplication.
Drop the `?? Math.random` and `?? Date.now()` fallbacks from the
ULID generator setup. Both prerequisites
(`globalThis.__ulidTimestamp` and the host-replaced seeded
`Math.random`) are always set by `snapshot-runtime.ts` before the
serde bundle is evaluated; silently falling back to unseeded
`Math.random` or live `Date.now()` would re-introduce the
non-determinism we deliberately fixed (concurrent VM invocations of
the same resumption must produce identical correlationIds for the
world's EntityConflictError dedup to work). Now throws if
`__ulidTimestamp` isn't a number, and passes the seeded
`Math.random` reference explicitly to `monotonicFactory` so
upstream's `detectPRNG` never runs (it'd throw in QuickJS anyway,
since `crypto` is unavailable).
Drop the `URL` / `URLSearchParams` / `DOMException` availability
guards in `common-vm.ts`. quickjs-wasi's URL extension is always
loaded (`url.so`) and DOMException is always constructible — the
guards were dead code carried over from when those weren't reliably
available. The reducer/reviver code is now straightforward
`instanceof URL` / `new URL(...)` / `new DOMException(...)`.
Remove `packages/core/src/serialization/base64.ts` and its
sub-path exports (`./serialization/workflow`,
`./serialization/workflow-vm`). The pure-JS base64 helpers were
leftover from before `base64.so` shipped `btoa`/`atob` natively;
the VM-side reducers in `common-vm.ts` now build base64 strings via
the native ones. The sub-path exports had zero consumers in this
repo (the same cleanup landed on the `serialization-refactor`
branch in 05e0fee but never made it onto `snapshot-runtime`
because the branches diverged earlier).
Remove `packages/workflow/src/internal/serialization.ts` and its
`./internal/serialization` package.json export. Same story — zero
consumers, previously removed in #1082, then accidentally
reintroduced via `f04fd8e91`.
TooTallNate added a commit that referenced this pull request May 1, 2026
…to existing pipeline (#1299)
* Add serialization module foundation: types, codec interface, format prefix
Start of the serialization refactor (separate from snapshot-runtime).
New files:
- serialization/types.ts — SerializationFormat enum, SerializableSpecial
interface, Reducers/Revivers types
- serialization/codec.ts — Codec interface with formatPrefix, serialize,
deserialize, and optional deserializeLegacy
- serialization/format.ts — Format prefix encode/decode/peek, moved from
the monolithic serialization.ts
The Codec interface enables future alternative formats (CBOR, JSON) while
keeping the devalue implementation as the current default.
* Add reducers, devalue codec, encryption, and mode-specific modules
Serialization refactor Phase 1: create the new module structure alongside
the existing monolithic serialization.ts (which continues to work).
New files:
- serialization/reducers/common.ts — Date, Error, Map, Set, URL, BigInt,
typed arrays, Headers, Request, Response, RegExp, URLSearchParams
- serialization/reducers/class.ts — Class/Instance with WORKFLOW_SERIALIZE/
DESERIALIZE support
- serialization/reducers/step-function.ts — StepFunction with closure vars
- serialization/codec-devalue.ts — devalue Codec implementation
- serialization/encryption.ts — composable encrypt/decrypt layer
- serialization/workflow.ts — synchronous, no encryption, for VM use
- serialization/step.ts — async with encryption, for step handler
- serialization/client.ts — async with encryption, for start() API
- serialization/index.ts — re-exports all public API
- serialization/serialization.test.ts — 25 focused tests
All modes compose their reducer/reviver sets from the shared building blocks.
Cross-mode compatibility verified: data serialized in any mode can be
deserialized in any other mode (for common types).
Existing 108 serialization tests continue to pass unchanged.
* Add sub-path exports for workflow serialization module
- Add ./serialization/workflow export to @workflow/core package.json
- Add ./internal/serialization re-export to workflow meta-package
- The workflow bundle can now import serialize/deserialize via:
import { serialize, deserialize } from 'workflow/internal/serialization'
Full test suite passes: 493 tests across 22 files (including 25 new
serialization module tests).
* Address code review feedback
1. Fix reducer composition order: Class/Instance reducers now come BEFORE
common reducers in all three modes (workflow, step, client). This ensures
custom Error subclasses with WORKFLOW_SERIALIZE are handled by the
Instance reducer before the generic Error reducer (devalue uses
first-match-wins semantics).
2. Fix encryption decrypt() to fail fast when encrypted data is encountered
without a decryption key, instead of silently returning encrypted bytes
that would fail later with an unhelpful format error.
3. Remove Request/Response from common reducers — they don't have matching
common revivers, so including them caused asymmetric behavior (serialize
as Request, deserialize as plain object). Request/Response handling
belongs in mode-specific modules that can provide proper revivers.
4. Document Node.js dependency in the workflow serialization re-export.
The current implementation uses node:util and Buffer. For the QuickJS
VM (snapshot runtime), these will need polyfills — tracked separately.
* Move reducer/reviver composition into the devalue codec
The Codec interface now takes a SerializationMode ('workflow', 'step',
'client') instead of raw reducers/revivers. The reducer/reviver
composition is internal to the devalue codec implementation.
This is the right abstraction because reducers/revivers are devalue-
specific concepts. A future CBOR codec would handle Date, typed arrays,
Map, Set natively via the CBOR type system — it wouldn't use reducers
at all. A JSON codec would only support standard JSON types.
The mode-specific modules (workflow.ts, step.ts, client.ts) are now
simpler — they just pass the mode string to the codec.
* Replace SerializationFormatType enum with open-ended FormatPrefix type
The format prefix is now a branded string type validated by
isFormatPrefix() — any 4-character [a-z0-9] string is valid.
This removes the hard-coded enum of known formats, making the system
truly open for extension:
type FormatPrefix = string & { __brand: 'FormatPrefix' };
function isFormatPrefix(value: string): value is FormatPrefix;
The SerializationFormat object still provides well-known constants
('devl', 'encr') but they're now just typed constants, not an
exhaustive enum.
peekFormatPrefix() and decodeFormatPrefix() use isFormatPrefix() for
validation instead of checking against a known list. Unknown but valid
prefixes (e.g. 'cbor', 'json', 'v2b1') are accepted — the caller
decides whether they can handle the format.
6 new isFormatPrefix tests covering: valid strings, too short, too long,
uppercase, special characters. 1 new test for unknown-but-valid prefixes.
* Wire modular serialization modules into serialization.ts, add 138 unit tests
Replace duplicate format prefix, reducer/reviver, and encryption helper
code in the monolithic serialization.ts with imports from the modular
serialization/ directory. This completes the refactoring started in the
earlier additive-only commits.
Key changes:
- serialization.ts now imports types, format prefix, common/class/step-function
reducers and revivers, and encryption helpers from ./serialization/ modules
- Removed ~450 lines of duplicate code from serialization.ts
- Made encryption error messages consistent between old and new modules
- Added 138 comprehensive unit tests covering types, format prefix,
encryption, codec, all three reducer modules, all three mode modules,
cross-mode compatibility, and edge cases
- Updated one existing test assertion for new error message wording
* Address code review feedback
- encryption.ts: throw WorkflowRuntimeError instead of plain Error in
decrypt() to preserve the error contract from legacy maybeDecrypt()
- format.ts: document that open-ended prefix validation ([a-z0-9]{4})
is intentional for forward compatibility — callers check support
- errors.ts: extract duplicated formatSerializationError into shared
utility, remove 4 copies from workflow.ts, step.ts, client.ts
- codec-devalue.ts: document that globalThis default is a known
limitation; legacy dehydrate/hydrate path still supports custom global
* Fix codec-devalue.ts comment: clarify modular modules are not used in current runtime
The globalThis default is not a limitation for the current runtime —
all serialization goes through dehydrate*/hydrate* in serialization.ts
which passes the correct global. The modular modules are infrastructure
for the future snapshot runtime where serialization runs inside the VM.
* Wire dehydrate/hydrate functions through modular serialize/deserialize
The dehydrate*/hydrate* functions in serialization.ts now delegate to
the modular mode modules (workflowModule, stepModule, clientModule)
instead of directly calling devalue stringify/parse/unflatten.
Key changes:
- Extended Codec interface with CodecOptions (global, extraReducers,
extraRevivers) so the codec can receive VM globals and mode-specific
stream/Request/Response handlers
- devalueCodec threads global through to all reducer/reviver factories
so instanceof checks work across VM boundaries
- Mode modules (workflow.ts, step.ts, client.ts) accept CodecOptions
and pass them through to the codec
- dehydrate*/hydrate* functions now call module serialize/deserialize
with stream and Request/Response reducers/revivers passed as extras
- v1Compat path remains inline (pre-codec, uses stringify + revive)
- Error context strings preserved via try/catch re-wrapping
* Bump changeset from patch to minor for serialization refactor
Return types of public get*Reducers/get*Revivers functions narrowed
from Reducers/Revivers to Partial<Reducers>/Partial<Revivers>, which
is a TypeScript-level breaking change. Also adds new sub-path exports
(@workflow/core/serialization/workflow, workflow/internal/serialization)
which is additive. Minor bump is the appropriate semver for both.
* Remove unused workflow/internal/serialization re-export and @workflow/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
* Downgrade changeset from minor to patch
After auditing actual consumers of the narrowed return types
(getExternalReducers/getWorkflowReducers/getExternalRevivers/getWorkflowRevivers
now return Partial<Reducers>/Partial<Revivers>), no in-repo or external
consumer indexes specific keys on the returned object in a way that would
break. The only internal caller that did (runtime/run.ts) was updated in
this same PR. The narrowing is type-safer but effectively invisible at
runtime and for idiomatic callers that spread or forward the object.
Since the refactor is internally restructuring only, patch is the
appropriate semver bump.
* Trim serialization-refactor changeset
* Dedup formatSerializationError: import from serialization/errors.ts
The legacy serialization.ts had its own inlined copy of
formatSerializationError. Now that the helper is exported from
serialization/errors.ts (already consumed by workflow.ts/step.ts/client.ts),
import it here too to keep the single source of truth.
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.

4 participants

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

Remove "workflow/internal/serialization" export - #1082

Merged
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export
Feb 16, 2026
Merged

Remove "workflow/internal/serialization" export#1082
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export

Conversation

@TooTallNate

Copy link
Copy Markdown
Member

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.
@changeset-bot

changeset-botBot commented Feb 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: aa8c9bd

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

This PR includes changesets to release 14 packages
NameType
workflowPatch
@workflow/world-testingPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@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 Feb 16, 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🥇 Express0.032s (-6.9% 🟢)1.005s (~)0.973s101.00x
💻 LocalNitro0.032s (+2.5%)1.005s (~)0.972s101.00x
💻 LocalNext.js (Turbopack)0.043s1.005s0.962s101.33x
🌐 RedisNext.js (Turbopack)0.056s1.005s0.949s101.74x
🌐 MongoDBNext.js (Turbopack)0.103s1.007s0.904s103.21x
🐘 PostgresExpress0.155s (-23.4% 🟢)1.010s (-0.8%)0.854s104.82x
🐘 PostgresNitro0.251s (+10.8% 🔺)1.010s (-0.9%)0.758s107.81x
🐘 PostgresNext.js (Turbopack)0.370s1.009s0.639s1011.50x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.544s (-14.7% 🟢)1.950s (-9.5% 🟢)1.405s101.00x
▲ VercelNext.js (Turbopack)0.597s (-27.3% 🟢)1.924s (-10.9% 🟢)1.327s101.10x
▲ VercelExpress0.654s (+8.9% 🔺)2.105s (+2.0%)1.451s101.20x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.096s2.006s0.910s101.00x
💻 LocalExpress1.105s (~)2.006s (~)0.901s101.01x
💻 LocalNitro1.106s (~)2.006s (~)0.901s101.01x
🌐 RedisNext.js (Turbopack)1.130s2.007s0.877s101.03x
🌐 MongoDBNext.js (Turbopack)1.310s2.008s0.698s101.20x
🐘 PostgresNext.js (Turbopack)2.254s3.015s0.761s102.06x
🐘 PostgresExpress2.376s (-3.5%)3.014s (~)0.638s102.17x
🐘 PostgresNitro2.425s (+6.8% 🔺)3.014s (+7.1% 🔺)0.590s102.21x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.276s (-5.5% 🟢)3.134s (-10.2% 🟢)0.858s101.00x
▲ VercelNitro2.337s (+1.7%)3.339s (-8.6% 🟢)1.003s101.03x
▲ VercelExpress2.746s (+21.6% 🔺)3.740s (+11.4% 🔺)0.995s101.21x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.745s11.021s0.276s31.00x
💻 LocalExpress10.822s (-0.6%)11.021s (~)0.199s31.01x
🌐 RedisNext.js (Turbopack)10.831s11.026s0.195s31.01x
💻 LocalNitro10.838s (~)11.023s (~)0.185s31.01x
🌐 MongoDBNext.js (Turbopack)12.321s13.025s0.704s31.15x
🐘 PostgresNext.js (Turbopack)15.142s16.046s0.904s21.41x
🐘 PostgresNitro20.293s (+33.2% 🔺)21.058s (+31.2% 🔺)0.766s21.89x
🐘 PostgresExpress20.404s (+6.9% 🔺)21.059s (+7.7% 🔺)0.656s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)17.672s (-0.7%)18.305s (-3.7%)0.633s21.00x
▲ VercelExpress17.692s (~)18.568s (-1.8%)0.876s21.00x
▲ VercelNitro17.791s (+2.4%)18.766s (~)0.975s21.01x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.189s28.050s0.862s31.00x
🌐 RedisNext.js (Turbopack)27.233s28.058s0.825s31.00x
💻 LocalNitro27.534s (~)28.054s (~)0.520s31.01x
💻 LocalExpress27.549s (~)28.053s (~)0.504s31.01x
🌐 MongoDBNext.js (Turbopack)30.633s31.056s0.424s21.13x
🐘 PostgresNext.js (Turbopack)37.971s38.090s0.118s21.40x
🐘 PostgresExpress50.161s (+30.6% 🔺)50.629s (+29.5% 🔺)0.468s21.84x
🐘 PostgresNitro50.337s (+32.9% 🔺)51.131s (+34.2% 🔺)0.795s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express69.695s (+54.5% 🔺)70.938s (+53.0% 🔺)1.243s11.00x
▲ VercelNitro70.423s (+66.2% 🔺)71.351s (+63.6% 🔺)0.928s11.01x
▲ VercelNext.js (Turbopack)70.900s (+64.7% 🔺)72.015s (+63.7% 🔺)1.115s11.02x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)54.959s55.107s0.147s21.00x
💻 LocalNext.js (Turbopack)56.821s57.098s0.277s21.03x
💻 LocalNitro57.415s (~)58.105s (~)0.690s21.04x
💻 LocalExpress57.420s (~)58.102s (~)0.682s21.04x
🌐 MongoDBNext.js (Turbopack)61.261s62.089s0.828s21.11x
🐘 PostgresNext.js (Turbopack)70.379s70.653s0.274s21.28x
🐘 PostgresNitro100.268s (+33.2% 🔺)101.233s (+32.9% 🔺)0.965s11.82x
🐘 PostgresExpress100.368s (+32.8% 🔺)101.239s (+32.9% 🔺)0.871s11.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express88.970s (-4.8%)90.053s (-5.1% 🟢)1.083s11.00x
▲ VercelNext.js (Turbopack)92.487s (~)92.957s (-1.2%)0.470s11.04x
▲ VercelNitro92.749s (~)93.604s (~)0.855s11.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.294s2.007s0.713s151.00x
💻 LocalExpress1.417s (-0.6%)2.006s (~)0.588s151.10x
💻 LocalNext.js (Turbopack)1.423s2.006s0.583s151.10x
💻 LocalNitro1.437s (+3.1%)2.006s (~)0.569s151.11x
🐘 PostgresNext.js (Turbopack)1.993s2.473s0.480s131.54x
🌐 MongoDBNext.js (Turbopack)2.169s3.009s0.840s101.68x
🐘 PostgresNitro2.313s (+4.6%)3.014s (+12.5% 🔺)0.701s101.79x
🐘 PostgresExpress2.373s (+21.7% 🔺)3.013s (+35.4% 🔺)0.640s101.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.497s (-39.3% 🟢)3.294s (-37.6% 🟢)0.798s101.00x
▲ VercelExpress2.670s (-70.5% 🟢)3.634s (-68.7% 🟢)0.964s91.07x
▲ VercelNitro2.807s (-70.1% 🟢)3.832s (-68.6% 🟢)1.024s81.12x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.560s3.008s0.448s101.00x
💻 LocalExpress2.610s (-7.6% 🟢)3.007s (~)0.398s101.02x
🌐 RedisNext.js (Turbopack)2.612s3.009s0.397s101.02x
💻 LocalNitro2.708s (+5.2% 🔺)3.007s (~)0.299s101.06x
🌐 MongoDBNext.js (Turbopack)4.772s5.180s0.408s61.86x
🐘 PostgresExpress8.159s (-12.9% 🟢)8.527s (-12.8% 🟢)0.368s43.19x
🐘 PostgresNitro9.134s (-8.4% 🟢)9.536s (-7.3% 🟢)0.402s43.57x
🐘 PostgresNext.js (Turbopack)12.189s12.367s0.178s34.76x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.961s (-82.2% 🟢)3.854s (-78.2% 🟢)0.894s81.00x
▲ VercelExpress3.024s (-83.0% 🟢)4.186s (-78.4% 🟢)1.162s81.02x
▲ VercelNitro3.030s (-78.9% 🟢)3.872s (-75.1% 🟢)0.842s81.02x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.130s5.013s0.883s61.00x
💻 LocalExpress7.513s (-10.7% 🟢)8.017s (-11.1% 🟢)0.505s41.82x
💻 LocalNext.js (Turbopack)7.596s8.265s0.669s41.84x
💻 LocalNitro7.637s (+5.4% 🔺)8.020s (~)0.383s41.85x
🌐 MongoDBNext.js (Turbopack)9.989s10.683s0.694s32.42x
🐘 PostgresNitro41.518s (-16.0% 🟢)42.127s (-15.9% 🟢)0.609s110.05x
🐘 PostgresExpress49.459s (-3.1%)50.130s (-2.0%)0.671s111.98x
🐘 PostgresNext.js (Turbopack)56.245s57.121s0.876s113.62x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.822s (-83.1% 🟢)5.752s (-75.8% 🟢)1.929s61.00x
▲ VercelNext.js (Turbopack)3.886s (-80.6% 🟢)4.752s (-77.2% 🟢)0.866s71.02x
▲ VercelExpress6.036s (+45.8% 🔺)7.351s (+34.5% 🔺)1.315s51.58x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.260s2.007s0.746s151.00x
💻 LocalExpress1.413s (-4.4%)2.006s (~)0.593s151.12x
💻 LocalNext.js (Turbopack)1.435s2.005s0.570s151.14x
💻 LocalNitro1.442s (+1.0%)2.006s (~)0.563s151.14x
🐘 PostgresNext.js (Turbopack)1.628s2.011s0.383s151.29x
🐘 PostgresNitro2.034s (-5.5% 🟢)2.922s (+6.6% 🔺)0.888s111.61x
🐘 PostgresExpress2.054s (-3.4%)2.681s (+3.3%)0.627s121.63x
🌐 MongoDBNext.js (Turbopack)2.170s3.007s0.837s101.72x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.243s (-5.5% 🟢)3.190s (-5.1% 🟢)0.947s101.00x
▲ VercelNext.js (Turbopack)2.279s (-4.7%)3.202s (-2.6%)0.924s101.02x
▲ VercelExpress2.343s (+4.7%)3.500s (+8.7% 🔺)1.156s91.04x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)2.545s3.009s0.464s101.00x
💻 LocalNext.js (Turbopack)2.657s3.009s0.352s101.04x
💻 LocalExpress2.730s (-3.8%)3.008s (~)0.279s101.07x
💻 LocalNitro2.745s (+1.1%)3.008s (~)0.262s101.08x
🌐 MongoDBNext.js (Turbopack)4.737s5.175s0.438s61.86x
🐘 PostgresExpress10.086s (-17.6% 🟢)10.696s (-15.8% 🟢)0.610s33.96x
🐘 PostgresNitro10.218s (+2.1%)11.032s (+3.1%)0.815s34.02x
🐘 PostgresNext.js (Turbopack)13.493s13.703s0.210s35.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.400s (-22.6% 🟢)3.309s (-19.9% 🟢)0.909s101.00x
▲ VercelNext.js (Turbopack)2.418s (-9.1% 🟢)3.105s (-16.2% 🟢)0.687s101.01x
▲ VercelExpress2.790s (-9.9% 🟢)3.753s (-7.1% 🟢)0.963s81.16x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.166s5.012s0.846s61.00x
💻 LocalNext.js (Turbopack)7.619s8.016s0.398s41.83x
💻 LocalExpress8.076s (-6.4% 🟢)8.773s (-2.8%)0.697s41.94x
💻 LocalNitro8.131s (+5.2% 🔺)9.021s (+9.1% 🔺)0.890s41.95x
🌐 MongoDBNext.js (Turbopack)9.835s10.349s0.513s32.36x
🐘 PostgresExpress50.121s (-2.8%)51.108s (-1.9%)0.987s112.03x
🐘 PostgresNitro50.610s (+0.9%)51.124s (~)0.514s112.15x
🐘 PostgresNext.js (Turbopack)56.644s57.134s0.490s113.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.941s (-3.2%)4.317s (+10.5% 🔺)1.376s71.00x
▲ VercelNitro3.249s (+11.5% 🔺)4.335s (+6.0% 🔺)1.085s81.10x
▲ VercelNext.js (Turbopack)4.099s (+28.6% 🔺)5.068s (+25.3% 🔺)0.969s61.39x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.147s1.002s0.012s1.017s0.870s101.00x
🌐 RedisNext.js (Turbopack)0.170s1.000s0.002s1.008s0.838s101.16x
💻 LocalNitro0.173s (+2.2%)1.002s (~)0.011s (+0.9%)1.017s (~)0.844s101.17x
💻 LocalExpress0.175s (-3.2%)1.002s (~)0.011s (-5.1% 🟢)1.016s (~)0.842s101.19x
🌐 MongoDBNext.js (Turbopack)0.492s0.956s0.001s1.009s0.517s103.35x
🐘 PostgresNext.js (Turbopack)0.782s0.750s0.001s1.010s0.228s105.32x
🐘 PostgresNitro2.372s (+76.5% 🔺)2.672s (+57.6% 🔺)0.001s (+16.7% 🔺)3.015s (+49.8% 🔺)0.644s1016.12x
🐘 PostgresExpress2.473s (+69.1% 🔺)2.569s (+55.2% 🔺)0.002s (+25.0% 🔺)3.015s (+49.8% 🔺)0.542s1016.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.068s (-21.7% 🟢)2.618s (-27.6% 🟢)0.180s (-13.3% 🟢)3.394s (-22.4% 🟢)1.326s101.00x
▲ VercelNext.js (Turbopack)2.147s (+5.4% 🔺)2.583s (-1.2%)0.227s (-2.6%)3.457s (~)1.310s101.04x
▲ VercelExpress2.244s (-28.9% 🟢)2.818s (-45.6% 🟢)0.244s (+29.3% 🔺)3.719s (-39.2% 🟢)1.475s101.08x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)8/12
🐘 PostgresNext.js (Turbopack)7/12
▲ VercelNitro5/12
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local10/12
Next.js (Turbopack)💻 Local6/12
Nitro💻 Local9/12
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

@TooTallNate
TooTallNate marked this pull request as ready for review February 16, 2026 17:27
@github-actions

github-actionsBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production512038550
✅ 💻 Local Development532068600
✅ 📦 Local Production532068600
✅ 🐘 Local Postgres532068600
✅ 🪟 Windows470350
❌ 🌍 Community Worlds107439159
✅ 📋 Other129021150
Total2391432752709

❌ Failed Tests

🌍 Community Worlds (43 failed)

turso (43 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • parallelSleepWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • 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 retry behavior workflow completes despite transient 5xx on step_completed
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • closureVariableWorkflow - nested step functions with closure variables
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4604
✅ example4604
✅ express4604
✅ fastify4604
✅ hono4604
✅ nextjs-turbopack4901
✅ nextjs-webpack4901
✅ nitro4604
✅ nuxt4604
✅ sveltekit4604
✅ vite4604
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4703
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
✅ mongodb4703
✅ redis-dev300
✅ redis4703
✅ turso-dev300
❌ turso4433
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4307
✅ e2e-local-postgres-nest-stable4307
✅ e2e-local-prod-nest-stable4307

📋 View full workflow run

CopilotAI review requested due to automatic review settings February 16, 2026 17:27
@TooTallNateGraphite App

Copy link
Copy Markdown
MemberAuthor

This stack of pull requests is managed by Graphite. Learn more about stacking.

@vercel

vercelBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-astro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-express-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-fastify-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-hono-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nitro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nuxt-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-sveltekit-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-vite-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-nestReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-swc-playgroundReadyReadyPreview, CommentFeb 16, 2026 5:56pm
1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
workflow-docsSkippedSkippedFeb 16, 2026 5:56pm

@TooTallNate
TooTallNate requested review from a team and removed request for CopilotFebruary 16, 2026 17:27
Comment threadpackages/workflow/package.json
…ge.json breaks 5 files in `packages/world-testing` that still import `hydrateWorkflowReturnValue` from that path.
Co-authored-by: TooTallNate <n@n8.io>
CopilotAI review requested due to automatic review settings February 16, 2026 17:52
@TooTallNate
TooTallNate removed the request for review from CopilotFebruary 16, 2026 17:52
@vercel
vercelBottemporarily deployed to Preview – workflow-docs February 16, 2026 17:52 Inactive

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request removes the workflow/internal/serialization export which was only being used in e2e test files. The changes simplify the codebase by:

  • Removing the re-export wrapper in the workflow package
  • Migrating test files to use direct imports from @workflow/core/serialization
  • Simplifying NextJS Pages Router API endpoints to use JSON instead of binary serialization

Changes:

  • Removed workflow/internal/serialization export from the workflow package
  • Updated world-testing files to import from @workflow/core/serialization directly
  • Simplified NextJS test API endpoints to use JSON body parsing instead of binary serialization

Reviewed changes

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

Show a summary per file
FileDescription
packages/workflow/src/internal/serialization.tsDeleted re-export file
packages/workflow/package.jsonRemoved export entry for internal/serialization
workbench/nextjs-webpack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
workbench/nextjs-turbopack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
packages/world-testing/src/*.mtsUpdated imports to use @workflow/core/serialization
packages/world-testing/package.jsonAdded @workflow/core dependency
pnpm-lock.yamlUpdated lockfile for new dependency
.changeset/cyan-ravens-eat.mdAdded changeset documenting the removal
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

@TooTallNate
TooTallNate merged commit 0946dad into mainFeb 16, 2026
110 checks passed
@TooTallNate
TooTallNate deleted the 02-16-remove_workflow_internal_serialization_export branch February 16, 2026 19:50
pranaygp added a commit that referenced this pull request Feb 17, 2026
* origin/main:
Remove "workflow/internal/serialization" export (#1082)
TooTallNate added a commit that referenced this pull request Apr 19, 2026
…/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
TooTallNate added a commit that referenced this pull request Apr 30, 2026
…isites
Standardize on `Symbol.for('workflow-serialize')` /
`Symbol.for('workflow-deserialize')` everywhere — the parallel
`globalThis.__wdk_serialize` / `__wdk_deserialize` aliases have been
removed from `vm-bundle-entry.ts` and the snapshot runtime's inline
JS strings now use the symbol form directly. Single canonical name,
no duplication.
Drop the `?? Math.random` and `?? Date.now()` fallbacks from the
ULID generator setup. Both prerequisites
(`globalThis.__ulidTimestamp` and the host-replaced seeded
`Math.random`) are always set by `snapshot-runtime.ts` before the
serde bundle is evaluated; silently falling back to unseeded
`Math.random` or live `Date.now()` would re-introduce the
non-determinism we deliberately fixed (concurrent VM invocations of
the same resumption must produce identical correlationIds for the
world's EntityConflictError dedup to work). Now throws if
`__ulidTimestamp` isn't a number, and passes the seeded
`Math.random` reference explicitly to `monotonicFactory` so
upstream's `detectPRNG` never runs (it'd throw in QuickJS anyway,
since `crypto` is unavailable).
Drop the `URL` / `URLSearchParams` / `DOMException` availability
guards in `common-vm.ts`. quickjs-wasi's URL extension is always
loaded (`url.so`) and DOMException is always constructible — the
guards were dead code carried over from when those weren't reliably
available. The reducer/reviver code is now straightforward
`instanceof URL` / `new URL(...)` / `new DOMException(...)`.
Remove `packages/core/src/serialization/base64.ts` and its
sub-path exports (`./serialization/workflow`,
`./serialization/workflow-vm`). The pure-JS base64 helpers were
leftover from before `base64.so` shipped `btoa`/`atob` natively;
the VM-side reducers in `common-vm.ts` now build base64 strings via
the native ones. The sub-path exports had zero consumers in this
repo (the same cleanup landed on the `serialization-refactor`
branch in 05e0fee but never made it onto `snapshot-runtime`
because the branches diverged earlier).
Remove `packages/workflow/src/internal/serialization.ts` and its
`./internal/serialization` package.json export. Same story — zero
consumers, previously removed in #1082, then accidentally
reintroduced via `f04fd8e91`.
TooTallNate added a commit that referenced this pull request May 1, 2026
…to existing pipeline (#1299)
* Add serialization module foundation: types, codec interface, format prefix
Start of the serialization refactor (separate from snapshot-runtime).
New files:
- serialization/types.ts — SerializationFormat enum, SerializableSpecial
interface, Reducers/Revivers types
- serialization/codec.ts — Codec interface with formatPrefix, serialize,
deserialize, and optional deserializeLegacy
- serialization/format.ts — Format prefix encode/decode/peek, moved from
the monolithic serialization.ts
The Codec interface enables future alternative formats (CBOR, JSON) while
keeping the devalue implementation as the current default.
* Add reducers, devalue codec, encryption, and mode-specific modules
Serialization refactor Phase 1: create the new module structure alongside
the existing monolithic serialization.ts (which continues to work).
New files:
- serialization/reducers/common.ts — Date, Error, Map, Set, URL, BigInt,
typed arrays, Headers, Request, Response, RegExp, URLSearchParams
- serialization/reducers/class.ts — Class/Instance with WORKFLOW_SERIALIZE/
DESERIALIZE support
- serialization/reducers/step-function.ts — StepFunction with closure vars
- serialization/codec-devalue.ts — devalue Codec implementation
- serialization/encryption.ts — composable encrypt/decrypt layer
- serialization/workflow.ts — synchronous, no encryption, for VM use
- serialization/step.ts — async with encryption, for step handler
- serialization/client.ts — async with encryption, for start() API
- serialization/index.ts — re-exports all public API
- serialization/serialization.test.ts — 25 focused tests
All modes compose their reducer/reviver sets from the shared building blocks.
Cross-mode compatibility verified: data serialized in any mode can be
deserialized in any other mode (for common types).
Existing 108 serialization tests continue to pass unchanged.
* Add sub-path exports for workflow serialization module
- Add ./serialization/workflow export to @workflow/core package.json
- Add ./internal/serialization re-export to workflow meta-package
- The workflow bundle can now import serialize/deserialize via:
import { serialize, deserialize } from 'workflow/internal/serialization'
Full test suite passes: 493 tests across 22 files (including 25 new
serialization module tests).
* Address code review feedback
1. Fix reducer composition order: Class/Instance reducers now come BEFORE
common reducers in all three modes (workflow, step, client). This ensures
custom Error subclasses with WORKFLOW_SERIALIZE are handled by the
Instance reducer before the generic Error reducer (devalue uses
first-match-wins semantics).
2. Fix encryption decrypt() to fail fast when encrypted data is encountered
without a decryption key, instead of silently returning encrypted bytes
that would fail later with an unhelpful format error.
3. Remove Request/Response from common reducers — they don't have matching
common revivers, so including them caused asymmetric behavior (serialize
as Request, deserialize as plain object). Request/Response handling
belongs in mode-specific modules that can provide proper revivers.
4. Document Node.js dependency in the workflow serialization re-export.
The current implementation uses node:util and Buffer. For the QuickJS
VM (snapshot runtime), these will need polyfills — tracked separately.
* Move reducer/reviver composition into the devalue codec
The Codec interface now takes a SerializationMode ('workflow', 'step',
'client') instead of raw reducers/revivers. The reducer/reviver
composition is internal to the devalue codec implementation.
This is the right abstraction because reducers/revivers are devalue-
specific concepts. A future CBOR codec would handle Date, typed arrays,
Map, Set natively via the CBOR type system — it wouldn't use reducers
at all. A JSON codec would only support standard JSON types.
The mode-specific modules (workflow.ts, step.ts, client.ts) are now
simpler — they just pass the mode string to the codec.
* Replace SerializationFormatType enum with open-ended FormatPrefix type
The format prefix is now a branded string type validated by
isFormatPrefix() — any 4-character [a-z0-9] string is valid.
This removes the hard-coded enum of known formats, making the system
truly open for extension:
type FormatPrefix = string & { __brand: 'FormatPrefix' };
function isFormatPrefix(value: string): value is FormatPrefix;
The SerializationFormat object still provides well-known constants
('devl', 'encr') but they're now just typed constants, not an
exhaustive enum.
peekFormatPrefix() and decodeFormatPrefix() use isFormatPrefix() for
validation instead of checking against a known list. Unknown but valid
prefixes (e.g. 'cbor', 'json', 'v2b1') are accepted — the caller
decides whether they can handle the format.
6 new isFormatPrefix tests covering: valid strings, too short, too long,
uppercase, special characters. 1 new test for unknown-but-valid prefixes.
* Wire modular serialization modules into serialization.ts, add 138 unit tests
Replace duplicate format prefix, reducer/reviver, and encryption helper
code in the monolithic serialization.ts with imports from the modular
serialization/ directory. This completes the refactoring started in the
earlier additive-only commits.
Key changes:
- serialization.ts now imports types, format prefix, common/class/step-function
reducers and revivers, and encryption helpers from ./serialization/ modules
- Removed ~450 lines of duplicate code from serialization.ts
- Made encryption error messages consistent between old and new modules
- Added 138 comprehensive unit tests covering types, format prefix,
encryption, codec, all three reducer modules, all three mode modules,
cross-mode compatibility, and edge cases
- Updated one existing test assertion for new error message wording
* Address code review feedback
- encryption.ts: throw WorkflowRuntimeError instead of plain Error in
decrypt() to preserve the error contract from legacy maybeDecrypt()
- format.ts: document that open-ended prefix validation ([a-z0-9]{4})
is intentional for forward compatibility — callers check support
- errors.ts: extract duplicated formatSerializationError into shared
utility, remove 4 copies from workflow.ts, step.ts, client.ts
- codec-devalue.ts: document that globalThis default is a known
limitation; legacy dehydrate/hydrate path still supports custom global
* Fix codec-devalue.ts comment: clarify modular modules are not used in current runtime
The globalThis default is not a limitation for the current runtime —
all serialization goes through dehydrate*/hydrate* in serialization.ts
which passes the correct global. The modular modules are infrastructure
for the future snapshot runtime where serialization runs inside the VM.
* Wire dehydrate/hydrate functions through modular serialize/deserialize
The dehydrate*/hydrate* functions in serialization.ts now delegate to
the modular mode modules (workflowModule, stepModule, clientModule)
instead of directly calling devalue stringify/parse/unflatten.
Key changes:
- Extended Codec interface with CodecOptions (global, extraReducers,
extraRevivers) so the codec can receive VM globals and mode-specific
stream/Request/Response handlers
- devalueCodec threads global through to all reducer/reviver factories
so instanceof checks work across VM boundaries
- Mode modules (workflow.ts, step.ts, client.ts) accept CodecOptions
and pass them through to the codec
- dehydrate*/hydrate* functions now call module serialize/deserialize
with stream and Request/Response reducers/revivers passed as extras
- v1Compat path remains inline (pre-codec, uses stringify + revive)
- Error context strings preserved via try/catch re-wrapping
* Bump changeset from patch to minor for serialization refactor
Return types of public get*Reducers/get*Revivers functions narrowed
from Reducers/Revivers to Partial<Reducers>/Partial<Revivers>, which
is a TypeScript-level breaking change. Also adds new sub-path exports
(@workflow/core/serialization/workflow, workflow/internal/serialization)
which is additive. Minor bump is the appropriate semver for both.
* Remove unused workflow/internal/serialization re-export and @workflow/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
* Downgrade changeset from minor to patch
After auditing actual consumers of the narrowed return types
(getExternalReducers/getWorkflowReducers/getExternalRevivers/getWorkflowRevivers
now return Partial<Reducers>/Partial<Revivers>), no in-repo or external
consumer indexes specific keys on the returned object in a way that would
break. The only internal caller that did (runtime/run.ts) was updated in
this same PR. The narrowing is type-safer but effectively invisible at
runtime and for idiomatic callers that spread or forward the object.
Since the refactor is internally restructuring only, patch is the
appropriate semver bump.
* Trim serialization-refactor changeset
* Dedup formatSerializationError: import from serialization/errors.ts
The legacy serialization.ts had its own inlined copy of
formatSerializationError. Now that the helper is exported from
serialization/errors.ts (already consumed by workflow.ts/step.ts/client.ts),
import it here too to keep the single source of truth.
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.

4 participants

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

Remove "workflow/internal/serialization" export - #1082

Merged
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export
Feb 16, 2026
Merged

Remove "workflow/internal/serialization" export#1082
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export

Conversation

@TooTallNate

Copy link
Copy Markdown
Member

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.
@changeset-bot

changeset-botBot commented Feb 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: aa8c9bd

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

This PR includes changesets to release 14 packages
NameType
workflowPatch
@workflow/world-testingPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@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 Feb 16, 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🥇 Express0.032s (-6.9% 🟢)1.005s (~)0.973s101.00x
💻 LocalNitro0.032s (+2.5%)1.005s (~)0.972s101.00x
💻 LocalNext.js (Turbopack)0.043s1.005s0.962s101.33x
🌐 RedisNext.js (Turbopack)0.056s1.005s0.949s101.74x
🌐 MongoDBNext.js (Turbopack)0.103s1.007s0.904s103.21x
🐘 PostgresExpress0.155s (-23.4% 🟢)1.010s (-0.8%)0.854s104.82x
🐘 PostgresNitro0.251s (+10.8% 🔺)1.010s (-0.9%)0.758s107.81x
🐘 PostgresNext.js (Turbopack)0.370s1.009s0.639s1011.50x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.544s (-14.7% 🟢)1.950s (-9.5% 🟢)1.405s101.00x
▲ VercelNext.js (Turbopack)0.597s (-27.3% 🟢)1.924s (-10.9% 🟢)1.327s101.10x
▲ VercelExpress0.654s (+8.9% 🔺)2.105s (+2.0%)1.451s101.20x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.096s2.006s0.910s101.00x
💻 LocalExpress1.105s (~)2.006s (~)0.901s101.01x
💻 LocalNitro1.106s (~)2.006s (~)0.901s101.01x
🌐 RedisNext.js (Turbopack)1.130s2.007s0.877s101.03x
🌐 MongoDBNext.js (Turbopack)1.310s2.008s0.698s101.20x
🐘 PostgresNext.js (Turbopack)2.254s3.015s0.761s102.06x
🐘 PostgresExpress2.376s (-3.5%)3.014s (~)0.638s102.17x
🐘 PostgresNitro2.425s (+6.8% 🔺)3.014s (+7.1% 🔺)0.590s102.21x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.276s (-5.5% 🟢)3.134s (-10.2% 🟢)0.858s101.00x
▲ VercelNitro2.337s (+1.7%)3.339s (-8.6% 🟢)1.003s101.03x
▲ VercelExpress2.746s (+21.6% 🔺)3.740s (+11.4% 🔺)0.995s101.21x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.745s11.021s0.276s31.00x
💻 LocalExpress10.822s (-0.6%)11.021s (~)0.199s31.01x
🌐 RedisNext.js (Turbopack)10.831s11.026s0.195s31.01x
💻 LocalNitro10.838s (~)11.023s (~)0.185s31.01x
🌐 MongoDBNext.js (Turbopack)12.321s13.025s0.704s31.15x
🐘 PostgresNext.js (Turbopack)15.142s16.046s0.904s21.41x
🐘 PostgresNitro20.293s (+33.2% 🔺)21.058s (+31.2% 🔺)0.766s21.89x
🐘 PostgresExpress20.404s (+6.9% 🔺)21.059s (+7.7% 🔺)0.656s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)17.672s (-0.7%)18.305s (-3.7%)0.633s21.00x
▲ VercelExpress17.692s (~)18.568s (-1.8%)0.876s21.00x
▲ VercelNitro17.791s (+2.4%)18.766s (~)0.975s21.01x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.189s28.050s0.862s31.00x
🌐 RedisNext.js (Turbopack)27.233s28.058s0.825s31.00x
💻 LocalNitro27.534s (~)28.054s (~)0.520s31.01x
💻 LocalExpress27.549s (~)28.053s (~)0.504s31.01x
🌐 MongoDBNext.js (Turbopack)30.633s31.056s0.424s21.13x
🐘 PostgresNext.js (Turbopack)37.971s38.090s0.118s21.40x
🐘 PostgresExpress50.161s (+30.6% 🔺)50.629s (+29.5% 🔺)0.468s21.84x
🐘 PostgresNitro50.337s (+32.9% 🔺)51.131s (+34.2% 🔺)0.795s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express69.695s (+54.5% 🔺)70.938s (+53.0% 🔺)1.243s11.00x
▲ VercelNitro70.423s (+66.2% 🔺)71.351s (+63.6% 🔺)0.928s11.01x
▲ VercelNext.js (Turbopack)70.900s (+64.7% 🔺)72.015s (+63.7% 🔺)1.115s11.02x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)54.959s55.107s0.147s21.00x
💻 LocalNext.js (Turbopack)56.821s57.098s0.277s21.03x
💻 LocalNitro57.415s (~)58.105s (~)0.690s21.04x
💻 LocalExpress57.420s (~)58.102s (~)0.682s21.04x
🌐 MongoDBNext.js (Turbopack)61.261s62.089s0.828s21.11x
🐘 PostgresNext.js (Turbopack)70.379s70.653s0.274s21.28x
🐘 PostgresNitro100.268s (+33.2% 🔺)101.233s (+32.9% 🔺)0.965s11.82x
🐘 PostgresExpress100.368s (+32.8% 🔺)101.239s (+32.9% 🔺)0.871s11.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express88.970s (-4.8%)90.053s (-5.1% 🟢)1.083s11.00x
▲ VercelNext.js (Turbopack)92.487s (~)92.957s (-1.2%)0.470s11.04x
▲ VercelNitro92.749s (~)93.604s (~)0.855s11.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.294s2.007s0.713s151.00x
💻 LocalExpress1.417s (-0.6%)2.006s (~)0.588s151.10x
💻 LocalNext.js (Turbopack)1.423s2.006s0.583s151.10x
💻 LocalNitro1.437s (+3.1%)2.006s (~)0.569s151.11x
🐘 PostgresNext.js (Turbopack)1.993s2.473s0.480s131.54x
🌐 MongoDBNext.js (Turbopack)2.169s3.009s0.840s101.68x
🐘 PostgresNitro2.313s (+4.6%)3.014s (+12.5% 🔺)0.701s101.79x
🐘 PostgresExpress2.373s (+21.7% 🔺)3.013s (+35.4% 🔺)0.640s101.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.497s (-39.3% 🟢)3.294s (-37.6% 🟢)0.798s101.00x
▲ VercelExpress2.670s (-70.5% 🟢)3.634s (-68.7% 🟢)0.964s91.07x
▲ VercelNitro2.807s (-70.1% 🟢)3.832s (-68.6% 🟢)1.024s81.12x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.560s3.008s0.448s101.00x
💻 LocalExpress2.610s (-7.6% 🟢)3.007s (~)0.398s101.02x
🌐 RedisNext.js (Turbopack)2.612s3.009s0.397s101.02x
💻 LocalNitro2.708s (+5.2% 🔺)3.007s (~)0.299s101.06x
🌐 MongoDBNext.js (Turbopack)4.772s5.180s0.408s61.86x
🐘 PostgresExpress8.159s (-12.9% 🟢)8.527s (-12.8% 🟢)0.368s43.19x
🐘 PostgresNitro9.134s (-8.4% 🟢)9.536s (-7.3% 🟢)0.402s43.57x
🐘 PostgresNext.js (Turbopack)12.189s12.367s0.178s34.76x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.961s (-82.2% 🟢)3.854s (-78.2% 🟢)0.894s81.00x
▲ VercelExpress3.024s (-83.0% 🟢)4.186s (-78.4% 🟢)1.162s81.02x
▲ VercelNitro3.030s (-78.9% 🟢)3.872s (-75.1% 🟢)0.842s81.02x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.130s5.013s0.883s61.00x
💻 LocalExpress7.513s (-10.7% 🟢)8.017s (-11.1% 🟢)0.505s41.82x
💻 LocalNext.js (Turbopack)7.596s8.265s0.669s41.84x
💻 LocalNitro7.637s (+5.4% 🔺)8.020s (~)0.383s41.85x
🌐 MongoDBNext.js (Turbopack)9.989s10.683s0.694s32.42x
🐘 PostgresNitro41.518s (-16.0% 🟢)42.127s (-15.9% 🟢)0.609s110.05x
🐘 PostgresExpress49.459s (-3.1%)50.130s (-2.0%)0.671s111.98x
🐘 PostgresNext.js (Turbopack)56.245s57.121s0.876s113.62x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.822s (-83.1% 🟢)5.752s (-75.8% 🟢)1.929s61.00x
▲ VercelNext.js (Turbopack)3.886s (-80.6% 🟢)4.752s (-77.2% 🟢)0.866s71.02x
▲ VercelExpress6.036s (+45.8% 🔺)7.351s (+34.5% 🔺)1.315s51.58x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.260s2.007s0.746s151.00x
💻 LocalExpress1.413s (-4.4%)2.006s (~)0.593s151.12x
💻 LocalNext.js (Turbopack)1.435s2.005s0.570s151.14x
💻 LocalNitro1.442s (+1.0%)2.006s (~)0.563s151.14x
🐘 PostgresNext.js (Turbopack)1.628s2.011s0.383s151.29x
🐘 PostgresNitro2.034s (-5.5% 🟢)2.922s (+6.6% 🔺)0.888s111.61x
🐘 PostgresExpress2.054s (-3.4%)2.681s (+3.3%)0.627s121.63x
🌐 MongoDBNext.js (Turbopack)2.170s3.007s0.837s101.72x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.243s (-5.5% 🟢)3.190s (-5.1% 🟢)0.947s101.00x
▲ VercelNext.js (Turbopack)2.279s (-4.7%)3.202s (-2.6%)0.924s101.02x
▲ VercelExpress2.343s (+4.7%)3.500s (+8.7% 🔺)1.156s91.04x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)2.545s3.009s0.464s101.00x
💻 LocalNext.js (Turbopack)2.657s3.009s0.352s101.04x
💻 LocalExpress2.730s (-3.8%)3.008s (~)0.279s101.07x
💻 LocalNitro2.745s (+1.1%)3.008s (~)0.262s101.08x
🌐 MongoDBNext.js (Turbopack)4.737s5.175s0.438s61.86x
🐘 PostgresExpress10.086s (-17.6% 🟢)10.696s (-15.8% 🟢)0.610s33.96x
🐘 PostgresNitro10.218s (+2.1%)11.032s (+3.1%)0.815s34.02x
🐘 PostgresNext.js (Turbopack)13.493s13.703s0.210s35.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.400s (-22.6% 🟢)3.309s (-19.9% 🟢)0.909s101.00x
▲ VercelNext.js (Turbopack)2.418s (-9.1% 🟢)3.105s (-16.2% 🟢)0.687s101.01x
▲ VercelExpress2.790s (-9.9% 🟢)3.753s (-7.1% 🟢)0.963s81.16x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.166s5.012s0.846s61.00x
💻 LocalNext.js (Turbopack)7.619s8.016s0.398s41.83x
💻 LocalExpress8.076s (-6.4% 🟢)8.773s (-2.8%)0.697s41.94x
💻 LocalNitro8.131s (+5.2% 🔺)9.021s (+9.1% 🔺)0.890s41.95x
🌐 MongoDBNext.js (Turbopack)9.835s10.349s0.513s32.36x
🐘 PostgresExpress50.121s (-2.8%)51.108s (-1.9%)0.987s112.03x
🐘 PostgresNitro50.610s (+0.9%)51.124s (~)0.514s112.15x
🐘 PostgresNext.js (Turbopack)56.644s57.134s0.490s113.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.941s (-3.2%)4.317s (+10.5% 🔺)1.376s71.00x
▲ VercelNitro3.249s (+11.5% 🔺)4.335s (+6.0% 🔺)1.085s81.10x
▲ VercelNext.js (Turbopack)4.099s (+28.6% 🔺)5.068s (+25.3% 🔺)0.969s61.39x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.147s1.002s0.012s1.017s0.870s101.00x
🌐 RedisNext.js (Turbopack)0.170s1.000s0.002s1.008s0.838s101.16x
💻 LocalNitro0.173s (+2.2%)1.002s (~)0.011s (+0.9%)1.017s (~)0.844s101.17x
💻 LocalExpress0.175s (-3.2%)1.002s (~)0.011s (-5.1% 🟢)1.016s (~)0.842s101.19x
🌐 MongoDBNext.js (Turbopack)0.492s0.956s0.001s1.009s0.517s103.35x
🐘 PostgresNext.js (Turbopack)0.782s0.750s0.001s1.010s0.228s105.32x
🐘 PostgresNitro2.372s (+76.5% 🔺)2.672s (+57.6% 🔺)0.001s (+16.7% 🔺)3.015s (+49.8% 🔺)0.644s1016.12x
🐘 PostgresExpress2.473s (+69.1% 🔺)2.569s (+55.2% 🔺)0.002s (+25.0% 🔺)3.015s (+49.8% 🔺)0.542s1016.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.068s (-21.7% 🟢)2.618s (-27.6% 🟢)0.180s (-13.3% 🟢)3.394s (-22.4% 🟢)1.326s101.00x
▲ VercelNext.js (Turbopack)2.147s (+5.4% 🔺)2.583s (-1.2%)0.227s (-2.6%)3.457s (~)1.310s101.04x
▲ VercelExpress2.244s (-28.9% 🟢)2.818s (-45.6% 🟢)0.244s (+29.3% 🔺)3.719s (-39.2% 🟢)1.475s101.08x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)8/12
🐘 PostgresNext.js (Turbopack)7/12
▲ VercelNitro5/12
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local10/12
Next.js (Turbopack)💻 Local6/12
Nitro💻 Local9/12
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

@TooTallNate
TooTallNate marked this pull request as ready for review February 16, 2026 17:27
@github-actions

github-actionsBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production512038550
✅ 💻 Local Development532068600
✅ 📦 Local Production532068600
✅ 🐘 Local Postgres532068600
✅ 🪟 Windows470350
❌ 🌍 Community Worlds107439159
✅ 📋 Other129021150
Total2391432752709

❌ Failed Tests

🌍 Community Worlds (43 failed)

turso (43 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • parallelSleepWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • 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 retry behavior workflow completes despite transient 5xx on step_completed
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • closureVariableWorkflow - nested step functions with closure variables
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4604
✅ example4604
✅ express4604
✅ fastify4604
✅ hono4604
✅ nextjs-turbopack4901
✅ nextjs-webpack4901
✅ nitro4604
✅ nuxt4604
✅ sveltekit4604
✅ vite4604
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4703
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
✅ mongodb4703
✅ redis-dev300
✅ redis4703
✅ turso-dev300
❌ turso4433
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4307
✅ e2e-local-postgres-nest-stable4307
✅ e2e-local-prod-nest-stable4307

📋 View full workflow run

CopilotAI review requested due to automatic review settings February 16, 2026 17:27
@TooTallNateGraphite App

Copy link
Copy Markdown
MemberAuthor

This stack of pull requests is managed by Graphite. Learn more about stacking.

@vercel

vercelBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-astro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-express-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-fastify-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-hono-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nitro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nuxt-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-sveltekit-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-vite-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-nestReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-swc-playgroundReadyReadyPreview, CommentFeb 16, 2026 5:56pm
1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
workflow-docsSkippedSkippedFeb 16, 2026 5:56pm

@TooTallNate
TooTallNate requested review from a team and removed request for CopilotFebruary 16, 2026 17:27
Comment threadpackages/workflow/package.json
…ge.json breaks 5 files in `packages/world-testing` that still import `hydrateWorkflowReturnValue` from that path.
Co-authored-by: TooTallNate <n@n8.io>
CopilotAI review requested due to automatic review settings February 16, 2026 17:52
@TooTallNate
TooTallNate removed the request for review from CopilotFebruary 16, 2026 17:52
@vercel
vercelBottemporarily deployed to Preview – workflow-docs February 16, 2026 17:52 Inactive

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request removes the workflow/internal/serialization export which was only being used in e2e test files. The changes simplify the codebase by:

  • Removing the re-export wrapper in the workflow package
  • Migrating test files to use direct imports from @workflow/core/serialization
  • Simplifying NextJS Pages Router API endpoints to use JSON instead of binary serialization

Changes:

  • Removed workflow/internal/serialization export from the workflow package
  • Updated world-testing files to import from @workflow/core/serialization directly
  • Simplified NextJS test API endpoints to use JSON body parsing instead of binary serialization

Reviewed changes

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

Show a summary per file
FileDescription
packages/workflow/src/internal/serialization.tsDeleted re-export file
packages/workflow/package.jsonRemoved export entry for internal/serialization
workbench/nextjs-webpack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
workbench/nextjs-turbopack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
packages/world-testing/src/*.mtsUpdated imports to use @workflow/core/serialization
packages/world-testing/package.jsonAdded @workflow/core dependency
pnpm-lock.yamlUpdated lockfile for new dependency
.changeset/cyan-ravens-eat.mdAdded changeset documenting the removal
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

@TooTallNate
TooTallNate merged commit 0946dad into mainFeb 16, 2026
110 checks passed
@TooTallNate
TooTallNate deleted the 02-16-remove_workflow_internal_serialization_export branch February 16, 2026 19:50
pranaygp added a commit that referenced this pull request Feb 17, 2026
* origin/main:
Remove "workflow/internal/serialization" export (#1082)
TooTallNate added a commit that referenced this pull request Apr 19, 2026
…/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
TooTallNate added a commit that referenced this pull request Apr 30, 2026
…isites
Standardize on `Symbol.for('workflow-serialize')` /
`Symbol.for('workflow-deserialize')` everywhere — the parallel
`globalThis.__wdk_serialize` / `__wdk_deserialize` aliases have been
removed from `vm-bundle-entry.ts` and the snapshot runtime's inline
JS strings now use the symbol form directly. Single canonical name,
no duplication.
Drop the `?? Math.random` and `?? Date.now()` fallbacks from the
ULID generator setup. Both prerequisites
(`globalThis.__ulidTimestamp` and the host-replaced seeded
`Math.random`) are always set by `snapshot-runtime.ts` before the
serde bundle is evaluated; silently falling back to unseeded
`Math.random` or live `Date.now()` would re-introduce the
non-determinism we deliberately fixed (concurrent VM invocations of
the same resumption must produce identical correlationIds for the
world's EntityConflictError dedup to work). Now throws if
`__ulidTimestamp` isn't a number, and passes the seeded
`Math.random` reference explicitly to `monotonicFactory` so
upstream's `detectPRNG` never runs (it'd throw in QuickJS anyway,
since `crypto` is unavailable).
Drop the `URL` / `URLSearchParams` / `DOMException` availability
guards in `common-vm.ts`. quickjs-wasi's URL extension is always
loaded (`url.so`) and DOMException is always constructible — the
guards were dead code carried over from when those weren't reliably
available. The reducer/reviver code is now straightforward
`instanceof URL` / `new URL(...)` / `new DOMException(...)`.
Remove `packages/core/src/serialization/base64.ts` and its
sub-path exports (`./serialization/workflow`,
`./serialization/workflow-vm`). The pure-JS base64 helpers were
leftover from before `base64.so` shipped `btoa`/`atob` natively;
the VM-side reducers in `common-vm.ts` now build base64 strings via
the native ones. The sub-path exports had zero consumers in this
repo (the same cleanup landed on the `serialization-refactor`
branch in 05e0fee but never made it onto `snapshot-runtime`
because the branches diverged earlier).
Remove `packages/workflow/src/internal/serialization.ts` and its
`./internal/serialization` package.json export. Same story — zero
consumers, previously removed in #1082, then accidentally
reintroduced via `f04fd8e91`.
TooTallNate added a commit that referenced this pull request May 1, 2026
…to existing pipeline (#1299)
* Add serialization module foundation: types, codec interface, format prefix
Start of the serialization refactor (separate from snapshot-runtime).
New files:
- serialization/types.ts — SerializationFormat enum, SerializableSpecial
interface, Reducers/Revivers types
- serialization/codec.ts — Codec interface with formatPrefix, serialize,
deserialize, and optional deserializeLegacy
- serialization/format.ts — Format prefix encode/decode/peek, moved from
the monolithic serialization.ts
The Codec interface enables future alternative formats (CBOR, JSON) while
keeping the devalue implementation as the current default.
* Add reducers, devalue codec, encryption, and mode-specific modules
Serialization refactor Phase 1: create the new module structure alongside
the existing monolithic serialization.ts (which continues to work).
New files:
- serialization/reducers/common.ts — Date, Error, Map, Set, URL, BigInt,
typed arrays, Headers, Request, Response, RegExp, URLSearchParams
- serialization/reducers/class.ts — Class/Instance with WORKFLOW_SERIALIZE/
DESERIALIZE support
- serialization/reducers/step-function.ts — StepFunction with closure vars
- serialization/codec-devalue.ts — devalue Codec implementation
- serialization/encryption.ts — composable encrypt/decrypt layer
- serialization/workflow.ts — synchronous, no encryption, for VM use
- serialization/step.ts — async with encryption, for step handler
- serialization/client.ts — async with encryption, for start() API
- serialization/index.ts — re-exports all public API
- serialization/serialization.test.ts — 25 focused tests
All modes compose their reducer/reviver sets from the shared building blocks.
Cross-mode compatibility verified: data serialized in any mode can be
deserialized in any other mode (for common types).
Existing 108 serialization tests continue to pass unchanged.
* Add sub-path exports for workflow serialization module
- Add ./serialization/workflow export to @workflow/core package.json
- Add ./internal/serialization re-export to workflow meta-package
- The workflow bundle can now import serialize/deserialize via:
import { serialize, deserialize } from 'workflow/internal/serialization'
Full test suite passes: 493 tests across 22 files (including 25 new
serialization module tests).
* Address code review feedback
1. Fix reducer composition order: Class/Instance reducers now come BEFORE
common reducers in all three modes (workflow, step, client). This ensures
custom Error subclasses with WORKFLOW_SERIALIZE are handled by the
Instance reducer before the generic Error reducer (devalue uses
first-match-wins semantics).
2. Fix encryption decrypt() to fail fast when encrypted data is encountered
without a decryption key, instead of silently returning encrypted bytes
that would fail later with an unhelpful format error.
3. Remove Request/Response from common reducers — they don't have matching
common revivers, so including them caused asymmetric behavior (serialize
as Request, deserialize as plain object). Request/Response handling
belongs in mode-specific modules that can provide proper revivers.
4. Document Node.js dependency in the workflow serialization re-export.
The current implementation uses node:util and Buffer. For the QuickJS
VM (snapshot runtime), these will need polyfills — tracked separately.
* Move reducer/reviver composition into the devalue codec
The Codec interface now takes a SerializationMode ('workflow', 'step',
'client') instead of raw reducers/revivers. The reducer/reviver
composition is internal to the devalue codec implementation.
This is the right abstraction because reducers/revivers are devalue-
specific concepts. A future CBOR codec would handle Date, typed arrays,
Map, Set natively via the CBOR type system — it wouldn't use reducers
at all. A JSON codec would only support standard JSON types.
The mode-specific modules (workflow.ts, step.ts, client.ts) are now
simpler — they just pass the mode string to the codec.
* Replace SerializationFormatType enum with open-ended FormatPrefix type
The format prefix is now a branded string type validated by
isFormatPrefix() — any 4-character [a-z0-9] string is valid.
This removes the hard-coded enum of known formats, making the system
truly open for extension:
type FormatPrefix = string & { __brand: 'FormatPrefix' };
function isFormatPrefix(value: string): value is FormatPrefix;
The SerializationFormat object still provides well-known constants
('devl', 'encr') but they're now just typed constants, not an
exhaustive enum.
peekFormatPrefix() and decodeFormatPrefix() use isFormatPrefix() for
validation instead of checking against a known list. Unknown but valid
prefixes (e.g. 'cbor', 'json', 'v2b1') are accepted — the caller
decides whether they can handle the format.
6 new isFormatPrefix tests covering: valid strings, too short, too long,
uppercase, special characters. 1 new test for unknown-but-valid prefixes.
* Wire modular serialization modules into serialization.ts, add 138 unit tests
Replace duplicate format prefix, reducer/reviver, and encryption helper
code in the monolithic serialization.ts with imports from the modular
serialization/ directory. This completes the refactoring started in the
earlier additive-only commits.
Key changes:
- serialization.ts now imports types, format prefix, common/class/step-function
reducers and revivers, and encryption helpers from ./serialization/ modules
- Removed ~450 lines of duplicate code from serialization.ts
- Made encryption error messages consistent between old and new modules
- Added 138 comprehensive unit tests covering types, format prefix,
encryption, codec, all three reducer modules, all three mode modules,
cross-mode compatibility, and edge cases
- Updated one existing test assertion for new error message wording
* Address code review feedback
- encryption.ts: throw WorkflowRuntimeError instead of plain Error in
decrypt() to preserve the error contract from legacy maybeDecrypt()
- format.ts: document that open-ended prefix validation ([a-z0-9]{4})
is intentional for forward compatibility — callers check support
- errors.ts: extract duplicated formatSerializationError into shared
utility, remove 4 copies from workflow.ts, step.ts, client.ts
- codec-devalue.ts: document that globalThis default is a known
limitation; legacy dehydrate/hydrate path still supports custom global
* Fix codec-devalue.ts comment: clarify modular modules are not used in current runtime
The globalThis default is not a limitation for the current runtime —
all serialization goes through dehydrate*/hydrate* in serialization.ts
which passes the correct global. The modular modules are infrastructure
for the future snapshot runtime where serialization runs inside the VM.
* Wire dehydrate/hydrate functions through modular serialize/deserialize
The dehydrate*/hydrate* functions in serialization.ts now delegate to
the modular mode modules (workflowModule, stepModule, clientModule)
instead of directly calling devalue stringify/parse/unflatten.
Key changes:
- Extended Codec interface with CodecOptions (global, extraReducers,
extraRevivers) so the codec can receive VM globals and mode-specific
stream/Request/Response handlers
- devalueCodec threads global through to all reducer/reviver factories
so instanceof checks work across VM boundaries
- Mode modules (workflow.ts, step.ts, client.ts) accept CodecOptions
and pass them through to the codec
- dehydrate*/hydrate* functions now call module serialize/deserialize
with stream and Request/Response reducers/revivers passed as extras
- v1Compat path remains inline (pre-codec, uses stringify + revive)
- Error context strings preserved via try/catch re-wrapping
* Bump changeset from patch to minor for serialization refactor
Return types of public get*Reducers/get*Revivers functions narrowed
from Reducers/Revivers to Partial<Reducers>/Partial<Revivers>, which
is a TypeScript-level breaking change. Also adds new sub-path exports
(@workflow/core/serialization/workflow, workflow/internal/serialization)
which is additive. Minor bump is the appropriate semver for both.
* Remove unused workflow/internal/serialization re-export and @workflow/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
* Downgrade changeset from minor to patch
After auditing actual consumers of the narrowed return types
(getExternalReducers/getWorkflowReducers/getExternalRevivers/getWorkflowRevivers
now return Partial<Reducers>/Partial<Revivers>), no in-repo or external
consumer indexes specific keys on the returned object in a way that would
break. The only internal caller that did (runtime/run.ts) was updated in
this same PR. The narrowing is type-safer but effectively invisible at
runtime and for idiomatic callers that spread or forward the object.
Since the refactor is internally restructuring only, patch is the
appropriate semver bump.
* Trim serialization-refactor changeset
* Dedup formatSerializationError: import from serialization/errors.ts
The legacy serialization.ts had its own inlined copy of
formatSerializationError. Now that the helper is exported from
serialization/errors.ts (already consumed by workflow.ts/step.ts/client.ts),
import it here too to keep the single source of truth.
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.

4 participants

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

Remove "workflow/internal/serialization" export - #1082

Merged
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export
Feb 16, 2026
Merged

Remove "workflow/internal/serialization" export#1082
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export

Conversation

@TooTallNate

Copy link
Copy Markdown
Member

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.
@changeset-bot

changeset-botBot commented Feb 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: aa8c9bd

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

This PR includes changesets to release 14 packages
NameType
workflowPatch
@workflow/world-testingPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@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 Feb 16, 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🥇 Express0.032s (-6.9% 🟢)1.005s (~)0.973s101.00x
💻 LocalNitro0.032s (+2.5%)1.005s (~)0.972s101.00x
💻 LocalNext.js (Turbopack)0.043s1.005s0.962s101.33x
🌐 RedisNext.js (Turbopack)0.056s1.005s0.949s101.74x
🌐 MongoDBNext.js (Turbopack)0.103s1.007s0.904s103.21x
🐘 PostgresExpress0.155s (-23.4% 🟢)1.010s (-0.8%)0.854s104.82x
🐘 PostgresNitro0.251s (+10.8% 🔺)1.010s (-0.9%)0.758s107.81x
🐘 PostgresNext.js (Turbopack)0.370s1.009s0.639s1011.50x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.544s (-14.7% 🟢)1.950s (-9.5% 🟢)1.405s101.00x
▲ VercelNext.js (Turbopack)0.597s (-27.3% 🟢)1.924s (-10.9% 🟢)1.327s101.10x
▲ VercelExpress0.654s (+8.9% 🔺)2.105s (+2.0%)1.451s101.20x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.096s2.006s0.910s101.00x
💻 LocalExpress1.105s (~)2.006s (~)0.901s101.01x
💻 LocalNitro1.106s (~)2.006s (~)0.901s101.01x
🌐 RedisNext.js (Turbopack)1.130s2.007s0.877s101.03x
🌐 MongoDBNext.js (Turbopack)1.310s2.008s0.698s101.20x
🐘 PostgresNext.js (Turbopack)2.254s3.015s0.761s102.06x
🐘 PostgresExpress2.376s (-3.5%)3.014s (~)0.638s102.17x
🐘 PostgresNitro2.425s (+6.8% 🔺)3.014s (+7.1% 🔺)0.590s102.21x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.276s (-5.5% 🟢)3.134s (-10.2% 🟢)0.858s101.00x
▲ VercelNitro2.337s (+1.7%)3.339s (-8.6% 🟢)1.003s101.03x
▲ VercelExpress2.746s (+21.6% 🔺)3.740s (+11.4% 🔺)0.995s101.21x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.745s11.021s0.276s31.00x
💻 LocalExpress10.822s (-0.6%)11.021s (~)0.199s31.01x
🌐 RedisNext.js (Turbopack)10.831s11.026s0.195s31.01x
💻 LocalNitro10.838s (~)11.023s (~)0.185s31.01x
🌐 MongoDBNext.js (Turbopack)12.321s13.025s0.704s31.15x
🐘 PostgresNext.js (Turbopack)15.142s16.046s0.904s21.41x
🐘 PostgresNitro20.293s (+33.2% 🔺)21.058s (+31.2% 🔺)0.766s21.89x
🐘 PostgresExpress20.404s (+6.9% 🔺)21.059s (+7.7% 🔺)0.656s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)17.672s (-0.7%)18.305s (-3.7%)0.633s21.00x
▲ VercelExpress17.692s (~)18.568s (-1.8%)0.876s21.00x
▲ VercelNitro17.791s (+2.4%)18.766s (~)0.975s21.01x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.189s28.050s0.862s31.00x
🌐 RedisNext.js (Turbopack)27.233s28.058s0.825s31.00x
💻 LocalNitro27.534s (~)28.054s (~)0.520s31.01x
💻 LocalExpress27.549s (~)28.053s (~)0.504s31.01x
🌐 MongoDBNext.js (Turbopack)30.633s31.056s0.424s21.13x
🐘 PostgresNext.js (Turbopack)37.971s38.090s0.118s21.40x
🐘 PostgresExpress50.161s (+30.6% 🔺)50.629s (+29.5% 🔺)0.468s21.84x
🐘 PostgresNitro50.337s (+32.9% 🔺)51.131s (+34.2% 🔺)0.795s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express69.695s (+54.5% 🔺)70.938s (+53.0% 🔺)1.243s11.00x
▲ VercelNitro70.423s (+66.2% 🔺)71.351s (+63.6% 🔺)0.928s11.01x
▲ VercelNext.js (Turbopack)70.900s (+64.7% 🔺)72.015s (+63.7% 🔺)1.115s11.02x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)54.959s55.107s0.147s21.00x
💻 LocalNext.js (Turbopack)56.821s57.098s0.277s21.03x
💻 LocalNitro57.415s (~)58.105s (~)0.690s21.04x
💻 LocalExpress57.420s (~)58.102s (~)0.682s21.04x
🌐 MongoDBNext.js (Turbopack)61.261s62.089s0.828s21.11x
🐘 PostgresNext.js (Turbopack)70.379s70.653s0.274s21.28x
🐘 PostgresNitro100.268s (+33.2% 🔺)101.233s (+32.9% 🔺)0.965s11.82x
🐘 PostgresExpress100.368s (+32.8% 🔺)101.239s (+32.9% 🔺)0.871s11.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express88.970s (-4.8%)90.053s (-5.1% 🟢)1.083s11.00x
▲ VercelNext.js (Turbopack)92.487s (~)92.957s (-1.2%)0.470s11.04x
▲ VercelNitro92.749s (~)93.604s (~)0.855s11.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.294s2.007s0.713s151.00x
💻 LocalExpress1.417s (-0.6%)2.006s (~)0.588s151.10x
💻 LocalNext.js (Turbopack)1.423s2.006s0.583s151.10x
💻 LocalNitro1.437s (+3.1%)2.006s (~)0.569s151.11x
🐘 PostgresNext.js (Turbopack)1.993s2.473s0.480s131.54x
🌐 MongoDBNext.js (Turbopack)2.169s3.009s0.840s101.68x
🐘 PostgresNitro2.313s (+4.6%)3.014s (+12.5% 🔺)0.701s101.79x
🐘 PostgresExpress2.373s (+21.7% 🔺)3.013s (+35.4% 🔺)0.640s101.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.497s (-39.3% 🟢)3.294s (-37.6% 🟢)0.798s101.00x
▲ VercelExpress2.670s (-70.5% 🟢)3.634s (-68.7% 🟢)0.964s91.07x
▲ VercelNitro2.807s (-70.1% 🟢)3.832s (-68.6% 🟢)1.024s81.12x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.560s3.008s0.448s101.00x
💻 LocalExpress2.610s (-7.6% 🟢)3.007s (~)0.398s101.02x
🌐 RedisNext.js (Turbopack)2.612s3.009s0.397s101.02x
💻 LocalNitro2.708s (+5.2% 🔺)3.007s (~)0.299s101.06x
🌐 MongoDBNext.js (Turbopack)4.772s5.180s0.408s61.86x
🐘 PostgresExpress8.159s (-12.9% 🟢)8.527s (-12.8% 🟢)0.368s43.19x
🐘 PostgresNitro9.134s (-8.4% 🟢)9.536s (-7.3% 🟢)0.402s43.57x
🐘 PostgresNext.js (Turbopack)12.189s12.367s0.178s34.76x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.961s (-82.2% 🟢)3.854s (-78.2% 🟢)0.894s81.00x
▲ VercelExpress3.024s (-83.0% 🟢)4.186s (-78.4% 🟢)1.162s81.02x
▲ VercelNitro3.030s (-78.9% 🟢)3.872s (-75.1% 🟢)0.842s81.02x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.130s5.013s0.883s61.00x
💻 LocalExpress7.513s (-10.7% 🟢)8.017s (-11.1% 🟢)0.505s41.82x
💻 LocalNext.js (Turbopack)7.596s8.265s0.669s41.84x
💻 LocalNitro7.637s (+5.4% 🔺)8.020s (~)0.383s41.85x
🌐 MongoDBNext.js (Turbopack)9.989s10.683s0.694s32.42x
🐘 PostgresNitro41.518s (-16.0% 🟢)42.127s (-15.9% 🟢)0.609s110.05x
🐘 PostgresExpress49.459s (-3.1%)50.130s (-2.0%)0.671s111.98x
🐘 PostgresNext.js (Turbopack)56.245s57.121s0.876s113.62x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.822s (-83.1% 🟢)5.752s (-75.8% 🟢)1.929s61.00x
▲ VercelNext.js (Turbopack)3.886s (-80.6% 🟢)4.752s (-77.2% 🟢)0.866s71.02x
▲ VercelExpress6.036s (+45.8% 🔺)7.351s (+34.5% 🔺)1.315s51.58x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.260s2.007s0.746s151.00x
💻 LocalExpress1.413s (-4.4%)2.006s (~)0.593s151.12x
💻 LocalNext.js (Turbopack)1.435s2.005s0.570s151.14x
💻 LocalNitro1.442s (+1.0%)2.006s (~)0.563s151.14x
🐘 PostgresNext.js (Turbopack)1.628s2.011s0.383s151.29x
🐘 PostgresNitro2.034s (-5.5% 🟢)2.922s (+6.6% 🔺)0.888s111.61x
🐘 PostgresExpress2.054s (-3.4%)2.681s (+3.3%)0.627s121.63x
🌐 MongoDBNext.js (Turbopack)2.170s3.007s0.837s101.72x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.243s (-5.5% 🟢)3.190s (-5.1% 🟢)0.947s101.00x
▲ VercelNext.js (Turbopack)2.279s (-4.7%)3.202s (-2.6%)0.924s101.02x
▲ VercelExpress2.343s (+4.7%)3.500s (+8.7% 🔺)1.156s91.04x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)2.545s3.009s0.464s101.00x
💻 LocalNext.js (Turbopack)2.657s3.009s0.352s101.04x
💻 LocalExpress2.730s (-3.8%)3.008s (~)0.279s101.07x
💻 LocalNitro2.745s (+1.1%)3.008s (~)0.262s101.08x
🌐 MongoDBNext.js (Turbopack)4.737s5.175s0.438s61.86x
🐘 PostgresExpress10.086s (-17.6% 🟢)10.696s (-15.8% 🟢)0.610s33.96x
🐘 PostgresNitro10.218s (+2.1%)11.032s (+3.1%)0.815s34.02x
🐘 PostgresNext.js (Turbopack)13.493s13.703s0.210s35.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.400s (-22.6% 🟢)3.309s (-19.9% 🟢)0.909s101.00x
▲ VercelNext.js (Turbopack)2.418s (-9.1% 🟢)3.105s (-16.2% 🟢)0.687s101.01x
▲ VercelExpress2.790s (-9.9% 🟢)3.753s (-7.1% 🟢)0.963s81.16x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.166s5.012s0.846s61.00x
💻 LocalNext.js (Turbopack)7.619s8.016s0.398s41.83x
💻 LocalExpress8.076s (-6.4% 🟢)8.773s (-2.8%)0.697s41.94x
💻 LocalNitro8.131s (+5.2% 🔺)9.021s (+9.1% 🔺)0.890s41.95x
🌐 MongoDBNext.js (Turbopack)9.835s10.349s0.513s32.36x
🐘 PostgresExpress50.121s (-2.8%)51.108s (-1.9%)0.987s112.03x
🐘 PostgresNitro50.610s (+0.9%)51.124s (~)0.514s112.15x
🐘 PostgresNext.js (Turbopack)56.644s57.134s0.490s113.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.941s (-3.2%)4.317s (+10.5% 🔺)1.376s71.00x
▲ VercelNitro3.249s (+11.5% 🔺)4.335s (+6.0% 🔺)1.085s81.10x
▲ VercelNext.js (Turbopack)4.099s (+28.6% 🔺)5.068s (+25.3% 🔺)0.969s61.39x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.147s1.002s0.012s1.017s0.870s101.00x
🌐 RedisNext.js (Turbopack)0.170s1.000s0.002s1.008s0.838s101.16x
💻 LocalNitro0.173s (+2.2%)1.002s (~)0.011s (+0.9%)1.017s (~)0.844s101.17x
💻 LocalExpress0.175s (-3.2%)1.002s (~)0.011s (-5.1% 🟢)1.016s (~)0.842s101.19x
🌐 MongoDBNext.js (Turbopack)0.492s0.956s0.001s1.009s0.517s103.35x
🐘 PostgresNext.js (Turbopack)0.782s0.750s0.001s1.010s0.228s105.32x
🐘 PostgresNitro2.372s (+76.5% 🔺)2.672s (+57.6% 🔺)0.001s (+16.7% 🔺)3.015s (+49.8% 🔺)0.644s1016.12x
🐘 PostgresExpress2.473s (+69.1% 🔺)2.569s (+55.2% 🔺)0.002s (+25.0% 🔺)3.015s (+49.8% 🔺)0.542s1016.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.068s (-21.7% 🟢)2.618s (-27.6% 🟢)0.180s (-13.3% 🟢)3.394s (-22.4% 🟢)1.326s101.00x
▲ VercelNext.js (Turbopack)2.147s (+5.4% 🔺)2.583s (-1.2%)0.227s (-2.6%)3.457s (~)1.310s101.04x
▲ VercelExpress2.244s (-28.9% 🟢)2.818s (-45.6% 🟢)0.244s (+29.3% 🔺)3.719s (-39.2% 🟢)1.475s101.08x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)8/12
🐘 PostgresNext.js (Turbopack)7/12
▲ VercelNitro5/12
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local10/12
Next.js (Turbopack)💻 Local6/12
Nitro💻 Local9/12
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

@TooTallNate
TooTallNate marked this pull request as ready for review February 16, 2026 17:27
@github-actions

github-actionsBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production512038550
✅ 💻 Local Development532068600
✅ 📦 Local Production532068600
✅ 🐘 Local Postgres532068600
✅ 🪟 Windows470350
❌ 🌍 Community Worlds107439159
✅ 📋 Other129021150
Total2391432752709

❌ Failed Tests

🌍 Community Worlds (43 failed)

turso (43 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • parallelSleepWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • 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 retry behavior workflow completes despite transient 5xx on step_completed
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • closureVariableWorkflow - nested step functions with closure variables
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4604
✅ example4604
✅ express4604
✅ fastify4604
✅ hono4604
✅ nextjs-turbopack4901
✅ nextjs-webpack4901
✅ nitro4604
✅ nuxt4604
✅ sveltekit4604
✅ vite4604
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4703
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
✅ mongodb4703
✅ redis-dev300
✅ redis4703
✅ turso-dev300
❌ turso4433
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4307
✅ e2e-local-postgres-nest-stable4307
✅ e2e-local-prod-nest-stable4307

📋 View full workflow run

CopilotAI review requested due to automatic review settings February 16, 2026 17:27
@TooTallNateGraphite App

Copy link
Copy Markdown
MemberAuthor

This stack of pull requests is managed by Graphite. Learn more about stacking.

@vercel

vercelBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-astro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-express-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-fastify-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-hono-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nitro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nuxt-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-sveltekit-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-vite-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-nestReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-swc-playgroundReadyReadyPreview, CommentFeb 16, 2026 5:56pm
1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
workflow-docsSkippedSkippedFeb 16, 2026 5:56pm

@TooTallNate
TooTallNate requested review from a team and removed request for CopilotFebruary 16, 2026 17:27
Comment threadpackages/workflow/package.json
…ge.json breaks 5 files in `packages/world-testing` that still import `hydrateWorkflowReturnValue` from that path.
Co-authored-by: TooTallNate <n@n8.io>
CopilotAI review requested due to automatic review settings February 16, 2026 17:52
@TooTallNate
TooTallNate removed the request for review from CopilotFebruary 16, 2026 17:52
@vercel
vercelBottemporarily deployed to Preview – workflow-docs February 16, 2026 17:52 Inactive

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request removes the workflow/internal/serialization export which was only being used in e2e test files. The changes simplify the codebase by:

  • Removing the re-export wrapper in the workflow package
  • Migrating test files to use direct imports from @workflow/core/serialization
  • Simplifying NextJS Pages Router API endpoints to use JSON instead of binary serialization

Changes:

  • Removed workflow/internal/serialization export from the workflow package
  • Updated world-testing files to import from @workflow/core/serialization directly
  • Simplified NextJS test API endpoints to use JSON body parsing instead of binary serialization

Reviewed changes

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

Show a summary per file
FileDescription
packages/workflow/src/internal/serialization.tsDeleted re-export file
packages/workflow/package.jsonRemoved export entry for internal/serialization
workbench/nextjs-webpack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
workbench/nextjs-turbopack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
packages/world-testing/src/*.mtsUpdated imports to use @workflow/core/serialization
packages/world-testing/package.jsonAdded @workflow/core dependency
pnpm-lock.yamlUpdated lockfile for new dependency
.changeset/cyan-ravens-eat.mdAdded changeset documenting the removal
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

@TooTallNate
TooTallNate merged commit 0946dad into mainFeb 16, 2026
110 checks passed
@TooTallNate
TooTallNate deleted the 02-16-remove_workflow_internal_serialization_export branch February 16, 2026 19:50
pranaygp added a commit that referenced this pull request Feb 17, 2026
* origin/main:
Remove "workflow/internal/serialization" export (#1082)
TooTallNate added a commit that referenced this pull request Apr 19, 2026
…/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
TooTallNate added a commit that referenced this pull request Apr 30, 2026
…isites
Standardize on `Symbol.for('workflow-serialize')` /
`Symbol.for('workflow-deserialize')` everywhere — the parallel
`globalThis.__wdk_serialize` / `__wdk_deserialize` aliases have been
removed from `vm-bundle-entry.ts` and the snapshot runtime's inline
JS strings now use the symbol form directly. Single canonical name,
no duplication.
Drop the `?? Math.random` and `?? Date.now()` fallbacks from the
ULID generator setup. Both prerequisites
(`globalThis.__ulidTimestamp` and the host-replaced seeded
`Math.random`) are always set by `snapshot-runtime.ts` before the
serde bundle is evaluated; silently falling back to unseeded
`Math.random` or live `Date.now()` would re-introduce the
non-determinism we deliberately fixed (concurrent VM invocations of
the same resumption must produce identical correlationIds for the
world's EntityConflictError dedup to work). Now throws if
`__ulidTimestamp` isn't a number, and passes the seeded
`Math.random` reference explicitly to `monotonicFactory` so
upstream's `detectPRNG` never runs (it'd throw in QuickJS anyway,
since `crypto` is unavailable).
Drop the `URL` / `URLSearchParams` / `DOMException` availability
guards in `common-vm.ts`. quickjs-wasi's URL extension is always
loaded (`url.so`) and DOMException is always constructible — the
guards were dead code carried over from when those weren't reliably
available. The reducer/reviver code is now straightforward
`instanceof URL` / `new URL(...)` / `new DOMException(...)`.
Remove `packages/core/src/serialization/base64.ts` and its
sub-path exports (`./serialization/workflow`,
`./serialization/workflow-vm`). The pure-JS base64 helpers were
leftover from before `base64.so` shipped `btoa`/`atob` natively;
the VM-side reducers in `common-vm.ts` now build base64 strings via
the native ones. The sub-path exports had zero consumers in this
repo (the same cleanup landed on the `serialization-refactor`
branch in 05e0fee but never made it onto `snapshot-runtime`
because the branches diverged earlier).
Remove `packages/workflow/src/internal/serialization.ts` and its
`./internal/serialization` package.json export. Same story — zero
consumers, previously removed in #1082, then accidentally
reintroduced via `f04fd8e91`.
TooTallNate added a commit that referenced this pull request May 1, 2026
…to existing pipeline (#1299)
* Add serialization module foundation: types, codec interface, format prefix
Start of the serialization refactor (separate from snapshot-runtime).
New files:
- serialization/types.ts — SerializationFormat enum, SerializableSpecial
interface, Reducers/Revivers types
- serialization/codec.ts — Codec interface with formatPrefix, serialize,
deserialize, and optional deserializeLegacy
- serialization/format.ts — Format prefix encode/decode/peek, moved from
the monolithic serialization.ts
The Codec interface enables future alternative formats (CBOR, JSON) while
keeping the devalue implementation as the current default.
* Add reducers, devalue codec, encryption, and mode-specific modules
Serialization refactor Phase 1: create the new module structure alongside
the existing monolithic serialization.ts (which continues to work).
New files:
- serialization/reducers/common.ts — Date, Error, Map, Set, URL, BigInt,
typed arrays, Headers, Request, Response, RegExp, URLSearchParams
- serialization/reducers/class.ts — Class/Instance with WORKFLOW_SERIALIZE/
DESERIALIZE support
- serialization/reducers/step-function.ts — StepFunction with closure vars
- serialization/codec-devalue.ts — devalue Codec implementation
- serialization/encryption.ts — composable encrypt/decrypt layer
- serialization/workflow.ts — synchronous, no encryption, for VM use
- serialization/step.ts — async with encryption, for step handler
- serialization/client.ts — async with encryption, for start() API
- serialization/index.ts — re-exports all public API
- serialization/serialization.test.ts — 25 focused tests
All modes compose their reducer/reviver sets from the shared building blocks.
Cross-mode compatibility verified: data serialized in any mode can be
deserialized in any other mode (for common types).
Existing 108 serialization tests continue to pass unchanged.
* Add sub-path exports for workflow serialization module
- Add ./serialization/workflow export to @workflow/core package.json
- Add ./internal/serialization re-export to workflow meta-package
- The workflow bundle can now import serialize/deserialize via:
import { serialize, deserialize } from 'workflow/internal/serialization'
Full test suite passes: 493 tests across 22 files (including 25 new
serialization module tests).
* Address code review feedback
1. Fix reducer composition order: Class/Instance reducers now come BEFORE
common reducers in all three modes (workflow, step, client). This ensures
custom Error subclasses with WORKFLOW_SERIALIZE are handled by the
Instance reducer before the generic Error reducer (devalue uses
first-match-wins semantics).
2. Fix encryption decrypt() to fail fast when encrypted data is encountered
without a decryption key, instead of silently returning encrypted bytes
that would fail later with an unhelpful format error.
3. Remove Request/Response from common reducers — they don't have matching
common revivers, so including them caused asymmetric behavior (serialize
as Request, deserialize as plain object). Request/Response handling
belongs in mode-specific modules that can provide proper revivers.
4. Document Node.js dependency in the workflow serialization re-export.
The current implementation uses node:util and Buffer. For the QuickJS
VM (snapshot runtime), these will need polyfills — tracked separately.
* Move reducer/reviver composition into the devalue codec
The Codec interface now takes a SerializationMode ('workflow', 'step',
'client') instead of raw reducers/revivers. The reducer/reviver
composition is internal to the devalue codec implementation.
This is the right abstraction because reducers/revivers are devalue-
specific concepts. A future CBOR codec would handle Date, typed arrays,
Map, Set natively via the CBOR type system — it wouldn't use reducers
at all. A JSON codec would only support standard JSON types.
The mode-specific modules (workflow.ts, step.ts, client.ts) are now
simpler — they just pass the mode string to the codec.
* Replace SerializationFormatType enum with open-ended FormatPrefix type
The format prefix is now a branded string type validated by
isFormatPrefix() — any 4-character [a-z0-9] string is valid.
This removes the hard-coded enum of known formats, making the system
truly open for extension:
type FormatPrefix = string & { __brand: 'FormatPrefix' };
function isFormatPrefix(value: string): value is FormatPrefix;
The SerializationFormat object still provides well-known constants
('devl', 'encr') but they're now just typed constants, not an
exhaustive enum.
peekFormatPrefix() and decodeFormatPrefix() use isFormatPrefix() for
validation instead of checking against a known list. Unknown but valid
prefixes (e.g. 'cbor', 'json', 'v2b1') are accepted — the caller
decides whether they can handle the format.
6 new isFormatPrefix tests covering: valid strings, too short, too long,
uppercase, special characters. 1 new test for unknown-but-valid prefixes.
* Wire modular serialization modules into serialization.ts, add 138 unit tests
Replace duplicate format prefix, reducer/reviver, and encryption helper
code in the monolithic serialization.ts with imports from the modular
serialization/ directory. This completes the refactoring started in the
earlier additive-only commits.
Key changes:
- serialization.ts now imports types, format prefix, common/class/step-function
reducers and revivers, and encryption helpers from ./serialization/ modules
- Removed ~450 lines of duplicate code from serialization.ts
- Made encryption error messages consistent between old and new modules
- Added 138 comprehensive unit tests covering types, format prefix,
encryption, codec, all three reducer modules, all three mode modules,
cross-mode compatibility, and edge cases
- Updated one existing test assertion for new error message wording
* Address code review feedback
- encryption.ts: throw WorkflowRuntimeError instead of plain Error in
decrypt() to preserve the error contract from legacy maybeDecrypt()
- format.ts: document that open-ended prefix validation ([a-z0-9]{4})
is intentional for forward compatibility — callers check support
- errors.ts: extract duplicated formatSerializationError into shared
utility, remove 4 copies from workflow.ts, step.ts, client.ts
- codec-devalue.ts: document that globalThis default is a known
limitation; legacy dehydrate/hydrate path still supports custom global
* Fix codec-devalue.ts comment: clarify modular modules are not used in current runtime
The globalThis default is not a limitation for the current runtime —
all serialization goes through dehydrate*/hydrate* in serialization.ts
which passes the correct global. The modular modules are infrastructure
for the future snapshot runtime where serialization runs inside the VM.
* Wire dehydrate/hydrate functions through modular serialize/deserialize
The dehydrate*/hydrate* functions in serialization.ts now delegate to
the modular mode modules (workflowModule, stepModule, clientModule)
instead of directly calling devalue stringify/parse/unflatten.
Key changes:
- Extended Codec interface with CodecOptions (global, extraReducers,
extraRevivers) so the codec can receive VM globals and mode-specific
stream/Request/Response handlers
- devalueCodec threads global through to all reducer/reviver factories
so instanceof checks work across VM boundaries
- Mode modules (workflow.ts, step.ts, client.ts) accept CodecOptions
and pass them through to the codec
- dehydrate*/hydrate* functions now call module serialize/deserialize
with stream and Request/Response reducers/revivers passed as extras
- v1Compat path remains inline (pre-codec, uses stringify + revive)
- Error context strings preserved via try/catch re-wrapping
* Bump changeset from patch to minor for serialization refactor
Return types of public get*Reducers/get*Revivers functions narrowed
from Reducers/Revivers to Partial<Reducers>/Partial<Revivers>, which
is a TypeScript-level breaking change. Also adds new sub-path exports
(@workflow/core/serialization/workflow, workflow/internal/serialization)
which is additive. Minor bump is the appropriate semver for both.
* Remove unused workflow/internal/serialization re-export and @workflow/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
* Downgrade changeset from minor to patch
After auditing actual consumers of the narrowed return types
(getExternalReducers/getWorkflowReducers/getExternalRevivers/getWorkflowRevivers
now return Partial<Reducers>/Partial<Revivers>), no in-repo or external
consumer indexes specific keys on the returned object in a way that would
break. The only internal caller that did (runtime/run.ts) was updated in
this same PR. The narrowing is type-safer but effectively invisible at
runtime and for idiomatic callers that spread or forward the object.
Since the refactor is internally restructuring only, patch is the
appropriate semver bump.
* Trim serialization-refactor changeset
* Dedup formatSerializationError: import from serialization/errors.ts
The legacy serialization.ts had its own inlined copy of
formatSerializationError. Now that the helper is exported from
serialization/errors.ts (already consumed by workflow.ts/step.ts/client.ts),
import it here too to keep the single source of truth.
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.

4 participants

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

Remove "workflow/internal/serialization" export - #1082

Merged
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export
Feb 16, 2026
Merged

Remove "workflow/internal/serialization" export#1082
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export

Conversation

@TooTallNate

Copy link
Copy Markdown
Member

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.
@changeset-bot

changeset-botBot commented Feb 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: aa8c9bd

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

This PR includes changesets to release 14 packages
NameType
workflowPatch
@workflow/world-testingPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@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 Feb 16, 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🥇 Express0.032s (-6.9% 🟢)1.005s (~)0.973s101.00x
💻 LocalNitro0.032s (+2.5%)1.005s (~)0.972s101.00x
💻 LocalNext.js (Turbopack)0.043s1.005s0.962s101.33x
🌐 RedisNext.js (Turbopack)0.056s1.005s0.949s101.74x
🌐 MongoDBNext.js (Turbopack)0.103s1.007s0.904s103.21x
🐘 PostgresExpress0.155s (-23.4% 🟢)1.010s (-0.8%)0.854s104.82x
🐘 PostgresNitro0.251s (+10.8% 🔺)1.010s (-0.9%)0.758s107.81x
🐘 PostgresNext.js (Turbopack)0.370s1.009s0.639s1011.50x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.544s (-14.7% 🟢)1.950s (-9.5% 🟢)1.405s101.00x
▲ VercelNext.js (Turbopack)0.597s (-27.3% 🟢)1.924s (-10.9% 🟢)1.327s101.10x
▲ VercelExpress0.654s (+8.9% 🔺)2.105s (+2.0%)1.451s101.20x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.096s2.006s0.910s101.00x
💻 LocalExpress1.105s (~)2.006s (~)0.901s101.01x
💻 LocalNitro1.106s (~)2.006s (~)0.901s101.01x
🌐 RedisNext.js (Turbopack)1.130s2.007s0.877s101.03x
🌐 MongoDBNext.js (Turbopack)1.310s2.008s0.698s101.20x
🐘 PostgresNext.js (Turbopack)2.254s3.015s0.761s102.06x
🐘 PostgresExpress2.376s (-3.5%)3.014s (~)0.638s102.17x
🐘 PostgresNitro2.425s (+6.8% 🔺)3.014s (+7.1% 🔺)0.590s102.21x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.276s (-5.5% 🟢)3.134s (-10.2% 🟢)0.858s101.00x
▲ VercelNitro2.337s (+1.7%)3.339s (-8.6% 🟢)1.003s101.03x
▲ VercelExpress2.746s (+21.6% 🔺)3.740s (+11.4% 🔺)0.995s101.21x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.745s11.021s0.276s31.00x
💻 LocalExpress10.822s (-0.6%)11.021s (~)0.199s31.01x
🌐 RedisNext.js (Turbopack)10.831s11.026s0.195s31.01x
💻 LocalNitro10.838s (~)11.023s (~)0.185s31.01x
🌐 MongoDBNext.js (Turbopack)12.321s13.025s0.704s31.15x
🐘 PostgresNext.js (Turbopack)15.142s16.046s0.904s21.41x
🐘 PostgresNitro20.293s (+33.2% 🔺)21.058s (+31.2% 🔺)0.766s21.89x
🐘 PostgresExpress20.404s (+6.9% 🔺)21.059s (+7.7% 🔺)0.656s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)17.672s (-0.7%)18.305s (-3.7%)0.633s21.00x
▲ VercelExpress17.692s (~)18.568s (-1.8%)0.876s21.00x
▲ VercelNitro17.791s (+2.4%)18.766s (~)0.975s21.01x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.189s28.050s0.862s31.00x
🌐 RedisNext.js (Turbopack)27.233s28.058s0.825s31.00x
💻 LocalNitro27.534s (~)28.054s (~)0.520s31.01x
💻 LocalExpress27.549s (~)28.053s (~)0.504s31.01x
🌐 MongoDBNext.js (Turbopack)30.633s31.056s0.424s21.13x
🐘 PostgresNext.js (Turbopack)37.971s38.090s0.118s21.40x
🐘 PostgresExpress50.161s (+30.6% 🔺)50.629s (+29.5% 🔺)0.468s21.84x
🐘 PostgresNitro50.337s (+32.9% 🔺)51.131s (+34.2% 🔺)0.795s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express69.695s (+54.5% 🔺)70.938s (+53.0% 🔺)1.243s11.00x
▲ VercelNitro70.423s (+66.2% 🔺)71.351s (+63.6% 🔺)0.928s11.01x
▲ VercelNext.js (Turbopack)70.900s (+64.7% 🔺)72.015s (+63.7% 🔺)1.115s11.02x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)54.959s55.107s0.147s21.00x
💻 LocalNext.js (Turbopack)56.821s57.098s0.277s21.03x
💻 LocalNitro57.415s (~)58.105s (~)0.690s21.04x
💻 LocalExpress57.420s (~)58.102s (~)0.682s21.04x
🌐 MongoDBNext.js (Turbopack)61.261s62.089s0.828s21.11x
🐘 PostgresNext.js (Turbopack)70.379s70.653s0.274s21.28x
🐘 PostgresNitro100.268s (+33.2% 🔺)101.233s (+32.9% 🔺)0.965s11.82x
🐘 PostgresExpress100.368s (+32.8% 🔺)101.239s (+32.9% 🔺)0.871s11.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express88.970s (-4.8%)90.053s (-5.1% 🟢)1.083s11.00x
▲ VercelNext.js (Turbopack)92.487s (~)92.957s (-1.2%)0.470s11.04x
▲ VercelNitro92.749s (~)93.604s (~)0.855s11.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.294s2.007s0.713s151.00x
💻 LocalExpress1.417s (-0.6%)2.006s (~)0.588s151.10x
💻 LocalNext.js (Turbopack)1.423s2.006s0.583s151.10x
💻 LocalNitro1.437s (+3.1%)2.006s (~)0.569s151.11x
🐘 PostgresNext.js (Turbopack)1.993s2.473s0.480s131.54x
🌐 MongoDBNext.js (Turbopack)2.169s3.009s0.840s101.68x
🐘 PostgresNitro2.313s (+4.6%)3.014s (+12.5% 🔺)0.701s101.79x
🐘 PostgresExpress2.373s (+21.7% 🔺)3.013s (+35.4% 🔺)0.640s101.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.497s (-39.3% 🟢)3.294s (-37.6% 🟢)0.798s101.00x
▲ VercelExpress2.670s (-70.5% 🟢)3.634s (-68.7% 🟢)0.964s91.07x
▲ VercelNitro2.807s (-70.1% 🟢)3.832s (-68.6% 🟢)1.024s81.12x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.560s3.008s0.448s101.00x
💻 LocalExpress2.610s (-7.6% 🟢)3.007s (~)0.398s101.02x
🌐 RedisNext.js (Turbopack)2.612s3.009s0.397s101.02x
💻 LocalNitro2.708s (+5.2% 🔺)3.007s (~)0.299s101.06x
🌐 MongoDBNext.js (Turbopack)4.772s5.180s0.408s61.86x
🐘 PostgresExpress8.159s (-12.9% 🟢)8.527s (-12.8% 🟢)0.368s43.19x
🐘 PostgresNitro9.134s (-8.4% 🟢)9.536s (-7.3% 🟢)0.402s43.57x
🐘 PostgresNext.js (Turbopack)12.189s12.367s0.178s34.76x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.961s (-82.2% 🟢)3.854s (-78.2% 🟢)0.894s81.00x
▲ VercelExpress3.024s (-83.0% 🟢)4.186s (-78.4% 🟢)1.162s81.02x
▲ VercelNitro3.030s (-78.9% 🟢)3.872s (-75.1% 🟢)0.842s81.02x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.130s5.013s0.883s61.00x
💻 LocalExpress7.513s (-10.7% 🟢)8.017s (-11.1% 🟢)0.505s41.82x
💻 LocalNext.js (Turbopack)7.596s8.265s0.669s41.84x
💻 LocalNitro7.637s (+5.4% 🔺)8.020s (~)0.383s41.85x
🌐 MongoDBNext.js (Turbopack)9.989s10.683s0.694s32.42x
🐘 PostgresNitro41.518s (-16.0% 🟢)42.127s (-15.9% 🟢)0.609s110.05x
🐘 PostgresExpress49.459s (-3.1%)50.130s (-2.0%)0.671s111.98x
🐘 PostgresNext.js (Turbopack)56.245s57.121s0.876s113.62x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.822s (-83.1% 🟢)5.752s (-75.8% 🟢)1.929s61.00x
▲ VercelNext.js (Turbopack)3.886s (-80.6% 🟢)4.752s (-77.2% 🟢)0.866s71.02x
▲ VercelExpress6.036s (+45.8% 🔺)7.351s (+34.5% 🔺)1.315s51.58x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.260s2.007s0.746s151.00x
💻 LocalExpress1.413s (-4.4%)2.006s (~)0.593s151.12x
💻 LocalNext.js (Turbopack)1.435s2.005s0.570s151.14x
💻 LocalNitro1.442s (+1.0%)2.006s (~)0.563s151.14x
🐘 PostgresNext.js (Turbopack)1.628s2.011s0.383s151.29x
🐘 PostgresNitro2.034s (-5.5% 🟢)2.922s (+6.6% 🔺)0.888s111.61x
🐘 PostgresExpress2.054s (-3.4%)2.681s (+3.3%)0.627s121.63x
🌐 MongoDBNext.js (Turbopack)2.170s3.007s0.837s101.72x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.243s (-5.5% 🟢)3.190s (-5.1% 🟢)0.947s101.00x
▲ VercelNext.js (Turbopack)2.279s (-4.7%)3.202s (-2.6%)0.924s101.02x
▲ VercelExpress2.343s (+4.7%)3.500s (+8.7% 🔺)1.156s91.04x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)2.545s3.009s0.464s101.00x
💻 LocalNext.js (Turbopack)2.657s3.009s0.352s101.04x
💻 LocalExpress2.730s (-3.8%)3.008s (~)0.279s101.07x
💻 LocalNitro2.745s (+1.1%)3.008s (~)0.262s101.08x
🌐 MongoDBNext.js (Turbopack)4.737s5.175s0.438s61.86x
🐘 PostgresExpress10.086s (-17.6% 🟢)10.696s (-15.8% 🟢)0.610s33.96x
🐘 PostgresNitro10.218s (+2.1%)11.032s (+3.1%)0.815s34.02x
🐘 PostgresNext.js (Turbopack)13.493s13.703s0.210s35.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.400s (-22.6% 🟢)3.309s (-19.9% 🟢)0.909s101.00x
▲ VercelNext.js (Turbopack)2.418s (-9.1% 🟢)3.105s (-16.2% 🟢)0.687s101.01x
▲ VercelExpress2.790s (-9.9% 🟢)3.753s (-7.1% 🟢)0.963s81.16x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.166s5.012s0.846s61.00x
💻 LocalNext.js (Turbopack)7.619s8.016s0.398s41.83x
💻 LocalExpress8.076s (-6.4% 🟢)8.773s (-2.8%)0.697s41.94x
💻 LocalNitro8.131s (+5.2% 🔺)9.021s (+9.1% 🔺)0.890s41.95x
🌐 MongoDBNext.js (Turbopack)9.835s10.349s0.513s32.36x
🐘 PostgresExpress50.121s (-2.8%)51.108s (-1.9%)0.987s112.03x
🐘 PostgresNitro50.610s (+0.9%)51.124s (~)0.514s112.15x
🐘 PostgresNext.js (Turbopack)56.644s57.134s0.490s113.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.941s (-3.2%)4.317s (+10.5% 🔺)1.376s71.00x
▲ VercelNitro3.249s (+11.5% 🔺)4.335s (+6.0% 🔺)1.085s81.10x
▲ VercelNext.js (Turbopack)4.099s (+28.6% 🔺)5.068s (+25.3% 🔺)0.969s61.39x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.147s1.002s0.012s1.017s0.870s101.00x
🌐 RedisNext.js (Turbopack)0.170s1.000s0.002s1.008s0.838s101.16x
💻 LocalNitro0.173s (+2.2%)1.002s (~)0.011s (+0.9%)1.017s (~)0.844s101.17x
💻 LocalExpress0.175s (-3.2%)1.002s (~)0.011s (-5.1% 🟢)1.016s (~)0.842s101.19x
🌐 MongoDBNext.js (Turbopack)0.492s0.956s0.001s1.009s0.517s103.35x
🐘 PostgresNext.js (Turbopack)0.782s0.750s0.001s1.010s0.228s105.32x
🐘 PostgresNitro2.372s (+76.5% 🔺)2.672s (+57.6% 🔺)0.001s (+16.7% 🔺)3.015s (+49.8% 🔺)0.644s1016.12x
🐘 PostgresExpress2.473s (+69.1% 🔺)2.569s (+55.2% 🔺)0.002s (+25.0% 🔺)3.015s (+49.8% 🔺)0.542s1016.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.068s (-21.7% 🟢)2.618s (-27.6% 🟢)0.180s (-13.3% 🟢)3.394s (-22.4% 🟢)1.326s101.00x
▲ VercelNext.js (Turbopack)2.147s (+5.4% 🔺)2.583s (-1.2%)0.227s (-2.6%)3.457s (~)1.310s101.04x
▲ VercelExpress2.244s (-28.9% 🟢)2.818s (-45.6% 🟢)0.244s (+29.3% 🔺)3.719s (-39.2% 🟢)1.475s101.08x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)8/12
🐘 PostgresNext.js (Turbopack)7/12
▲ VercelNitro5/12
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local10/12
Next.js (Turbopack)💻 Local6/12
Nitro💻 Local9/12
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

@TooTallNate
TooTallNate marked this pull request as ready for review February 16, 2026 17:27
@github-actions

github-actionsBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production512038550
✅ 💻 Local Development532068600
✅ 📦 Local Production532068600
✅ 🐘 Local Postgres532068600
✅ 🪟 Windows470350
❌ 🌍 Community Worlds107439159
✅ 📋 Other129021150
Total2391432752709

❌ Failed Tests

🌍 Community Worlds (43 failed)

turso (43 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • parallelSleepWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • 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 retry behavior workflow completes despite transient 5xx on step_completed
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • closureVariableWorkflow - nested step functions with closure variables
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4604
✅ example4604
✅ express4604
✅ fastify4604
✅ hono4604
✅ nextjs-turbopack4901
✅ nextjs-webpack4901
✅ nitro4604
✅ nuxt4604
✅ sveltekit4604
✅ vite4604
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4703
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
✅ mongodb4703
✅ redis-dev300
✅ redis4703
✅ turso-dev300
❌ turso4433
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4307
✅ e2e-local-postgres-nest-stable4307
✅ e2e-local-prod-nest-stable4307

📋 View full workflow run

CopilotAI review requested due to automatic review settings February 16, 2026 17:27
@TooTallNateGraphite App

Copy link
Copy Markdown
MemberAuthor

This stack of pull requests is managed by Graphite. Learn more about stacking.

@vercel

vercelBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-astro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-express-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-fastify-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-hono-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nitro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nuxt-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-sveltekit-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-vite-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-nestReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-swc-playgroundReadyReadyPreview, CommentFeb 16, 2026 5:56pm
1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
workflow-docsSkippedSkippedFeb 16, 2026 5:56pm

@TooTallNate
TooTallNate requested review from a team and removed request for CopilotFebruary 16, 2026 17:27
Comment threadpackages/workflow/package.json
…ge.json breaks 5 files in `packages/world-testing` that still import `hydrateWorkflowReturnValue` from that path.
Co-authored-by: TooTallNate <n@n8.io>
CopilotAI review requested due to automatic review settings February 16, 2026 17:52
@TooTallNate
TooTallNate removed the request for review from CopilotFebruary 16, 2026 17:52
@vercel
vercelBottemporarily deployed to Preview – workflow-docs February 16, 2026 17:52 Inactive

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request removes the workflow/internal/serialization export which was only being used in e2e test files. The changes simplify the codebase by:

  • Removing the re-export wrapper in the workflow package
  • Migrating test files to use direct imports from @workflow/core/serialization
  • Simplifying NextJS Pages Router API endpoints to use JSON instead of binary serialization

Changes:

  • Removed workflow/internal/serialization export from the workflow package
  • Updated world-testing files to import from @workflow/core/serialization directly
  • Simplified NextJS test API endpoints to use JSON body parsing instead of binary serialization

Reviewed changes

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

Show a summary per file
FileDescription
packages/workflow/src/internal/serialization.tsDeleted re-export file
packages/workflow/package.jsonRemoved export entry for internal/serialization
workbench/nextjs-webpack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
workbench/nextjs-turbopack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
packages/world-testing/src/*.mtsUpdated imports to use @workflow/core/serialization
packages/world-testing/package.jsonAdded @workflow/core dependency
pnpm-lock.yamlUpdated lockfile for new dependency
.changeset/cyan-ravens-eat.mdAdded changeset documenting the removal
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

@TooTallNate
TooTallNate merged commit 0946dad into mainFeb 16, 2026
110 checks passed
@TooTallNate
TooTallNate deleted the 02-16-remove_workflow_internal_serialization_export branch February 16, 2026 19:50
pranaygp added a commit that referenced this pull request Feb 17, 2026
* origin/main:
Remove "workflow/internal/serialization" export (#1082)
TooTallNate added a commit that referenced this pull request Apr 19, 2026
…/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
TooTallNate added a commit that referenced this pull request Apr 30, 2026
…isites
Standardize on `Symbol.for('workflow-serialize')` /
`Symbol.for('workflow-deserialize')` everywhere — the parallel
`globalThis.__wdk_serialize` / `__wdk_deserialize` aliases have been
removed from `vm-bundle-entry.ts` and the snapshot runtime's inline
JS strings now use the symbol form directly. Single canonical name,
no duplication.
Drop the `?? Math.random` and `?? Date.now()` fallbacks from the
ULID generator setup. Both prerequisites
(`globalThis.__ulidTimestamp` and the host-replaced seeded
`Math.random`) are always set by `snapshot-runtime.ts` before the
serde bundle is evaluated; silently falling back to unseeded
`Math.random` or live `Date.now()` would re-introduce the
non-determinism we deliberately fixed (concurrent VM invocations of
the same resumption must produce identical correlationIds for the
world's EntityConflictError dedup to work). Now throws if
`__ulidTimestamp` isn't a number, and passes the seeded
`Math.random` reference explicitly to `monotonicFactory` so
upstream's `detectPRNG` never runs (it'd throw in QuickJS anyway,
since `crypto` is unavailable).
Drop the `URL` / `URLSearchParams` / `DOMException` availability
guards in `common-vm.ts`. quickjs-wasi's URL extension is always
loaded (`url.so`) and DOMException is always constructible — the
guards were dead code carried over from when those weren't reliably
available. The reducer/reviver code is now straightforward
`instanceof URL` / `new URL(...)` / `new DOMException(...)`.
Remove `packages/core/src/serialization/base64.ts` and its
sub-path exports (`./serialization/workflow`,
`./serialization/workflow-vm`). The pure-JS base64 helpers were
leftover from before `base64.so` shipped `btoa`/`atob` natively;
the VM-side reducers in `common-vm.ts` now build base64 strings via
the native ones. The sub-path exports had zero consumers in this
repo (the same cleanup landed on the `serialization-refactor`
branch in 05e0fee but never made it onto `snapshot-runtime`
because the branches diverged earlier).
Remove `packages/workflow/src/internal/serialization.ts` and its
`./internal/serialization` package.json export. Same story — zero
consumers, previously removed in #1082, then accidentally
reintroduced via `f04fd8e91`.
TooTallNate added a commit that referenced this pull request May 1, 2026
…to existing pipeline (#1299)
* Add serialization module foundation: types, codec interface, format prefix
Start of the serialization refactor (separate from snapshot-runtime).
New files:
- serialization/types.ts — SerializationFormat enum, SerializableSpecial
interface, Reducers/Revivers types
- serialization/codec.ts — Codec interface with formatPrefix, serialize,
deserialize, and optional deserializeLegacy
- serialization/format.ts — Format prefix encode/decode/peek, moved from
the monolithic serialization.ts
The Codec interface enables future alternative formats (CBOR, JSON) while
keeping the devalue implementation as the current default.
* Add reducers, devalue codec, encryption, and mode-specific modules
Serialization refactor Phase 1: create the new module structure alongside
the existing monolithic serialization.ts (which continues to work).
New files:
- serialization/reducers/common.ts — Date, Error, Map, Set, URL, BigInt,
typed arrays, Headers, Request, Response, RegExp, URLSearchParams
- serialization/reducers/class.ts — Class/Instance with WORKFLOW_SERIALIZE/
DESERIALIZE support
- serialization/reducers/step-function.ts — StepFunction with closure vars
- serialization/codec-devalue.ts — devalue Codec implementation
- serialization/encryption.ts — composable encrypt/decrypt layer
- serialization/workflow.ts — synchronous, no encryption, for VM use
- serialization/step.ts — async with encryption, for step handler
- serialization/client.ts — async with encryption, for start() API
- serialization/index.ts — re-exports all public API
- serialization/serialization.test.ts — 25 focused tests
All modes compose their reducer/reviver sets from the shared building blocks.
Cross-mode compatibility verified: data serialized in any mode can be
deserialized in any other mode (for common types).
Existing 108 serialization tests continue to pass unchanged.
* Add sub-path exports for workflow serialization module
- Add ./serialization/workflow export to @workflow/core package.json
- Add ./internal/serialization re-export to workflow meta-package
- The workflow bundle can now import serialize/deserialize via:
import { serialize, deserialize } from 'workflow/internal/serialization'
Full test suite passes: 493 tests across 22 files (including 25 new
serialization module tests).
* Address code review feedback
1. Fix reducer composition order: Class/Instance reducers now come BEFORE
common reducers in all three modes (workflow, step, client). This ensures
custom Error subclasses with WORKFLOW_SERIALIZE are handled by the
Instance reducer before the generic Error reducer (devalue uses
first-match-wins semantics).
2. Fix encryption decrypt() to fail fast when encrypted data is encountered
without a decryption key, instead of silently returning encrypted bytes
that would fail later with an unhelpful format error.
3. Remove Request/Response from common reducers — they don't have matching
common revivers, so including them caused asymmetric behavior (serialize
as Request, deserialize as plain object). Request/Response handling
belongs in mode-specific modules that can provide proper revivers.
4. Document Node.js dependency in the workflow serialization re-export.
The current implementation uses node:util and Buffer. For the QuickJS
VM (snapshot runtime), these will need polyfills — tracked separately.
* Move reducer/reviver composition into the devalue codec
The Codec interface now takes a SerializationMode ('workflow', 'step',
'client') instead of raw reducers/revivers. The reducer/reviver
composition is internal to the devalue codec implementation.
This is the right abstraction because reducers/revivers are devalue-
specific concepts. A future CBOR codec would handle Date, typed arrays,
Map, Set natively via the CBOR type system — it wouldn't use reducers
at all. A JSON codec would only support standard JSON types.
The mode-specific modules (workflow.ts, step.ts, client.ts) are now
simpler — they just pass the mode string to the codec.
* Replace SerializationFormatType enum with open-ended FormatPrefix type
The format prefix is now a branded string type validated by
isFormatPrefix() — any 4-character [a-z0-9] string is valid.
This removes the hard-coded enum of known formats, making the system
truly open for extension:
type FormatPrefix = string & { __brand: 'FormatPrefix' };
function isFormatPrefix(value: string): value is FormatPrefix;
The SerializationFormat object still provides well-known constants
('devl', 'encr') but they're now just typed constants, not an
exhaustive enum.
peekFormatPrefix() and decodeFormatPrefix() use isFormatPrefix() for
validation instead of checking against a known list. Unknown but valid
prefixes (e.g. 'cbor', 'json', 'v2b1') are accepted — the caller
decides whether they can handle the format.
6 new isFormatPrefix tests covering: valid strings, too short, too long,
uppercase, special characters. 1 new test for unknown-but-valid prefixes.
* Wire modular serialization modules into serialization.ts, add 138 unit tests
Replace duplicate format prefix, reducer/reviver, and encryption helper
code in the monolithic serialization.ts with imports from the modular
serialization/ directory. This completes the refactoring started in the
earlier additive-only commits.
Key changes:
- serialization.ts now imports types, format prefix, common/class/step-function
reducers and revivers, and encryption helpers from ./serialization/ modules
- Removed ~450 lines of duplicate code from serialization.ts
- Made encryption error messages consistent between old and new modules
- Added 138 comprehensive unit tests covering types, format prefix,
encryption, codec, all three reducer modules, all three mode modules,
cross-mode compatibility, and edge cases
- Updated one existing test assertion for new error message wording
* Address code review feedback
- encryption.ts: throw WorkflowRuntimeError instead of plain Error in
decrypt() to preserve the error contract from legacy maybeDecrypt()
- format.ts: document that open-ended prefix validation ([a-z0-9]{4})
is intentional for forward compatibility — callers check support
- errors.ts: extract duplicated formatSerializationError into shared
utility, remove 4 copies from workflow.ts, step.ts, client.ts
- codec-devalue.ts: document that globalThis default is a known
limitation; legacy dehydrate/hydrate path still supports custom global
* Fix codec-devalue.ts comment: clarify modular modules are not used in current runtime
The globalThis default is not a limitation for the current runtime —
all serialization goes through dehydrate*/hydrate* in serialization.ts
which passes the correct global. The modular modules are infrastructure
for the future snapshot runtime where serialization runs inside the VM.
* Wire dehydrate/hydrate functions through modular serialize/deserialize
The dehydrate*/hydrate* functions in serialization.ts now delegate to
the modular mode modules (workflowModule, stepModule, clientModule)
instead of directly calling devalue stringify/parse/unflatten.
Key changes:
- Extended Codec interface with CodecOptions (global, extraReducers,
extraRevivers) so the codec can receive VM globals and mode-specific
stream/Request/Response handlers
- devalueCodec threads global through to all reducer/reviver factories
so instanceof checks work across VM boundaries
- Mode modules (workflow.ts, step.ts, client.ts) accept CodecOptions
and pass them through to the codec
- dehydrate*/hydrate* functions now call module serialize/deserialize
with stream and Request/Response reducers/revivers passed as extras
- v1Compat path remains inline (pre-codec, uses stringify + revive)
- Error context strings preserved via try/catch re-wrapping
* Bump changeset from patch to minor for serialization refactor
Return types of public get*Reducers/get*Revivers functions narrowed
from Reducers/Revivers to Partial<Reducers>/Partial<Revivers>, which
is a TypeScript-level breaking change. Also adds new sub-path exports
(@workflow/core/serialization/workflow, workflow/internal/serialization)
which is additive. Minor bump is the appropriate semver for both.
* Remove unused workflow/internal/serialization re-export and @workflow/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
* Downgrade changeset from minor to patch
After auditing actual consumers of the narrowed return types
(getExternalReducers/getWorkflowReducers/getExternalRevivers/getWorkflowRevivers
now return Partial<Reducers>/Partial<Revivers>), no in-repo or external
consumer indexes specific keys on the returned object in a way that would
break. The only internal caller that did (runtime/run.ts) was updated in
this same PR. The narrowing is type-safer but effectively invisible at
runtime and for idiomatic callers that spread or forward the object.
Since the refactor is internally restructuring only, patch is the
appropriate semver bump.
* Trim serialization-refactor changeset
* Dedup formatSerializationError: import from serialization/errors.ts
The legacy serialization.ts had its own inlined copy of
formatSerializationError. Now that the helper is exported from
serialization/errors.ts (already consumed by workflow.ts/step.ts/client.ts),
import it here too to keep the single source of truth.
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.

4 participants

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

Remove "workflow/internal/serialization" export - #1082

Merged
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export
Feb 16, 2026
Merged

Remove "workflow/internal/serialization" export#1082
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export

Conversation

@TooTallNate

Copy link
Copy Markdown
Member

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.
@changeset-bot

changeset-botBot commented Feb 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: aa8c9bd

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

This PR includes changesets to release 14 packages
NameType
workflowPatch
@workflow/world-testingPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@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 Feb 16, 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🥇 Express0.032s (-6.9% 🟢)1.005s (~)0.973s101.00x
💻 LocalNitro0.032s (+2.5%)1.005s (~)0.972s101.00x
💻 LocalNext.js (Turbopack)0.043s1.005s0.962s101.33x
🌐 RedisNext.js (Turbopack)0.056s1.005s0.949s101.74x
🌐 MongoDBNext.js (Turbopack)0.103s1.007s0.904s103.21x
🐘 PostgresExpress0.155s (-23.4% 🟢)1.010s (-0.8%)0.854s104.82x
🐘 PostgresNitro0.251s (+10.8% 🔺)1.010s (-0.9%)0.758s107.81x
🐘 PostgresNext.js (Turbopack)0.370s1.009s0.639s1011.50x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.544s (-14.7% 🟢)1.950s (-9.5% 🟢)1.405s101.00x
▲ VercelNext.js (Turbopack)0.597s (-27.3% 🟢)1.924s (-10.9% 🟢)1.327s101.10x
▲ VercelExpress0.654s (+8.9% 🔺)2.105s (+2.0%)1.451s101.20x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.096s2.006s0.910s101.00x
💻 LocalExpress1.105s (~)2.006s (~)0.901s101.01x
💻 LocalNitro1.106s (~)2.006s (~)0.901s101.01x
🌐 RedisNext.js (Turbopack)1.130s2.007s0.877s101.03x
🌐 MongoDBNext.js (Turbopack)1.310s2.008s0.698s101.20x
🐘 PostgresNext.js (Turbopack)2.254s3.015s0.761s102.06x
🐘 PostgresExpress2.376s (-3.5%)3.014s (~)0.638s102.17x
🐘 PostgresNitro2.425s (+6.8% 🔺)3.014s (+7.1% 🔺)0.590s102.21x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.276s (-5.5% 🟢)3.134s (-10.2% 🟢)0.858s101.00x
▲ VercelNitro2.337s (+1.7%)3.339s (-8.6% 🟢)1.003s101.03x
▲ VercelExpress2.746s (+21.6% 🔺)3.740s (+11.4% 🔺)0.995s101.21x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.745s11.021s0.276s31.00x
💻 LocalExpress10.822s (-0.6%)11.021s (~)0.199s31.01x
🌐 RedisNext.js (Turbopack)10.831s11.026s0.195s31.01x
💻 LocalNitro10.838s (~)11.023s (~)0.185s31.01x
🌐 MongoDBNext.js (Turbopack)12.321s13.025s0.704s31.15x
🐘 PostgresNext.js (Turbopack)15.142s16.046s0.904s21.41x
🐘 PostgresNitro20.293s (+33.2% 🔺)21.058s (+31.2% 🔺)0.766s21.89x
🐘 PostgresExpress20.404s (+6.9% 🔺)21.059s (+7.7% 🔺)0.656s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)17.672s (-0.7%)18.305s (-3.7%)0.633s21.00x
▲ VercelExpress17.692s (~)18.568s (-1.8%)0.876s21.00x
▲ VercelNitro17.791s (+2.4%)18.766s (~)0.975s21.01x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.189s28.050s0.862s31.00x
🌐 RedisNext.js (Turbopack)27.233s28.058s0.825s31.00x
💻 LocalNitro27.534s (~)28.054s (~)0.520s31.01x
💻 LocalExpress27.549s (~)28.053s (~)0.504s31.01x
🌐 MongoDBNext.js (Turbopack)30.633s31.056s0.424s21.13x
🐘 PostgresNext.js (Turbopack)37.971s38.090s0.118s21.40x
🐘 PostgresExpress50.161s (+30.6% 🔺)50.629s (+29.5% 🔺)0.468s21.84x
🐘 PostgresNitro50.337s (+32.9% 🔺)51.131s (+34.2% 🔺)0.795s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express69.695s (+54.5% 🔺)70.938s (+53.0% 🔺)1.243s11.00x
▲ VercelNitro70.423s (+66.2% 🔺)71.351s (+63.6% 🔺)0.928s11.01x
▲ VercelNext.js (Turbopack)70.900s (+64.7% 🔺)72.015s (+63.7% 🔺)1.115s11.02x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)54.959s55.107s0.147s21.00x
💻 LocalNext.js (Turbopack)56.821s57.098s0.277s21.03x
💻 LocalNitro57.415s (~)58.105s (~)0.690s21.04x
💻 LocalExpress57.420s (~)58.102s (~)0.682s21.04x
🌐 MongoDBNext.js (Turbopack)61.261s62.089s0.828s21.11x
🐘 PostgresNext.js (Turbopack)70.379s70.653s0.274s21.28x
🐘 PostgresNitro100.268s (+33.2% 🔺)101.233s (+32.9% 🔺)0.965s11.82x
🐘 PostgresExpress100.368s (+32.8% 🔺)101.239s (+32.9% 🔺)0.871s11.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express88.970s (-4.8%)90.053s (-5.1% 🟢)1.083s11.00x
▲ VercelNext.js (Turbopack)92.487s (~)92.957s (-1.2%)0.470s11.04x
▲ VercelNitro92.749s (~)93.604s (~)0.855s11.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.294s2.007s0.713s151.00x
💻 LocalExpress1.417s (-0.6%)2.006s (~)0.588s151.10x
💻 LocalNext.js (Turbopack)1.423s2.006s0.583s151.10x
💻 LocalNitro1.437s (+3.1%)2.006s (~)0.569s151.11x
🐘 PostgresNext.js (Turbopack)1.993s2.473s0.480s131.54x
🌐 MongoDBNext.js (Turbopack)2.169s3.009s0.840s101.68x
🐘 PostgresNitro2.313s (+4.6%)3.014s (+12.5% 🔺)0.701s101.79x
🐘 PostgresExpress2.373s (+21.7% 🔺)3.013s (+35.4% 🔺)0.640s101.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.497s (-39.3% 🟢)3.294s (-37.6% 🟢)0.798s101.00x
▲ VercelExpress2.670s (-70.5% 🟢)3.634s (-68.7% 🟢)0.964s91.07x
▲ VercelNitro2.807s (-70.1% 🟢)3.832s (-68.6% 🟢)1.024s81.12x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.560s3.008s0.448s101.00x
💻 LocalExpress2.610s (-7.6% 🟢)3.007s (~)0.398s101.02x
🌐 RedisNext.js (Turbopack)2.612s3.009s0.397s101.02x
💻 LocalNitro2.708s (+5.2% 🔺)3.007s (~)0.299s101.06x
🌐 MongoDBNext.js (Turbopack)4.772s5.180s0.408s61.86x
🐘 PostgresExpress8.159s (-12.9% 🟢)8.527s (-12.8% 🟢)0.368s43.19x
🐘 PostgresNitro9.134s (-8.4% 🟢)9.536s (-7.3% 🟢)0.402s43.57x
🐘 PostgresNext.js (Turbopack)12.189s12.367s0.178s34.76x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.961s (-82.2% 🟢)3.854s (-78.2% 🟢)0.894s81.00x
▲ VercelExpress3.024s (-83.0% 🟢)4.186s (-78.4% 🟢)1.162s81.02x
▲ VercelNitro3.030s (-78.9% 🟢)3.872s (-75.1% 🟢)0.842s81.02x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.130s5.013s0.883s61.00x
💻 LocalExpress7.513s (-10.7% 🟢)8.017s (-11.1% 🟢)0.505s41.82x
💻 LocalNext.js (Turbopack)7.596s8.265s0.669s41.84x
💻 LocalNitro7.637s (+5.4% 🔺)8.020s (~)0.383s41.85x
🌐 MongoDBNext.js (Turbopack)9.989s10.683s0.694s32.42x
🐘 PostgresNitro41.518s (-16.0% 🟢)42.127s (-15.9% 🟢)0.609s110.05x
🐘 PostgresExpress49.459s (-3.1%)50.130s (-2.0%)0.671s111.98x
🐘 PostgresNext.js (Turbopack)56.245s57.121s0.876s113.62x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.822s (-83.1% 🟢)5.752s (-75.8% 🟢)1.929s61.00x
▲ VercelNext.js (Turbopack)3.886s (-80.6% 🟢)4.752s (-77.2% 🟢)0.866s71.02x
▲ VercelExpress6.036s (+45.8% 🔺)7.351s (+34.5% 🔺)1.315s51.58x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.260s2.007s0.746s151.00x
💻 LocalExpress1.413s (-4.4%)2.006s (~)0.593s151.12x
💻 LocalNext.js (Turbopack)1.435s2.005s0.570s151.14x
💻 LocalNitro1.442s (+1.0%)2.006s (~)0.563s151.14x
🐘 PostgresNext.js (Turbopack)1.628s2.011s0.383s151.29x
🐘 PostgresNitro2.034s (-5.5% 🟢)2.922s (+6.6% 🔺)0.888s111.61x
🐘 PostgresExpress2.054s (-3.4%)2.681s (+3.3%)0.627s121.63x
🌐 MongoDBNext.js (Turbopack)2.170s3.007s0.837s101.72x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.243s (-5.5% 🟢)3.190s (-5.1% 🟢)0.947s101.00x
▲ VercelNext.js (Turbopack)2.279s (-4.7%)3.202s (-2.6%)0.924s101.02x
▲ VercelExpress2.343s (+4.7%)3.500s (+8.7% 🔺)1.156s91.04x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)2.545s3.009s0.464s101.00x
💻 LocalNext.js (Turbopack)2.657s3.009s0.352s101.04x
💻 LocalExpress2.730s (-3.8%)3.008s (~)0.279s101.07x
💻 LocalNitro2.745s (+1.1%)3.008s (~)0.262s101.08x
🌐 MongoDBNext.js (Turbopack)4.737s5.175s0.438s61.86x
🐘 PostgresExpress10.086s (-17.6% 🟢)10.696s (-15.8% 🟢)0.610s33.96x
🐘 PostgresNitro10.218s (+2.1%)11.032s (+3.1%)0.815s34.02x
🐘 PostgresNext.js (Turbopack)13.493s13.703s0.210s35.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.400s (-22.6% 🟢)3.309s (-19.9% 🟢)0.909s101.00x
▲ VercelNext.js (Turbopack)2.418s (-9.1% 🟢)3.105s (-16.2% 🟢)0.687s101.01x
▲ VercelExpress2.790s (-9.9% 🟢)3.753s (-7.1% 🟢)0.963s81.16x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.166s5.012s0.846s61.00x
💻 LocalNext.js (Turbopack)7.619s8.016s0.398s41.83x
💻 LocalExpress8.076s (-6.4% 🟢)8.773s (-2.8%)0.697s41.94x
💻 LocalNitro8.131s (+5.2% 🔺)9.021s (+9.1% 🔺)0.890s41.95x
🌐 MongoDBNext.js (Turbopack)9.835s10.349s0.513s32.36x
🐘 PostgresExpress50.121s (-2.8%)51.108s (-1.9%)0.987s112.03x
🐘 PostgresNitro50.610s (+0.9%)51.124s (~)0.514s112.15x
🐘 PostgresNext.js (Turbopack)56.644s57.134s0.490s113.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.941s (-3.2%)4.317s (+10.5% 🔺)1.376s71.00x
▲ VercelNitro3.249s (+11.5% 🔺)4.335s (+6.0% 🔺)1.085s81.10x
▲ VercelNext.js (Turbopack)4.099s (+28.6% 🔺)5.068s (+25.3% 🔺)0.969s61.39x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.147s1.002s0.012s1.017s0.870s101.00x
🌐 RedisNext.js (Turbopack)0.170s1.000s0.002s1.008s0.838s101.16x
💻 LocalNitro0.173s (+2.2%)1.002s (~)0.011s (+0.9%)1.017s (~)0.844s101.17x
💻 LocalExpress0.175s (-3.2%)1.002s (~)0.011s (-5.1% 🟢)1.016s (~)0.842s101.19x
🌐 MongoDBNext.js (Turbopack)0.492s0.956s0.001s1.009s0.517s103.35x
🐘 PostgresNext.js (Turbopack)0.782s0.750s0.001s1.010s0.228s105.32x
🐘 PostgresNitro2.372s (+76.5% 🔺)2.672s (+57.6% 🔺)0.001s (+16.7% 🔺)3.015s (+49.8% 🔺)0.644s1016.12x
🐘 PostgresExpress2.473s (+69.1% 🔺)2.569s (+55.2% 🔺)0.002s (+25.0% 🔺)3.015s (+49.8% 🔺)0.542s1016.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.068s (-21.7% 🟢)2.618s (-27.6% 🟢)0.180s (-13.3% 🟢)3.394s (-22.4% 🟢)1.326s101.00x
▲ VercelNext.js (Turbopack)2.147s (+5.4% 🔺)2.583s (-1.2%)0.227s (-2.6%)3.457s (~)1.310s101.04x
▲ VercelExpress2.244s (-28.9% 🟢)2.818s (-45.6% 🟢)0.244s (+29.3% 🔺)3.719s (-39.2% 🟢)1.475s101.08x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)8/12
🐘 PostgresNext.js (Turbopack)7/12
▲ VercelNitro5/12
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local10/12
Next.js (Turbopack)💻 Local6/12
Nitro💻 Local9/12
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

@TooTallNate
TooTallNate marked this pull request as ready for review February 16, 2026 17:27
@github-actions

github-actionsBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production512038550
✅ 💻 Local Development532068600
✅ 📦 Local Production532068600
✅ 🐘 Local Postgres532068600
✅ 🪟 Windows470350
❌ 🌍 Community Worlds107439159
✅ 📋 Other129021150
Total2391432752709

❌ Failed Tests

🌍 Community Worlds (43 failed)

turso (43 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • parallelSleepWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • 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 retry behavior workflow completes despite transient 5xx on step_completed
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • closureVariableWorkflow - nested step functions with closure variables
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4604
✅ example4604
✅ express4604
✅ fastify4604
✅ hono4604
✅ nextjs-turbopack4901
✅ nextjs-webpack4901
✅ nitro4604
✅ nuxt4604
✅ sveltekit4604
✅ vite4604
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4703
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
✅ mongodb4703
✅ redis-dev300
✅ redis4703
✅ turso-dev300
❌ turso4433
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4307
✅ e2e-local-postgres-nest-stable4307
✅ e2e-local-prod-nest-stable4307

📋 View full workflow run

CopilotAI review requested due to automatic review settings February 16, 2026 17:27
@TooTallNateGraphite App

Copy link
Copy Markdown
MemberAuthor

This stack of pull requests is managed by Graphite. Learn more about stacking.

@vercel

vercelBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-astro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-express-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-fastify-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-hono-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nitro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nuxt-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-sveltekit-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-vite-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-nestReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-swc-playgroundReadyReadyPreview, CommentFeb 16, 2026 5:56pm
1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
workflow-docsSkippedSkippedFeb 16, 2026 5:56pm

@TooTallNate
TooTallNate requested review from a team and removed request for CopilotFebruary 16, 2026 17:27
Comment threadpackages/workflow/package.json
…ge.json breaks 5 files in `packages/world-testing` that still import `hydrateWorkflowReturnValue` from that path.
Co-authored-by: TooTallNate <n@n8.io>
CopilotAI review requested due to automatic review settings February 16, 2026 17:52
@TooTallNate
TooTallNate removed the request for review from CopilotFebruary 16, 2026 17:52
@vercel
vercelBottemporarily deployed to Preview – workflow-docs February 16, 2026 17:52 Inactive

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request removes the workflow/internal/serialization export which was only being used in e2e test files. The changes simplify the codebase by:

  • Removing the re-export wrapper in the workflow package
  • Migrating test files to use direct imports from @workflow/core/serialization
  • Simplifying NextJS Pages Router API endpoints to use JSON instead of binary serialization

Changes:

  • Removed workflow/internal/serialization export from the workflow package
  • Updated world-testing files to import from @workflow/core/serialization directly
  • Simplified NextJS test API endpoints to use JSON body parsing instead of binary serialization

Reviewed changes

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

Show a summary per file
FileDescription
packages/workflow/src/internal/serialization.tsDeleted re-export file
packages/workflow/package.jsonRemoved export entry for internal/serialization
workbench/nextjs-webpack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
workbench/nextjs-turbopack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
packages/world-testing/src/*.mtsUpdated imports to use @workflow/core/serialization
packages/world-testing/package.jsonAdded @workflow/core dependency
pnpm-lock.yamlUpdated lockfile for new dependency
.changeset/cyan-ravens-eat.mdAdded changeset documenting the removal
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

@TooTallNate
TooTallNate merged commit 0946dad into mainFeb 16, 2026
110 checks passed
@TooTallNate
TooTallNate deleted the 02-16-remove_workflow_internal_serialization_export branch February 16, 2026 19:50
pranaygp added a commit that referenced this pull request Feb 17, 2026
* origin/main:
Remove "workflow/internal/serialization" export (#1082)
TooTallNate added a commit that referenced this pull request Apr 19, 2026
…/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
TooTallNate added a commit that referenced this pull request Apr 30, 2026
…isites
Standardize on `Symbol.for('workflow-serialize')` /
`Symbol.for('workflow-deserialize')` everywhere — the parallel
`globalThis.__wdk_serialize` / `__wdk_deserialize` aliases have been
removed from `vm-bundle-entry.ts` and the snapshot runtime's inline
JS strings now use the symbol form directly. Single canonical name,
no duplication.
Drop the `?? Math.random` and `?? Date.now()` fallbacks from the
ULID generator setup. Both prerequisites
(`globalThis.__ulidTimestamp` and the host-replaced seeded
`Math.random`) are always set by `snapshot-runtime.ts` before the
serde bundle is evaluated; silently falling back to unseeded
`Math.random` or live `Date.now()` would re-introduce the
non-determinism we deliberately fixed (concurrent VM invocations of
the same resumption must produce identical correlationIds for the
world's EntityConflictError dedup to work). Now throws if
`__ulidTimestamp` isn't a number, and passes the seeded
`Math.random` reference explicitly to `monotonicFactory` so
upstream's `detectPRNG` never runs (it'd throw in QuickJS anyway,
since `crypto` is unavailable).
Drop the `URL` / `URLSearchParams` / `DOMException` availability
guards in `common-vm.ts`. quickjs-wasi's URL extension is always
loaded (`url.so`) and DOMException is always constructible — the
guards were dead code carried over from when those weren't reliably
available. The reducer/reviver code is now straightforward
`instanceof URL` / `new URL(...)` / `new DOMException(...)`.
Remove `packages/core/src/serialization/base64.ts` and its
sub-path exports (`./serialization/workflow`,
`./serialization/workflow-vm`). The pure-JS base64 helpers were
leftover from before `base64.so` shipped `btoa`/`atob` natively;
the VM-side reducers in `common-vm.ts` now build base64 strings via
the native ones. The sub-path exports had zero consumers in this
repo (the same cleanup landed on the `serialization-refactor`
branch in 05e0fee but never made it onto `snapshot-runtime`
because the branches diverged earlier).
Remove `packages/workflow/src/internal/serialization.ts` and its
`./internal/serialization` package.json export. Same story — zero
consumers, previously removed in #1082, then accidentally
reintroduced via `f04fd8e91`.
TooTallNate added a commit that referenced this pull request May 1, 2026
…to existing pipeline (#1299)
* Add serialization module foundation: types, codec interface, format prefix
Start of the serialization refactor (separate from snapshot-runtime).
New files:
- serialization/types.ts — SerializationFormat enum, SerializableSpecial
interface, Reducers/Revivers types
- serialization/codec.ts — Codec interface with formatPrefix, serialize,
deserialize, and optional deserializeLegacy
- serialization/format.ts — Format prefix encode/decode/peek, moved from
the monolithic serialization.ts
The Codec interface enables future alternative formats (CBOR, JSON) while
keeping the devalue implementation as the current default.
* Add reducers, devalue codec, encryption, and mode-specific modules
Serialization refactor Phase 1: create the new module structure alongside
the existing monolithic serialization.ts (which continues to work).
New files:
- serialization/reducers/common.ts — Date, Error, Map, Set, URL, BigInt,
typed arrays, Headers, Request, Response, RegExp, URLSearchParams
- serialization/reducers/class.ts — Class/Instance with WORKFLOW_SERIALIZE/
DESERIALIZE support
- serialization/reducers/step-function.ts — StepFunction with closure vars
- serialization/codec-devalue.ts — devalue Codec implementation
- serialization/encryption.ts — composable encrypt/decrypt layer
- serialization/workflow.ts — synchronous, no encryption, for VM use
- serialization/step.ts — async with encryption, for step handler
- serialization/client.ts — async with encryption, for start() API
- serialization/index.ts — re-exports all public API
- serialization/serialization.test.ts — 25 focused tests
All modes compose their reducer/reviver sets from the shared building blocks.
Cross-mode compatibility verified: data serialized in any mode can be
deserialized in any other mode (for common types).
Existing 108 serialization tests continue to pass unchanged.
* Add sub-path exports for workflow serialization module
- Add ./serialization/workflow export to @workflow/core package.json
- Add ./internal/serialization re-export to workflow meta-package
- The workflow bundle can now import serialize/deserialize via:
import { serialize, deserialize } from 'workflow/internal/serialization'
Full test suite passes: 493 tests across 22 files (including 25 new
serialization module tests).
* Address code review feedback
1. Fix reducer composition order: Class/Instance reducers now come BEFORE
common reducers in all three modes (workflow, step, client). This ensures
custom Error subclasses with WORKFLOW_SERIALIZE are handled by the
Instance reducer before the generic Error reducer (devalue uses
first-match-wins semantics).
2. Fix encryption decrypt() to fail fast when encrypted data is encountered
without a decryption key, instead of silently returning encrypted bytes
that would fail later with an unhelpful format error.
3. Remove Request/Response from common reducers — they don't have matching
common revivers, so including them caused asymmetric behavior (serialize
as Request, deserialize as plain object). Request/Response handling
belongs in mode-specific modules that can provide proper revivers.
4. Document Node.js dependency in the workflow serialization re-export.
The current implementation uses node:util and Buffer. For the QuickJS
VM (snapshot runtime), these will need polyfills — tracked separately.
* Move reducer/reviver composition into the devalue codec
The Codec interface now takes a SerializationMode ('workflow', 'step',
'client') instead of raw reducers/revivers. The reducer/reviver
composition is internal to the devalue codec implementation.
This is the right abstraction because reducers/revivers are devalue-
specific concepts. A future CBOR codec would handle Date, typed arrays,
Map, Set natively via the CBOR type system — it wouldn't use reducers
at all. A JSON codec would only support standard JSON types.
The mode-specific modules (workflow.ts, step.ts, client.ts) are now
simpler — they just pass the mode string to the codec.
* Replace SerializationFormatType enum with open-ended FormatPrefix type
The format prefix is now a branded string type validated by
isFormatPrefix() — any 4-character [a-z0-9] string is valid.
This removes the hard-coded enum of known formats, making the system
truly open for extension:
type FormatPrefix = string & { __brand: 'FormatPrefix' };
function isFormatPrefix(value: string): value is FormatPrefix;
The SerializationFormat object still provides well-known constants
('devl', 'encr') but they're now just typed constants, not an
exhaustive enum.
peekFormatPrefix() and decodeFormatPrefix() use isFormatPrefix() for
validation instead of checking against a known list. Unknown but valid
prefixes (e.g. 'cbor', 'json', 'v2b1') are accepted — the caller
decides whether they can handle the format.
6 new isFormatPrefix tests covering: valid strings, too short, too long,
uppercase, special characters. 1 new test for unknown-but-valid prefixes.
* Wire modular serialization modules into serialization.ts, add 138 unit tests
Replace duplicate format prefix, reducer/reviver, and encryption helper
code in the monolithic serialization.ts with imports from the modular
serialization/ directory. This completes the refactoring started in the
earlier additive-only commits.
Key changes:
- serialization.ts now imports types, format prefix, common/class/step-function
reducers and revivers, and encryption helpers from ./serialization/ modules
- Removed ~450 lines of duplicate code from serialization.ts
- Made encryption error messages consistent between old and new modules
- Added 138 comprehensive unit tests covering types, format prefix,
encryption, codec, all three reducer modules, all three mode modules,
cross-mode compatibility, and edge cases
- Updated one existing test assertion for new error message wording
* Address code review feedback
- encryption.ts: throw WorkflowRuntimeError instead of plain Error in
decrypt() to preserve the error contract from legacy maybeDecrypt()
- format.ts: document that open-ended prefix validation ([a-z0-9]{4})
is intentional for forward compatibility — callers check support
- errors.ts: extract duplicated formatSerializationError into shared
utility, remove 4 copies from workflow.ts, step.ts, client.ts
- codec-devalue.ts: document that globalThis default is a known
limitation; legacy dehydrate/hydrate path still supports custom global
* Fix codec-devalue.ts comment: clarify modular modules are not used in current runtime
The globalThis default is not a limitation for the current runtime —
all serialization goes through dehydrate*/hydrate* in serialization.ts
which passes the correct global. The modular modules are infrastructure
for the future snapshot runtime where serialization runs inside the VM.
* Wire dehydrate/hydrate functions through modular serialize/deserialize
The dehydrate*/hydrate* functions in serialization.ts now delegate to
the modular mode modules (workflowModule, stepModule, clientModule)
instead of directly calling devalue stringify/parse/unflatten.
Key changes:
- Extended Codec interface with CodecOptions (global, extraReducers,
extraRevivers) so the codec can receive VM globals and mode-specific
stream/Request/Response handlers
- devalueCodec threads global through to all reducer/reviver factories
so instanceof checks work across VM boundaries
- Mode modules (workflow.ts, step.ts, client.ts) accept CodecOptions
and pass them through to the codec
- dehydrate*/hydrate* functions now call module serialize/deserialize
with stream and Request/Response reducers/revivers passed as extras
- v1Compat path remains inline (pre-codec, uses stringify + revive)
- Error context strings preserved via try/catch re-wrapping
* Bump changeset from patch to minor for serialization refactor
Return types of public get*Reducers/get*Revivers functions narrowed
from Reducers/Revivers to Partial<Reducers>/Partial<Revivers>, which
is a TypeScript-level breaking change. Also adds new sub-path exports
(@workflow/core/serialization/workflow, workflow/internal/serialization)
which is additive. Minor bump is the appropriate semver for both.
* Remove unused workflow/internal/serialization re-export and @workflow/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
* Downgrade changeset from minor to patch
After auditing actual consumers of the narrowed return types
(getExternalReducers/getWorkflowReducers/getExternalRevivers/getWorkflowRevivers
now return Partial<Reducers>/Partial<Revivers>), no in-repo or external
consumer indexes specific keys on the returned object in a way that would
break. The only internal caller that did (runtime/run.ts) was updated in
this same PR. The narrowing is type-safer but effectively invisible at
runtime and for idiomatic callers that spread or forward the object.
Since the refactor is internally restructuring only, patch is the
appropriate semver bump.
* Trim serialization-refactor changeset
* Dedup formatSerializationError: import from serialization/errors.ts
The legacy serialization.ts had its own inlined copy of
formatSerializationError. Now that the helper is exported from
serialization/errors.ts (already consumed by workflow.ts/step.ts/client.ts),
import it here too to keep the single source of truth.
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.

4 participants

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

Remove "workflow/internal/serialization" export - #1082

Merged
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export
Feb 16, 2026
Merged

Remove "workflow/internal/serialization" export#1082
TooTallNate merged 3 commits into
mainfrom
02-16-remove_workflow_internal_serialization_export

Conversation

@TooTallNate

Copy link
Copy Markdown
Member

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.

Was only being used in these two e2e test files and the data being passed in those tests don't rely on any specialized data types, so just use JSON there.
@changeset-bot

changeset-botBot commented Feb 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: aa8c9bd

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

This PR includes changesets to release 14 packages
NameType
workflowPatch
@workflow/world-testingPatch
@workflow/corePatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/web-sharedPatch
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@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 Feb 16, 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🥇 Express0.032s (-6.9% 🟢)1.005s (~)0.973s101.00x
💻 LocalNitro0.032s (+2.5%)1.005s (~)0.972s101.00x
💻 LocalNext.js (Turbopack)0.043s1.005s0.962s101.33x
🌐 RedisNext.js (Turbopack)0.056s1.005s0.949s101.74x
🌐 MongoDBNext.js (Turbopack)0.103s1.007s0.904s103.21x
🐘 PostgresExpress0.155s (-23.4% 🟢)1.010s (-0.8%)0.854s104.82x
🐘 PostgresNitro0.251s (+10.8% 🔺)1.010s (-0.9%)0.758s107.81x
🐘 PostgresNext.js (Turbopack)0.370s1.009s0.639s1011.50x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro0.544s (-14.7% 🟢)1.950s (-9.5% 🟢)1.405s101.00x
▲ VercelNext.js (Turbopack)0.597s (-27.3% 🟢)1.924s (-10.9% 🟢)1.327s101.10x
▲ VercelExpress0.654s (+8.9% 🔺)2.105s (+2.0%)1.451s101.20x

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

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)1.096s2.006s0.910s101.00x
💻 LocalExpress1.105s (~)2.006s (~)0.901s101.01x
💻 LocalNitro1.106s (~)2.006s (~)0.901s101.01x
🌐 RedisNext.js (Turbopack)1.130s2.007s0.877s101.03x
🌐 MongoDBNext.js (Turbopack)1.310s2.008s0.698s101.20x
🐘 PostgresNext.js (Turbopack)2.254s3.015s0.761s102.06x
🐘 PostgresExpress2.376s (-3.5%)3.014s (~)0.638s102.17x
🐘 PostgresNitro2.425s (+6.8% 🔺)3.014s (+7.1% 🔺)0.590s102.21x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.276s (-5.5% 🟢)3.134s (-10.2% 🟢)0.858s101.00x
▲ VercelNitro2.337s (+1.7%)3.339s (-8.6% 🟢)1.003s101.03x
▲ VercelExpress2.746s (+21.6% 🔺)3.740s (+11.4% 🔺)0.995s101.21x

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

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)10.745s11.021s0.276s31.00x
💻 LocalExpress10.822s (-0.6%)11.021s (~)0.199s31.01x
🌐 RedisNext.js (Turbopack)10.831s11.026s0.195s31.01x
💻 LocalNitro10.838s (~)11.023s (~)0.185s31.01x
🌐 MongoDBNext.js (Turbopack)12.321s13.025s0.704s31.15x
🐘 PostgresNext.js (Turbopack)15.142s16.046s0.904s21.41x
🐘 PostgresNitro20.293s (+33.2% 🔺)21.058s (+31.2% 🔺)0.766s21.89x
🐘 PostgresExpress20.404s (+6.9% 🔺)21.059s (+7.7% 🔺)0.656s21.90x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)17.672s (-0.7%)18.305s (-3.7%)0.633s21.00x
▲ VercelExpress17.692s (~)18.568s (-1.8%)0.876s21.00x
▲ VercelNitro17.791s (+2.4%)18.766s (~)0.975s21.01x

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

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)27.189s28.050s0.862s31.00x
🌐 RedisNext.js (Turbopack)27.233s28.058s0.825s31.00x
💻 LocalNitro27.534s (~)28.054s (~)0.520s31.01x
💻 LocalExpress27.549s (~)28.053s (~)0.504s31.01x
🌐 MongoDBNext.js (Turbopack)30.633s31.056s0.424s21.13x
🐘 PostgresNext.js (Turbopack)37.971s38.090s0.118s21.40x
🐘 PostgresExpress50.161s (+30.6% 🔺)50.629s (+29.5% 🔺)0.468s21.84x
🐘 PostgresNitro50.337s (+32.9% 🔺)51.131s (+34.2% 🔺)0.795s21.85x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express69.695s (+54.5% 🔺)70.938s (+53.0% 🔺)1.243s11.00x
▲ VercelNitro70.423s (+66.2% 🔺)71.351s (+63.6% 🔺)0.928s11.01x
▲ VercelNext.js (Turbopack)70.900s (+64.7% 🔺)72.015s (+63.7% 🔺)1.115s11.02x

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

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)54.959s55.107s0.147s21.00x
💻 LocalNext.js (Turbopack)56.821s57.098s0.277s21.03x
💻 LocalNitro57.415s (~)58.105s (~)0.690s21.04x
💻 LocalExpress57.420s (~)58.102s (~)0.682s21.04x
🌐 MongoDBNext.js (Turbopack)61.261s62.089s0.828s21.11x
🐘 PostgresNext.js (Turbopack)70.379s70.653s0.274s21.28x
🐘 PostgresNitro100.268s (+33.2% 🔺)101.233s (+32.9% 🔺)0.965s11.82x
🐘 PostgresExpress100.368s (+32.8% 🔺)101.239s (+32.9% 🔺)0.871s11.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express88.970s (-4.8%)90.053s (-5.1% 🟢)1.083s11.00x
▲ VercelNext.js (Turbopack)92.487s (~)92.957s (-1.2%)0.470s11.04x
▲ VercelNitro92.749s (~)93.604s (~)0.855s11.04x

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

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.294s2.007s0.713s151.00x
💻 LocalExpress1.417s (-0.6%)2.006s (~)0.588s151.10x
💻 LocalNext.js (Turbopack)1.423s2.006s0.583s151.10x
💻 LocalNitro1.437s (+3.1%)2.006s (~)0.569s151.11x
🐘 PostgresNext.js (Turbopack)1.993s2.473s0.480s131.54x
🌐 MongoDBNext.js (Turbopack)2.169s3.009s0.840s101.68x
🐘 PostgresNitro2.313s (+4.6%)3.014s (+12.5% 🔺)0.701s101.79x
🐘 PostgresExpress2.373s (+21.7% 🔺)3.013s (+35.4% 🔺)0.640s101.83x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.497s (-39.3% 🟢)3.294s (-37.6% 🟢)0.798s101.00x
▲ VercelExpress2.670s (-70.5% 🟢)3.634s (-68.7% 🟢)0.964s91.07x
▲ VercelNitro2.807s (-70.1% 🟢)3.832s (-68.6% 🟢)1.024s81.12x

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

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)2.560s3.008s0.448s101.00x
💻 LocalExpress2.610s (-7.6% 🟢)3.007s (~)0.398s101.02x
🌐 RedisNext.js (Turbopack)2.612s3.009s0.397s101.02x
💻 LocalNitro2.708s (+5.2% 🔺)3.007s (~)0.299s101.06x
🌐 MongoDBNext.js (Turbopack)4.772s5.180s0.408s61.86x
🐘 PostgresExpress8.159s (-12.9% 🟢)8.527s (-12.8% 🟢)0.368s43.19x
🐘 PostgresNitro9.134s (-8.4% 🟢)9.536s (-7.3% 🟢)0.402s43.57x
🐘 PostgresNext.js (Turbopack)12.189s12.367s0.178s34.76x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.961s (-82.2% 🟢)3.854s (-78.2% 🟢)0.894s81.00x
▲ VercelExpress3.024s (-83.0% 🟢)4.186s (-78.4% 🟢)1.162s81.02x
▲ VercelNitro3.030s (-78.9% 🟢)3.872s (-75.1% 🟢)0.842s81.02x

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

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.130s5.013s0.883s61.00x
💻 LocalExpress7.513s (-10.7% 🟢)8.017s (-11.1% 🟢)0.505s41.82x
💻 LocalNext.js (Turbopack)7.596s8.265s0.669s41.84x
💻 LocalNitro7.637s (+5.4% 🔺)8.020s (~)0.383s41.85x
🌐 MongoDBNext.js (Turbopack)9.989s10.683s0.694s32.42x
🐘 PostgresNitro41.518s (-16.0% 🟢)42.127s (-15.9% 🟢)0.609s110.05x
🐘 PostgresExpress49.459s (-3.1%)50.130s (-2.0%)0.671s111.98x
🐘 PostgresNext.js (Turbopack)56.245s57.121s0.876s113.62x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro3.822s (-83.1% 🟢)5.752s (-75.8% 🟢)1.929s61.00x
▲ VercelNext.js (Turbopack)3.886s (-80.6% 🟢)4.752s (-77.2% 🟢)0.866s71.02x
▲ VercelExpress6.036s (+45.8% 🔺)7.351s (+34.5% 🔺)1.315s51.58x

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

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)1.260s2.007s0.746s151.00x
💻 LocalExpress1.413s (-4.4%)2.006s (~)0.593s151.12x
💻 LocalNext.js (Turbopack)1.435s2.005s0.570s151.14x
💻 LocalNitro1.442s (+1.0%)2.006s (~)0.563s151.14x
🐘 PostgresNext.js (Turbopack)1.628s2.011s0.383s151.29x
🐘 PostgresNitro2.034s (-5.5% 🟢)2.922s (+6.6% 🔺)0.888s111.61x
🐘 PostgresExpress2.054s (-3.4%)2.681s (+3.3%)0.627s121.63x
🌐 MongoDBNext.js (Turbopack)2.170s3.007s0.837s101.72x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.243s (-5.5% 🟢)3.190s (-5.1% 🟢)0.947s101.00x
▲ VercelNext.js (Turbopack)2.279s (-4.7%)3.202s (-2.6%)0.924s101.02x
▲ VercelExpress2.343s (+4.7%)3.500s (+8.7% 🔺)1.156s91.04x

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

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)2.545s3.009s0.464s101.00x
💻 LocalNext.js (Turbopack)2.657s3.009s0.352s101.04x
💻 LocalExpress2.730s (-3.8%)3.008s (~)0.279s101.07x
💻 LocalNitro2.745s (+1.1%)3.008s (~)0.262s101.08x
🌐 MongoDBNext.js (Turbopack)4.737s5.175s0.438s61.86x
🐘 PostgresExpress10.086s (-17.6% 🟢)10.696s (-15.8% 🟢)0.610s33.96x
🐘 PostgresNitro10.218s (+2.1%)11.032s (+3.1%)0.815s34.02x
🐘 PostgresNext.js (Turbopack)13.493s13.703s0.210s35.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.400s (-22.6% 🟢)3.309s (-19.9% 🟢)0.909s101.00x
▲ VercelNext.js (Turbopack)2.418s (-9.1% 🟢)3.105s (-16.2% 🟢)0.687s101.01x
▲ VercelExpress2.790s (-9.9% 🟢)3.753s (-7.1% 🟢)0.963s81.16x

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

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🌐 Redis🥇 Next.js (Turbopack)4.166s5.012s0.846s61.00x
💻 LocalNext.js (Turbopack)7.619s8.016s0.398s41.83x
💻 LocalExpress8.076s (-6.4% 🟢)8.773s (-2.8%)0.697s41.94x
💻 LocalNitro8.131s (+5.2% 🔺)9.021s (+9.1% 🔺)0.890s41.95x
🌐 MongoDBNext.js (Turbopack)9.835s10.349s0.513s32.36x
🐘 PostgresExpress50.121s (-2.8%)51.108s (-1.9%)0.987s112.03x
🐘 PostgresNitro50.610s (+0.9%)51.124s (~)0.514s112.15x
🐘 PostgresNext.js (Turbopack)56.644s57.134s0.490s113.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.941s (-3.2%)4.317s (+10.5% 🔺)1.376s71.00x
▲ VercelNitro3.249s (+11.5% 🔺)4.335s (+6.0% 🔺)1.085s81.10x
▲ VercelNext.js (Turbopack)4.099s (+28.6% 🔺)5.068s (+25.3% 🔺)0.969s61.39x

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

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)0.147s1.002s0.012s1.017s0.870s101.00x
🌐 RedisNext.js (Turbopack)0.170s1.000s0.002s1.008s0.838s101.16x
💻 LocalNitro0.173s (+2.2%)1.002s (~)0.011s (+0.9%)1.017s (~)0.844s101.17x
💻 LocalExpress0.175s (-3.2%)1.002s (~)0.011s (-5.1% 🟢)1.016s (~)0.842s101.19x
🌐 MongoDBNext.js (Turbopack)0.492s0.956s0.001s1.009s0.517s103.35x
🐘 PostgresNext.js (Turbopack)0.782s0.750s0.001s1.010s0.228s105.32x
🐘 PostgresNitro2.372s (+76.5% 🔺)2.672s (+57.6% 🔺)0.001s (+16.7% 🔺)3.015s (+49.8% 🔺)0.644s1016.12x
🐘 PostgresExpress2.473s (+69.1% 🔺)2.569s (+55.2% 🔺)0.002s (+25.0% 🔺)3.015s (+49.8% 🔺)0.542s1016.81x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Nitro2.068s (-21.7% 🟢)2.618s (-27.6% 🟢)0.180s (-13.3% 🟢)3.394s (-22.4% 🟢)1.326s101.00x
▲ VercelNext.js (Turbopack)2.147s (+5.4% 🔺)2.583s (-1.2%)0.227s (-2.6%)3.457s (~)1.310s101.04x
▲ VercelExpress2.244s (-28.9% 🟢)2.818s (-45.6% 🟢)0.244s (+29.3% 🔺)3.719s (-39.2% 🟢)1.475s101.08x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)8/12
🐘 PostgresNext.js (Turbopack)7/12
▲ VercelNitro5/12
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express💻 Local10/12
Next.js (Turbopack)💻 Local6/12
Nitro💻 Local9/12
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

@TooTallNate
TooTallNate marked this pull request as ready for review February 16, 2026 17:27
@github-actions

github-actionsBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
✅ ▲ Vercel Production512038550
✅ 💻 Local Development532068600
✅ 📦 Local Production532068600
✅ 🐘 Local Postgres532068600
✅ 🪟 Windows470350
❌ 🌍 Community Worlds107439159
✅ 📋 Other129021150
Total2391432752709

❌ Failed Tests

🌍 Community Worlds (43 failed)

turso (43 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • parallelSleepWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • 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 retry behavior workflow completes despite transient 5xx on step_completed
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • closureVariableWorkflow - nested step functions with closure variables
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro4604
✅ example4604
✅ express4604
✅ fastify4604
✅ hono4604
✅ nextjs-turbopack4901
✅ nextjs-webpack4901
✅ nitro4604
✅ nuxt4604
✅ sveltekit4604
✅ vite4604
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable4307
✅ express-stable4307
✅ fastify-stable4307
✅ hono-stable4307
✅ nextjs-turbopack-canary4703
✅ nextjs-turbopack-stable4703
✅ nextjs-webpack-canary4703
✅ nextjs-webpack-stable4703
✅ nitro-stable4307
✅ nuxt-stable4307
✅ sveltekit-stable4307
✅ vite-stable4307
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack4703
❌ 🌍 Community Worlds
AppPassedFailedSkipped
✅ mongodb-dev300
✅ mongodb4703
✅ redis-dev300
✅ redis4703
✅ turso-dev300
❌ turso4433
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable4307
✅ e2e-local-postgres-nest-stable4307
✅ e2e-local-prod-nest-stable4307

📋 View full workflow run

CopilotAI review requested due to automatic review settings February 16, 2026 17:27
@TooTallNateGraphite App

Copy link
Copy Markdown
MemberAuthor

This stack of pull requests is managed by Graphite. Learn more about stacking.

@vercel

vercelBot commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-nextjs-workflow-webpackReadyReadyPreview, CommentFeb 16, 2026 5:56pm
example-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-astro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-express-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-fastify-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-hono-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nitro-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-nuxt-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-sveltekit-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workbench-vite-workflowReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-nestReadyReadyPreview, CommentFeb 16, 2026 5:56pm
workflow-swc-playgroundReadyReadyPreview, CommentFeb 16, 2026 5:56pm
1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
workflow-docsSkippedSkippedFeb 16, 2026 5:56pm

@TooTallNate
TooTallNate requested review from a team and removed request for CopilotFebruary 16, 2026 17:27
Comment threadpackages/workflow/package.json
…ge.json breaks 5 files in `packages/world-testing` that still import `hydrateWorkflowReturnValue` from that path.
Co-authored-by: TooTallNate <n@n8.io>
CopilotAI review requested due to automatic review settings February 16, 2026 17:52
@TooTallNate
TooTallNate removed the request for review from CopilotFebruary 16, 2026 17:52
@vercel
vercelBottemporarily deployed to Preview – workflow-docs February 16, 2026 17:52 Inactive

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request removes the workflow/internal/serialization export which was only being used in e2e test files. The changes simplify the codebase by:

  • Removing the re-export wrapper in the workflow package
  • Migrating test files to use direct imports from @workflow/core/serialization
  • Simplifying NextJS Pages Router API endpoints to use JSON instead of binary serialization

Changes:

  • Removed workflow/internal/serialization export from the workflow package
  • Updated world-testing files to import from @workflow/core/serialization directly
  • Simplified NextJS test API endpoints to use JSON body parsing instead of binary serialization

Reviewed changes

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

Show a summary per file
FileDescription
packages/workflow/src/internal/serialization.tsDeleted re-export file
packages/workflow/package.jsonRemoved export entry for internal/serialization
workbench/nextjs-webpack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
workbench/nextjs-turbopack/pages/api/trigger-pages.tsRemoved binary serialization logic, simplified to use JSON body or query params
packages/world-testing/src/*.mtsUpdated imports to use @workflow/core/serialization
packages/world-testing/package.jsonAdded @workflow/core dependency
pnpm-lock.yamlUpdated lockfile for new dependency
.changeset/cyan-ravens-eat.mdAdded changeset documenting the removal
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

@TooTallNate
TooTallNate merged commit 0946dad into mainFeb 16, 2026
110 checks passed
@TooTallNate
TooTallNate deleted the 02-16-remove_workflow_internal_serialization_export branch February 16, 2026 19:50
pranaygp added a commit that referenced this pull request Feb 17, 2026
* origin/main:
Remove "workflow/internal/serialization" export (#1082)
TooTallNate added a commit that referenced this pull request Apr 19, 2026
…/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
TooTallNate added a commit that referenced this pull request Apr 30, 2026
…isites
Standardize on `Symbol.for('workflow-serialize')` /
`Symbol.for('workflow-deserialize')` everywhere — the parallel
`globalThis.__wdk_serialize` / `__wdk_deserialize` aliases have been
removed from `vm-bundle-entry.ts` and the snapshot runtime's inline
JS strings now use the symbol form directly. Single canonical name,
no duplication.
Drop the `?? Math.random` and `?? Date.now()` fallbacks from the
ULID generator setup. Both prerequisites
(`globalThis.__ulidTimestamp` and the host-replaced seeded
`Math.random`) are always set by `snapshot-runtime.ts` before the
serde bundle is evaluated; silently falling back to unseeded
`Math.random` or live `Date.now()` would re-introduce the
non-determinism we deliberately fixed (concurrent VM invocations of
the same resumption must produce identical correlationIds for the
world's EntityConflictError dedup to work). Now throws if
`__ulidTimestamp` isn't a number, and passes the seeded
`Math.random` reference explicitly to `monotonicFactory` so
upstream's `detectPRNG` never runs (it'd throw in QuickJS anyway,
since `crypto` is unavailable).
Drop the `URL` / `URLSearchParams` / `DOMException` availability
guards in `common-vm.ts`. quickjs-wasi's URL extension is always
loaded (`url.so`) and DOMException is always constructible — the
guards were dead code carried over from when those weren't reliably
available. The reducer/reviver code is now straightforward
`instanceof URL` / `new URL(...)` / `new DOMException(...)`.
Remove `packages/core/src/serialization/base64.ts` and its
sub-path exports (`./serialization/workflow`,
`./serialization/workflow-vm`). The pure-JS base64 helpers were
leftover from before `base64.so` shipped `btoa`/`atob` natively;
the VM-side reducers in `common-vm.ts` now build base64 strings via
the native ones. The sub-path exports had zero consumers in this
repo (the same cleanup landed on the `serialization-refactor`
branch in 05e0fee but never made it onto `snapshot-runtime`
because the branches diverged earlier).
Remove `packages/workflow/src/internal/serialization.ts` and its
`./internal/serialization` package.json export. Same story — zero
consumers, previously removed in #1082, then accidentally
reintroduced via `f04fd8e91`.
TooTallNate added a commit that referenced this pull request May 1, 2026
…to existing pipeline (#1299)
* Add serialization module foundation: types, codec interface, format prefix
Start of the serialization refactor (separate from snapshot-runtime).
New files:
- serialization/types.ts — SerializationFormat enum, SerializableSpecial
interface, Reducers/Revivers types
- serialization/codec.ts — Codec interface with formatPrefix, serialize,
deserialize, and optional deserializeLegacy
- serialization/format.ts — Format prefix encode/decode/peek, moved from
the monolithic serialization.ts
The Codec interface enables future alternative formats (CBOR, JSON) while
keeping the devalue implementation as the current default.
* Add reducers, devalue codec, encryption, and mode-specific modules
Serialization refactor Phase 1: create the new module structure alongside
the existing monolithic serialization.ts (which continues to work).
New files:
- serialization/reducers/common.ts — Date, Error, Map, Set, URL, BigInt,
typed arrays, Headers, Request, Response, RegExp, URLSearchParams
- serialization/reducers/class.ts — Class/Instance with WORKFLOW_SERIALIZE/
DESERIALIZE support
- serialization/reducers/step-function.ts — StepFunction with closure vars
- serialization/codec-devalue.ts — devalue Codec implementation
- serialization/encryption.ts — composable encrypt/decrypt layer
- serialization/workflow.ts — synchronous, no encryption, for VM use
- serialization/step.ts — async with encryption, for step handler
- serialization/client.ts — async with encryption, for start() API
- serialization/index.ts — re-exports all public API
- serialization/serialization.test.ts — 25 focused tests
All modes compose their reducer/reviver sets from the shared building blocks.
Cross-mode compatibility verified: data serialized in any mode can be
deserialized in any other mode (for common types).
Existing 108 serialization tests continue to pass unchanged.
* Add sub-path exports for workflow serialization module
- Add ./serialization/workflow export to @workflow/core package.json
- Add ./internal/serialization re-export to workflow meta-package
- The workflow bundle can now import serialize/deserialize via:
import { serialize, deserialize } from 'workflow/internal/serialization'
Full test suite passes: 493 tests across 22 files (including 25 new
serialization module tests).
* Address code review feedback
1. Fix reducer composition order: Class/Instance reducers now come BEFORE
common reducers in all three modes (workflow, step, client). This ensures
custom Error subclasses with WORKFLOW_SERIALIZE are handled by the
Instance reducer before the generic Error reducer (devalue uses
first-match-wins semantics).
2. Fix encryption decrypt() to fail fast when encrypted data is encountered
without a decryption key, instead of silently returning encrypted bytes
that would fail later with an unhelpful format error.
3. Remove Request/Response from common reducers — they don't have matching
common revivers, so including them caused asymmetric behavior (serialize
as Request, deserialize as plain object). Request/Response handling
belongs in mode-specific modules that can provide proper revivers.
4. Document Node.js dependency in the workflow serialization re-export.
The current implementation uses node:util and Buffer. For the QuickJS
VM (snapshot runtime), these will need polyfills — tracked separately.
* Move reducer/reviver composition into the devalue codec
The Codec interface now takes a SerializationMode ('workflow', 'step',
'client') instead of raw reducers/revivers. The reducer/reviver
composition is internal to the devalue codec implementation.
This is the right abstraction because reducers/revivers are devalue-
specific concepts. A future CBOR codec would handle Date, typed arrays,
Map, Set natively via the CBOR type system — it wouldn't use reducers
at all. A JSON codec would only support standard JSON types.
The mode-specific modules (workflow.ts, step.ts, client.ts) are now
simpler — they just pass the mode string to the codec.
* Replace SerializationFormatType enum with open-ended FormatPrefix type
The format prefix is now a branded string type validated by
isFormatPrefix() — any 4-character [a-z0-9] string is valid.
This removes the hard-coded enum of known formats, making the system
truly open for extension:
type FormatPrefix = string & { __brand: 'FormatPrefix' };
function isFormatPrefix(value: string): value is FormatPrefix;
The SerializationFormat object still provides well-known constants
('devl', 'encr') but they're now just typed constants, not an
exhaustive enum.
peekFormatPrefix() and decodeFormatPrefix() use isFormatPrefix() for
validation instead of checking against a known list. Unknown but valid
prefixes (e.g. 'cbor', 'json', 'v2b1') are accepted — the caller
decides whether they can handle the format.
6 new isFormatPrefix tests covering: valid strings, too short, too long,
uppercase, special characters. 1 new test for unknown-but-valid prefixes.
* Wire modular serialization modules into serialization.ts, add 138 unit tests
Replace duplicate format prefix, reducer/reviver, and encryption helper
code in the monolithic serialization.ts with imports from the modular
serialization/ directory. This completes the refactoring started in the
earlier additive-only commits.
Key changes:
- serialization.ts now imports types, format prefix, common/class/step-function
reducers and revivers, and encryption helpers from ./serialization/ modules
- Removed ~450 lines of duplicate code from serialization.ts
- Made encryption error messages consistent between old and new modules
- Added 138 comprehensive unit tests covering types, format prefix,
encryption, codec, all three reducer modules, all three mode modules,
cross-mode compatibility, and edge cases
- Updated one existing test assertion for new error message wording
* Address code review feedback
- encryption.ts: throw WorkflowRuntimeError instead of plain Error in
decrypt() to preserve the error contract from legacy maybeDecrypt()
- format.ts: document that open-ended prefix validation ([a-z0-9]{4})
is intentional for forward compatibility — callers check support
- errors.ts: extract duplicated formatSerializationError into shared
utility, remove 4 copies from workflow.ts, step.ts, client.ts
- codec-devalue.ts: document that globalThis default is a known
limitation; legacy dehydrate/hydrate path still supports custom global
* Fix codec-devalue.ts comment: clarify modular modules are not used in current runtime
The globalThis default is not a limitation for the current runtime —
all serialization goes through dehydrate*/hydrate* in serialization.ts
which passes the correct global. The modular modules are infrastructure
for the future snapshot runtime where serialization runs inside the VM.
* Wire dehydrate/hydrate functions through modular serialize/deserialize
The dehydrate*/hydrate* functions in serialization.ts now delegate to
the modular mode modules (workflowModule, stepModule, clientModule)
instead of directly calling devalue stringify/parse/unflatten.
Key changes:
- Extended Codec interface with CodecOptions (global, extraReducers,
extraRevivers) so the codec can receive VM globals and mode-specific
stream/Request/Response handlers
- devalueCodec threads global through to all reducer/reviver factories
so instanceof checks work across VM boundaries
- Mode modules (workflow.ts, step.ts, client.ts) accept CodecOptions
and pass them through to the codec
- dehydrate*/hydrate* functions now call module serialize/deserialize
with stream and Request/Response reducers/revivers passed as extras
- v1Compat path remains inline (pre-codec, uses stringify + revive)
- Error context strings preserved via try/catch re-wrapping
* Bump changeset from patch to minor for serialization refactor
Return types of public get*Reducers/get*Revivers functions narrowed
from Reducers/Revivers to Partial<Reducers>/Partial<Revivers>, which
is a TypeScript-level breaking change. Also adds new sub-path exports
(@workflow/core/serialization/workflow, workflow/internal/serialization)
which is additive. Minor bump is the appropriate semver for both.
* Remove unused workflow/internal/serialization re-export and @workflow/core/serialization/workflow sub-path
Both exports had zero consumers in the repo. The workflow/internal/serialization
export was previously removed on main in #1082 for the same reason. The
modular workflow.serialize/deserialize is still reachable via
@workflow/core/serialization when needed. These exports can be reintroduced
by the snapshot runtime branch if/when it actually needs them.
Also updates the changeset to drop the 'new sub-path exports' bullet.
* Downgrade changeset from minor to patch
After auditing actual consumers of the narrowed return types
(getExternalReducers/getWorkflowReducers/getExternalRevivers/getWorkflowRevivers
now return Partial<Reducers>/Partial<Revivers>), no in-repo or external
consumer indexes specific keys on the returned object in a way that would
break. The only internal caller that did (runtime/run.ts) was updated in
this same PR. The narrowing is type-safer but effectively invisible at
runtime and for idiomatic callers that spread or forward the object.
Since the refactor is internally restructuring only, patch is the
appropriate semver bump.
* Trim serialization-refactor changeset
* Dedup formatSerializationError: import from serialization/errors.ts
The legacy serialization.ts had its own inlined copy of
formatSerializationError. Now that the helper is exported from
serialization/errors.ts (already consumed by workflow.ts/step.ts/client.ts),
import it here too to keep the single source of truth.
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.

4 participants

@TooTallNate@VaguelySerious@karthikscale3