* refactor(swc-plugin): remove client transform mode, merge into step mode
Remove the `client` transform mode from the SWC compiler plugin. The
`client` and `step` modes were nearly identical — both preserved step
function bodies, replaced workflow bodies with throw stubs, and emitted
the same JSON manifest. Step mode now absorbs all client-mode behaviors:
- Dead code elimination (previously only workflow + client)
- Hoisted variable references for object property steps
- All integrations use mode: 'step' instead of 'client'
BREAKING CHANGE: The `client` value for the SWC plugin `mode` option is
no longer accepted. Use `step` instead.
* fix(nitro): force-inline workflow packages in dev mode for serde classId registration
In dev mode, Nitro's Rollup externalizes npm packages like @workflow/core,
so the SWC transform plugin never processes files like run.js. This means
serde classes (e.g. Run) never get the classId registration IIFE, causing
serialization failures when step functions return Run instances.
Uses a Rollup resolveId hook to force workflow SDK packages to be bundled
(non-external) while leaving all other dependencies external. This is more
targeted than noExternals=true which bundles everything and causes TDZ
errors from circular imports in packages like vue-bundle-renderer/h3.
The Nitro module now also ignores .nitro/workflow/** in watchOptions so
writing generated workflow bundles does not retrigger Nitro's own dev
bundle rebuild loop.
Also wraps dev:reload workflow rebuilds and makes LocalBuilder.build()
atomic (writes to temp files, renames on success) to avoid partial output
state during HMR.
For Nuxt, also configures Vite's ssr.noExternal to bundle workflow
packages in the SSR context.
* fix(nitro,nuxt): address review feedback on dev-mode classId fix
- nitro builders: use crypto.randomUUID() for temp file suffix instead of
Date.now() to avoid collisions under rapid/concurrent build() calls,
and serialize concurrent build() calls through an internal queue so
two overlapping dev rebuilds cannot clobber each other's temp outputs.
- nitro index: use fileURLToPath() to convert file:// URLs to filesystem
paths, which correctly handles Windows paths (file:///C:/... -> C:\...)
and percent-decoding, instead of relying on new URL(...).pathname.
- nuxt module: normalize vite.ssr.noExternal to an array (preserving any
existing string/RegExp/array entry) before appending workflow package
matchers, so the force-bundle behavior is not a no-op when noExternal
is already set to a non-array value.
* Move SWC playground transform from server action to client-side WASM
Create a new swc-playground-wasm crate that bundles swc_ecma_parser,
swc_ecma_codegen, and the swc_workflow transform visitor into a single
WASM binary via wasm-bindgen/wasm-pack. This runs the code
transformation entirely in the browser, eliminating the server action
round-trip and serverless function cold starts on every keystroke.
- New packages/swc-playground-wasm Rust crate targeting wasm32-unknown-unknown
- Exposes transform() and transformAll() functions via wasm-bindgen
- Client loads WASM + JS glue from public/wasm/ as static assets
- Removed @swc/core and @workflow/swc-plugin server-side dependencies
- Removed serverExternalPackages/outputFileTracingIncludes Next.js config
- Added WASM loading indicator and error state in the UI
- Reduced transform debounce from 500ms to 300ms (no network latency)
* Add missing extends key to swc-playground-wasm turbo.json
* Fix build: ensure rustup is available for wasm32-unknown-unknown target
The Vercel build environment has a system Rust without rustup, so
the wasm32-unknown-unknown target can't be managed. Now the build
script checks for rustup specifically (not just cargo) and installs
it when missing, matching the pattern in swc-plugin-workflow/build.js.
* Add workflow type definitions to Monaco editor for intellisense
Auto-generate type declarations from built .d.ts files of workflow,
@workflow/core, @workflow/errors, @workflow/world, and @workflow/utils
packages. Register them with Monaco's TypeScript language service via
addExtraLib() so imports like 'workflow' resolve with full types,
eliminating red squiggles and enabling autocomplete/hover info.
* Fix Monaco type resolution: register root index.d.ts for each package
Monaco's NodeJs module resolution looks for index.d.ts at the package
root, not just in dist/. Register the main entry .d.ts content at both
paths (dist/ and root) so bare imports resolve with full type info.
Also remove the 2307 diagnostic suppression so non-existent imports
correctly show errors.
* Fix Monaco tooltip overflow by enabling fixedOverflowWidgets
* Fix hydration mismatch, Monaco type resolution paths, and WASM init warning
- Fix hydration mismatch: gate Reset button disabled state on isHydrated
so server and client render consistently during hydration
- Fix Monaco types: use bare node_modules/ paths instead of file:///
URIs for addExtraLib, which is what Monaco's NodeJs resolver expects
- Remove virtual package.json entries (Monaco doesn't use them)
- Fix wasm-bindgen init deprecation: pass object { module_or_path }
instead of bare string argument
* Fix Monaco module resolution: set model URI to file:/// and match addExtraLib paths
Monaco's TypeScript NodeJs resolver needs the editor model and the
addExtraLib entries to share the same URI scheme. Set the input editor
model path to file:///src/input.tsx and register all type declarations
at file:///node_modules/... so resolution of bare imports like
'workflow' correctly finds the virtual node_modules.
Also register a root index.d.ts for packages like @workflow/world that
lack an explicit 'types' field in their package.json exports.
* Use declare module ambient declarations for reliable Monaco type resolution
Replace the virtual node_modules filesystem approach with declare module
ambient declarations. This is the standard approach used by TypeScript
Playground and StackBlitz — it works regardless of Monaco's internal URI
scheme and module resolution quirks.
The generation script now:
- Registers all .d.ts files at file:///node_modules/<pkg>/dist/... paths
- Generates a global ambient declarations file with declare module blocks
that map bare import specifiers to their .d.ts entry points
- Includes @workflow/serde and workflow sub-exports (api, errors, observability)
- Supports configurable sub-export mappings per package
* Inline types into declare module blocks for full Monaco type support
Replace the export-from-file approach with fully inlined declare module
blocks. The script now reads each .d.ts entry point, recursively inlines
all relative imports, strips external import statements (resolved via
other declare module blocks), and produces a single ambient declarations
string.
This correctly handles:
- unique symbol exports (@workflow/serde)
- cross-package re-exports (workflow re-exporting from @workflow/core)
- JSDoc comments preserved for hover documentation
- Sub-path exports (workflow/api, workflow/errors, workflow/observability)
- Added @workflow/serde package
* Add workspace packages as dependencies so Turbo builds their types
The generate-monaco-types script reads .d.ts files from the built
dist/ directories of workflow, @workflow/core, @workflow/errors, etc.
On Vercel, Turbo only builds explicit dependencies — without these
workspace references, the packages were never built and the dist/
directories didn't exist, resulting in 0 type modules generated.
* Add @types/node declarations to Monaco editor
Register all @types/node .d.ts files via addExtraLib so Node.js
built-in modules (crypto, fs, path, etc.) are available in the
playground editor with full type information.
* Collect @types/node .d.ts files recursively to include subpath modules
The previous non-recursive scan missed subdirectory files like
fs/promises.d.ts, stream/web.d.ts, dns/promises.d.ts, etc., causing
'Cannot find module node:fs/promises' errors.