Skip to content

Conversation

@TooTallNate
Copy link
Member

@TooTallNate TooTallNate commented Jan 10, 2026

Added support for custom class instance serialization across workflow/step boundaries.

What changed?

  • Introduced a new @workflow/serde package with WORKFLOW_SERIALIZE and WORKFLOW_DESERIALIZE symbols
  • Enhanced the serialization system to handle custom class instances using these symbols
  • Updated the SWC plugin to detect classes with serialization methods and register them
  • Added class registry mechanism that works in both step and workflow contexts
  • Implemented comprehensive tests for various serialization scenarios

How to test?

The PR includes a new e2e test customSerializationWorkflow that demonstrates the feature:

import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from '@workflow/serde';

// Define a class with custom serialization
class Point {
  constructor(public x: number, public y: number) {}

  static [WORKFLOW_SERIALIZE](instance: Point) {
    return { x: instance.x, y: instance.y };
  }

  static [WORKFLOW_DESERIALIZE](data: { x: number; y: number }) {
    return new Point(data.x, data.y);
  }
}

// Use in workflow and steps
export async function customSerializationWorkflow(x: number, y: number) {
  'use workflow';
  const point = new Point(x, y);
  const scaled = await transformPoint(point, 2);
  // ...
}

Run the e2e test to verify that class instances are properly serialized and deserialized.

Why make this change?

Previously, user-defined class instances couldn't be passed between workflows and steps without losing their prototype chain and methods. This change allows developers to define custom serialization/deserialization logic for their classes, enabling proper reconstruction of instances with their full functionality intact when crossing workflow/step boundaries.

@changeset-bot
Copy link

changeset-bot bot commented Jan 10, 2026

🦋 Changeset detected

Latest commit: 3a3ac36

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

This PR includes changesets to release 15 packages
Name Type
@workflow/swc-plugin Patch
@workflow/core Patch
@workflow/astro Patch
@workflow/builders Patch
@workflow/cli Patch
@workflow/next Patch
@workflow/nitro Patch
@workflow/rollup Patch
@workflow/sveltekit Patch
@workflow/docs-typecheck Patch
@workflow/web-shared Patch
workflow Patch
@workflow/world-testing Patch
@workflow/nuxt Patch
@workflow/ai Patch

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

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

@vercel
Copy link
Contributor

vercel bot commented Jan 10, 2026

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

Project Deployment Review Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview, Comment Jan 16, 2026 10:24am
example-nextjs-workflow-webpack Ready Ready Preview, Comment Jan 16, 2026 10:24am
example-workflow Ready Ready Preview, Comment Jan 16, 2026 10:24am
workbench-astro-workflow Ready Ready Preview, Comment Jan 16, 2026 10:24am
workbench-express-workflow Ready Ready Preview, Comment Jan 16, 2026 10:24am
workbench-fastify-workflow Ready Ready Preview, Comment Jan 16, 2026 10:24am
workbench-hono-workflow Ready Ready Preview, Comment Jan 16, 2026 10:24am
workbench-nitro-workflow Ready Ready Preview, Comment Jan 16, 2026 10:24am
workbench-nuxt-workflow Ready Ready Preview, Comment Jan 16, 2026 10:24am
workbench-sveltekit-workflow Ready Ready Preview, Comment Jan 16, 2026 10:24am
workbench-vite-workflow Ready Ready Preview, Comment Jan 16, 2026 10:24am
workflow-docs Ready Ready Preview, Comment Jan 16, 2026 10:24am

@github-actions
Copy link
Contributor

github-actions bot commented Jan 10, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 424 0 38 462
✅ 💻 Local Development 388 0 32 420
✅ 📦 Local Production 388 0 32 420
✅ 🐘 Local Postgres 388 0 32 420
✅ 🪟 Windows 42 0 0 42
❌ 🌍 Community Worlds 159 21 0 180
Total 1789 21 134 1944

❌ Failed Tests

🌍 Community Worlds (21 failed)

mongodb (1 failed):

  • webhookWorkflow

redis (1 failed):

  • webhookWorkflow

starter (18 failed):

  • addTenWorkflow
  • addTenWorkflow
  • 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 catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • 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()

