Add discovered serializable classes in all context modes#874
Conversation
🦋 Changeset detectedLatest commit: 5894707 The changes in this PR will be included in the next version bump. This PR includes changesets to release 16 packages
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 |
🧪 E2E Test Results❌ Some tests failed Summary
❌ Failed Tests🌍 Community Worlds (165 failed)mongodb (41 failed):
redis (41 failed):
starter (42 failed):
turso (41 failed):
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
❌ 🌍 Community Worlds
✅ 📋 Other
|
This stack of pull requests is managed by Graphite. Learn more about stacking. |
80a9195 to
b0a69ea
Compare
2ad5621 to
ccc981c
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds automatic cross-context class registration for serializable classes, enabling seamless serialization/deserialization across client, workflow, and step execution boundaries. The build system now automatically discovers files containing custom serialization patterns and includes them in all bundle contexts without manual configuration.
Changes:
- Added discovery and tracking of serializable files in the esbuild plugin
- Modified base-builder.ts to include serde files in step, workflow, and client bundles for cross-context registration
- Added comprehensive test workflow and model classes (Vector) demonstrating cross-context serialization
- Updated documentation to explain the cross-context class registration feature
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/builders/src/discover-entries-esbuild-plugin.ts | Tracks discovered serde files separately instead of treating them as step files |
| packages/builders/src/base-builder.ts | Includes serde files in all three bundle contexts (step, workflow, client) with appropriate filtering |
| workbench/example/workflows/serde-models.ts | Defines Vector class with custom serialization for testing |
| workbench/example/workflows/serde-steps.ts | Implements step functions that use Vector for cross-context testing |
| workbench/example/workflows/99_e2e.ts | Adds crossContextSerdeWorkflow to test the feature |
| workbench/sveltekit/src/workflows/serde-*.ts | Reference files for serde models and steps (needs fixing) |
| workbench/nitro-v3/workflows/serde-*.ts | Reference files for serde models and steps (needs fixing) |
| workbench/nextjs-webpack/workflows/serde-*.ts | Reference files for serde models and steps (needs fixing) |
| workbench/nextjs-turbopack/workflows/serde-*.ts | Reference files for serde models and steps (needs fixing) |
| packages/core/e2e/e2e.test.ts | Adds comprehensive e2e test for cross-context serialization |
| packages/swc-plugin-workflow/spec.md | Documents the cross-context class registration feature |
| .changeset/khaki-breads-wave.md | Changelog entry for the feature |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1 @@ | |||
| ../../example/workflows/serde-models.ts No newline at end of file | |||
There was a problem hiding this comment.
This file appears to contain only a path string rather than valid TypeScript code. Based on the codebase convention (e.g., helpers.ts is duplicated across workbench directories), this should either be a proper TypeScript re-export statement like export * from '../../example/workflows/serde-models.js'; or a full copy of the serde-models.ts file from the example directory. A file containing just a path string will cause build/import errors.
| @@ -0,0 +1 @@ | |||
| ../../example/workflows/serde-models.ts No newline at end of file | |||
There was a problem hiding this comment.
This file appears to contain only a path string rather than valid TypeScript code. Based on the codebase convention (e.g., helpers.ts is duplicated across workbench directories), this should either be a proper TypeScript re-export statement like export * from '../../example/workflows/serde-models.js'; or a full copy of the serde-models.ts file from the example directory. A file containing just a path string will cause build/import errors.
| @@ -0,0 +1 @@ | |||
| ../../example/workflows/serde-steps.ts No newline at end of file | |||
There was a problem hiding this comment.
This file appears to contain only a path string rather than valid TypeScript code. Based on the codebase convention (e.g., helpers.ts is duplicated across workbench directories), this should either be a proper TypeScript re-export statement like export * from '../../example/workflows/serde-steps.js'; or a full copy of the serde-steps.ts file from the example directory. A file containing just a path string will cause build/import errors.
| @@ -0,0 +1 @@ | |||
| ../../example/workflows/serde-models.ts No newline at end of file | |||
There was a problem hiding this comment.
This file appears to contain only a path string rather than valid TypeScript code. Based on the codebase convention (e.g., helpers.ts is duplicated across workbench directories), this should either be a proper TypeScript re-export statement like export * from '../../example/workflows/serde-models.js'; or a full copy of the serde-models.ts file from the example directory. A file containing just a path string will cause build/import errors.
| const createImport = (file: string) => { | ||
| // Normalize both paths to forward slashes before calling relative() | ||
| // This is critical on Windows where relative() can produce unexpected results with mixed path formats | ||
| const normalizedWorkingDir = this.config.workingDir.replace(/\\/g, '/'); | ||
| const normalizedFile = file.replace(/\\/g, '/'); | ||
| // Calculate relative path from working directory to the file | ||
| let relativePath = relative(normalizedWorkingDir, normalizedFile).replace( | ||
| /\\/g, | ||
| '/' | ||
| ); | ||
| // Ensure relative paths start with ./ so esbuild resolves them correctly | ||
| if (!relativePath.startsWith('.')) { | ||
| relativePath = `./${relativePath}`; | ||
| } | ||
| return `import '${relativePath}';`; | ||
| }; |
There was a problem hiding this comment.
The createImport helper function is duplicated three times in this file (here at lines 320-335, at lines 503-518 for workflow bundle, and at lines 746-756 for client bundle). Consider extracting this into a private class method to improve maintainability and ensure consistency across all three bundle contexts. This would make the code DRY and easier to maintain if the path normalization logic needs to be updated in the future.
| @@ -0,0 +1 @@ | |||
| ../../../example/workflows/serde-steps.ts No newline at end of file | |||
There was a problem hiding this comment.
This file appears to contain only a path string rather than valid TypeScript code. Based on the codebase convention (e.g., helpers.ts is duplicated across workbench directories), this should either be a proper TypeScript re-export statement like export * from '../../../example/workflows/serde-steps.js'; or a full copy of the serde-steps.ts file from the example directory. A file containing just a path string will cause build/import errors.
| @@ -0,0 +1 @@ | |||
| ../../example/workflows/serde-steps.ts No newline at end of file | |||
There was a problem hiding this comment.
This file appears to contain only a path string rather than valid TypeScript code. Based on the codebase convention (e.g., helpers.ts is duplicated across workbench directories), this should either be a proper TypeScript re-export statement like export * from '../../example/workflows/serde-steps.js'; or a full copy of the serde-steps.ts file from the example directory. A file containing just a path string will cause build/import errors.
| @@ -0,0 +1 @@ | |||
| ../../example/workflows/serde-steps.ts No newline at end of file | |||
There was a problem hiding this comment.
This file appears to contain only a path string rather than valid TypeScript code. Based on the codebase convention (e.g., helpers.ts is duplicated across workbench directories), this should either be a proper TypeScript re-export statement like export * from '../../example/workflows/serde-steps.js'; or a full copy of the serde-steps.ts file from the example directory. A file containing just a path string will cause build/import errors.
| const entryContent = ` | ||
| // Built in steps | ||
| import '${builtInSteps}'; | ||
| // User steps | ||
| ${imports} | ||
| ${stepImports} | ||
| // Serde files for cross-context class registration | ||
| ${serdeImports} |
There was a problem hiding this comment.
The entry content generation here always includes the "Serde files for cross-context class registration" comment even when serdeImports is empty (when serdeOnlyFiles is empty). For consistency with the workflow bundle implementation (lines 528-530), consider using a conditional approach to only include the comment when there are actual serde imports.
| @@ -0,0 +1 @@ | |||
| ../../../example/workflows/serde-models.ts No newline at end of file | |||
There was a problem hiding this comment.
This file appears to contain only a path string rather than valid TypeScript code. Based on the codebase convention (e.g., helpers.ts is duplicated across workbench directories), this should either be a proper TypeScript re-export statement like export * from '../../../example/workflows/serde-models.js'; or a full copy of the serde-models.ts file from the example directory. A file containing just a path string will cause build/import errors.
Merge activity
|
All classes that are Workflow serializable need to be registered and available in all context modes (client, workflow, step).
📊 Benchmark Results
workflow with no steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express workflow with 1 step💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express workflow with 10 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express Promise.all with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express Promise.all with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express Promise.race with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express Promise.race with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express Stream Benchmarks (includes TTFB metrics)workflow with stream💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express SummaryFastest Framework by WorldWinner determined by most benchmark wins
Fastest World by FrameworkWinner determined by most benchmark wins
Column Definitions
Worlds:
|

This PR ensures that all classes with custom serialization are automatically included in all bundle contexts (step, workflow, client) to ensure proper serialization/deserialization when crossing execution boundaries:
Classes defined in any context can now be properly serialized when passing data between:
The build system now automatically discovers all files containing serializable classes and includes them in each bundle, regardless of where the class is originally defined.
No manual configuration is required - cross-registration happens automatically during the build process.