turso (1 failed):

  • webhookWorkflow

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 38 0 4
✅ example 38 0 4
✅ express 38 0 4
✅ fastify 38 0 4
✅ hono 38 0 4
✅ nextjs-turbopack 41 0 1
✅ nextjs-webpack 41 0 1
✅ nitro 38 0 4
✅ nuxt 38 0 4
✅ sveltekit 38 0 4
✅ vite 38 0 4
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 38 0 4
✅ express-stable 38 0 4
✅ fastify-stable 38 0 4
✅ hono-stable 38 0 4
✅ nextjs-turbopack-stable 42 0 0
✅ nextjs-webpack-stable 42 0 0
✅ nitro-stable 38 0 4
✅ nuxt-stable 38 0 4
✅ sveltekit-stable 38 0 4
✅ vite-stable 38 0 4
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 38 0 4
✅ express-stable 38 0 4
✅ fastify-stable 38 0 4
✅ hono-stable 38 0 4
✅ nextjs-turbopack-stable 42 0 0
✅ nextjs-webpack-stable 42 0 0
✅ nitro-stable 38 0 4
✅ nuxt-stable 38 0 4
✅ sveltekit-stable 38 0 4
✅ vite-stable 38 0 4
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 38 0 4
✅ express-stable 38 0 4
✅ fastify-stable 38 0 4
✅ hono-stable 38 0 4
✅ nextjs-turbopack-stable 42 0 0
✅ nextjs-webpack-stable 42 0 0
✅ nitro-stable 38 0 4
✅ nuxt-stable 38 0 4
✅ sveltekit-stable 38 0 4
✅ vite-stable 38 0 4
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 42 0 0
❌ 🌍 Community Worlds
App Passed Failed Skipped
✅ mongodb-dev 3 0 0
❌ mongodb 41 1 0
✅ redis-dev 3 0 0
❌ redis 41 1 0
✅ starter-dev 3 0 0
❌ starter 24 18 0
✅ turso-dev 3 0 0
❌ turso 41 1 0

📋 View full workflow run

Copy link
Member Author

TooTallNate commented Jan 10, 2026

Copy link
Contributor

@vercel vercel bot left a comment

Choose a reason for hiding this comment

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

Additional Suggestion:

Global class serialization registry lacks unregister/cleanup mechanism, causing potential test isolation issues and silent overwriting of class registrations

Fix on Vercel

@VaguelySerious
Copy link
Member

Will get to this by your morning tomorrow

Copy link
Member

@VaguelySerious VaguelySerious left a comment

Choose a reason for hiding this comment

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

LGTM to merge, though some issues we should fix in follow-ups afterwards:

1: O11y

O11y code will fail when inspecting a run/step with this custom serialization. In the web UI, in the trace viewer sidebar panel, you get:

image

The console shows Error: Failed to hydrate data, which comes from web-shared server-actions when calling hydrateResourceIO from core/src/observability.ts.

In the CLI, it's a bit better, where for wf i steps -r wrun_01KF32121K0XCTS4JSCJQ5503V --withData, I get this nicer error: Error] Error: Class "class//example/workflows/10_single_stmt_control_flow.ts//Point" not found. Make sure the class is registered with registerSerializationClass.

For both, it'd be nice if we could follow-up with a PR that fixes these.

2: Run failure not showing error state

This is an issue that already exists and isn't due to this PR, but is made a lot clearer in this PR: if I fail to provide the right symbols, then I get an error in the console of the runtime that looks pretty good (Failed to queue message: WorkflowRuntimeError: Failed to serialize step arguments. Ensure you're passing serializable types [... some more info that's helpful ...]), BUT the run doesn't actually fail, it just gets stuck in "running".
I imagine a lot of runs stuck in "running" with no steps are down to something like this, since it applies to many serialization issues.
CC @pranaygp

If the queue call fails, we should update the Run to be in error state generally.

Comment on lines +874 to +876
* Note: We use Symbol.for() directly instead of importing WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
* because the SWC plugin specifically looks for the `Symbol.for('workflow-serialize')` pattern
* in computed property names.
Copy link
Member

Choose a reason for hiding this comment

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

+1. I'm fine with the e2e test just covering one of the possible states, since we have compiler tests for the other two, but the comment needs updating

@VaguelySerious
Copy link
Member

@TooTallNate Sorry, I accidentally pushed to this branch. I reverted the pushed commit since I didn't want to force-push, just FYI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants