Commit Graph

1245 Commits

Author SHA1 Message Date
Nathan Rajlich 0a86de3afd Inline all SWC plugin step registrations, remove workflow/internal/private (#1632)
The SWC compiler plugin no longer generates import statements. All step
function registrations and closure variable access are now self-contained
inline IIFEs with zero module dependencies
2026-04-08 16:58:17 -07:00
Peter Wielander e4362421ab [builders] Switch Vercel Build Output API from CJS to ESM (#1562) 2026-04-08 16:37:37 -07:00
Peter Wielander a5c90cefba [core] [world] Fix community world E2E tests broken by specVersion bump (#1658) 2026-04-08 13:14:59 -07:00
Nathan Rajlich c6b630fc07 fix: workflow/next ESM compatibility for .mjs config files (#1653)
Use named export instead of `export =` in the CJS shim so that
Node.js cjs-module-lexer can detect withWorkflow as a named export,
enabling `import { withWorkflow } from 'workflow/next'` in ESM.
2026-04-08 09:55:58 -07:00
workflow-devkit-release-bot[bot] a261b2128b Version Packages (beta) (#1635) 2026-04-07 17:37:59 -07:00
Nathan Rajlich 35b539b146 Add detect mode to SWC plugin for false positive directive filtering (#1641)
* Add detect mode to SWC plugin for false positive directive filtering

Add a new 'detect' mode to the SWC workflow plugin that walks the AST
to find directives and serde patterns and emits the manifest, but does
not transform any code. The discover-entries plugin now uses a two-phase
approach: fast regexp pre-scan to filter out most files, then SWC detect
mode on candidates to validate at the AST level. This eliminates false
positives where directive-like strings appear inside template literals
or other non-code contexts. The mode:false syntax-only transform is also
removed since esbuild handles TypeScript natively.

* Keep SWC syntax transform in discover phase for decorator support

esbuild does not support legacy decorators or emitDecoratorMetadata,
so all files still need the SWC syntax transform (TS→JS) during
discovery. For regexp-matched files the 'detect' mode handles both
the syntax transform and manifest in a single pass; for all other
files the existing mode:false call is used.

* Use Set for discoveredWorkflows/Steps/SerdeFiles

Eliminates the manual .includes() dedup check and prevents duplicate
entries structurally.

* Update DeferredDiscoveredEntries to use Set<string>

* Add @workflow/next to changeset
2026-04-07 16:53:55 -07:00
Nathan Rajlich bab8cddf98 Support getter functions with "use step" directive (#1630)
* Support getter functions with "use step" directive

Add SWC compiler plugin support for JavaScript getters marked with
"use step", enabling patterns like `await obj.prop` where the getter
triggers a step function invocation.

- Handle Prop::Getter in object literals and MethodKind::Getter in classes
- Emit Object.getOwnPropertyDescriptor registration in step mode
- Emit hoisted proxy + Object.defineProperty in workflow mode
- Emit error for getters with "use workflow"
- Fix @vercel/workflow -> @workflow/serde imports in existing fixtures
- Update spec.md with getter transformation documentation

* Add changeset for getter step support

* Add e2e test for getter step functions

* Add static getter support, sanitize hoisted var identifiers

Address PR review feedback:
- Support static getters with "use step" using ClassName (not .prototype)
- Add sanitize_ident_part() to produce valid JS identifiers from getter
  names that may contain special characters (e.g. string literal keys)
- Add static-getter-step test fixture
- Update spec.md with static getter transformation documentation

* Remove duplicate getter workflow error in visit_mut_prop_or_spread
2026-04-07 23:35:55 +00:00
Nathan Rajlich 44a18048a5 Reset package versions to 4.0.0 so changesets produces 5.0.0-beta.0 (#1648)
The previous pre-release versions (4.x.y-beta.N) caused two issues:
- semver.inc('4.0.0-beta.N', 'major') returns 4.0.0, not 5.0.0
- Pre-release numbers carried over (beta.61 -> beta.62 instead of beta.0)

Setting all versions to 4.0.0 (non-pre-release) ensures a clean major
bump to 5.0.0-beta.0. Also removes @workflow/swc-playground-wasm from
the changeset and pre.json since it is a private package.
2026-04-07 14:45:40 -07:00
Nathan Rajlich 372abba55f [builders] Fix Node.js builtin imports being relativized in step bundles (#1644) 2026-04-07 13:35:42 -07:00
Peter Wielander fa0d8a73ce [release] Update v5 changeset to major (#1643) 2026-04-07 13:21:05 -07:00
Peter Wielander c5cdfc0075 [release] Wipe changesets and reset pre.json for v5 release (#1642) 2026-04-07 13:17:00 -07:00
Peter Wielander 903f26dc45 Revert "Revert "Support dual-branch releases and bump to v5 beta (#1622)" (#1624)" (#1634) 2026-04-07 11:27:44 -07:00
workflow-devkit-release-bot[bot] 61831cf91e Version Packages (beta) (#1615) 2026-04-07 11:17:00 -07:00
Peter Wielander a6bcea9d28 [cli] [core] Probe deployment specVersion before CLI start (#1629) 2026-04-07 11:03:33 -07:00
Peter Wielander c9b30381f4 [core] Propagate stream cancellation to avoid leaking listeners (#1618) 2026-04-07 11:02:38 -07:00
Peter Wielander 70e89bfc96 [ai] Fixes DurableAgent telemetry missing AI SDK-compatible span attributes (#1608) 2026-04-07 11:00:17 -07:00
John Lindquist 5aab85b81b fix(next): remove unused dataDir option from withWorkflow() (#1619)
The `dataDir` option was accepted in the type definition but never
read — `WORKFLOW_LOCAL_DATA_DIR` was unconditionally set to
`.next/workflow-data`. Remove the dead option to avoid confusion.
2026-04-07 10:51:47 -07:00
Peter Wielander 5f138f2cee [core] [world] Gate CBOR queue transport on specVersion (#1627) 2026-04-07 09:36:51 -07:00
Matan Kushner 3308701b34 [builders] Resolve path aliases when externalizing non-step imports (#1613) 2026-04-06 17:01:24 -07:00
Peter Wielander 5b9eb406a8 [world-vercel] Paginate writeToStreamMulti (#1626) 2026-04-06 16:11:38 -07:00
Peter Wielander b7c357e33e Revert "Support dual-branch releases and bump to v5 beta (#1622)" (#1624) 2026-04-06 14:56:41 -07:00
Peter Wielander 38ee590b98 Support dual-branch releases and bump to v5 beta (#1622) 2026-04-06 14:22:29 -07:00
Peter Wielander 7e70d1823a [core] Add configurable stream flush interval per world (#1533) 2026-04-06 12:38:01 -07:00
Peter Wielander c8dce52606 [core] [world] Lazy run creation on start (#1537) 2026-04-06 12:25:23 -07:00
Nathan Rajlich 2680a427f0 fix: add Request and Response revivers to web and CLI hydration (#1414) 2026-04-06 06:30:36 +00:00
Nathan Rajlich ba916e1566 Add DOMException to VM context and first-class serialization support (#1512)
- Pass DOMException through to the workflow VM context alongside other
  Web APIs (Headers, URL, TextEncoder, etc.)
- Add DOMException reducer/reviver to the serialization pipeline,
  preserving message, name, derived code, and cause when present
- Add DOMException to the Serializable type union
- 8 new tests: round-trip for AbortError, NotFoundError, default name,
  cause preservation, cause absence, serialization key, cross-VM
  boundaries, and VM context availability
2026-04-06 06:10:30 +00:00
workflow-devkit-release-bot[bot] 29fe9ded57 Version Packages (beta) (#1600) 2026-04-03 17:05:54 -07:00
Peter Wielander ab872cc9fb [core] Make registeredSteps a global singleton to protect against module duplication (#1606) 2026-04-03 21:53:58 +00:00
Gregor Martynus e9ebe8faa3 [ai] Preserve reasoning content in DurableAgent conversation history (#1444)
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-04-03 14:42:10 -07:00
Nathan Rajlich f5d2aef58f Add serde compliance tooling and improve custom class serialization DX (#1552)
* Add serde compliance tooling and update skill documentation

- Add serde compliance checker library to @workflow/builders
- Add build-time warnings for serde classes with Node.js imports in workflow bundle
- Add 'workflow transform' CLI command for inspecting SWC output
- Implement 'workflow validate' CLI command with serde compliance checks
- Add serde analysis panel to SWC playground
- Update workflow skill with custom class serialization documentation

* Fix --json + --strict: use process.exitCode so JSON output is returned before exit

* Address code review feedback

- Fix --strict exit code: honor process.exitCode in BaseCommand.finally()
- Remove unused --module-specifier flag from transform command
- Add missing Node.js builtins (e.g. test) to playground detection list
- De-dupe build-time serde warnings by grouping identical issues across classes
- Make Serde Analysis panel collapsible like the output panels

* Use .gitignore for validate file discovery; fix changeset wording
2026-04-03 21:03:59 +00:00
Peter Wielander d8aaf27c79 [core] Enforce single ALS context to protect against duplicate module and caching issues (#1591) 2026-04-03 13:47:19 -07:00
Nathan Rajlich 7c996a76c5 fix(swc-plugin): rewrite anonymous export default class to const declaration (#1601)
* fix(builders): override sideEffects:false for discovered workflow/step/serde entries

When node_modules packages include "sideEffects": false in their
package.json, esbuild drops bare imports from the virtual-entry.js
file. This is incorrect because the SWC compiler transform injects
side-effectful registration code (workflow IDs, step IDs, class
serialization) into these modules.

Fix: return the resolved path alongside sideEffects: true from the
onResolve handler so esbuild uses the plugin's resolution result
instead of re-reading the package.json.

* refactor(builders): normalize sideEffectEntries with realpaths for symlink compatibility

Extract withRealpaths() helper and use it for both normalizedEntriesToBundle
and sideEffectEntries at all three bundle sites. This ensures the
sideEffects override works correctly under pnpm/workspace symlinked
layouts where enhanced-resolve may return realpaths that differ from
the original discovered file paths.

* perf(builders): skip enhanced-resolve for transitive imports when only sideEffectEntries is set

When entriesToBundle is not set (workflow/client bundles), only top-level
import statements need the sideEffects override — transitive imports
from deep within the bundle are not bare imports and don't need resolution.
Skip enhanced-resolve for non-import-statement kinds to reduce overhead.

* fix(swc-plugin): use binding name for class expression method registrations

When a pre-bundled package (e.g. via tsup) contains class expressions
like `var Foo = class _Foo { ... }`, the internal name `_Foo` is only
scoped inside the class body. The SWC plugin was incorrectly using the
internal name for method step registrations and class serialization
registrations emitted at module scope, causing ReferenceError at runtime.

Fix: always use the binding name (registration_name) for
current_class_name in visit_mut_class_expr, consistent with the existing
handling for anonymous class expressions. This ensures:
- registerStepFunction calls reference the binding name (Foo)
- Only one class registration IIFE is emitted (not duplicates for both
  Foo and _Foo)
- Step IDs use the binding name in their qualified path

* refactor(swc-plugin): rename internal_class_name to tracked_class_name for clarity

The variable no longer represents the internal class expression identifier
after being reassigned to the binding name. Rename to tracked_class_name
and eliminate the intermediate registration_name variable to make the
intent clearer and reduce confusion for future readers.

* fix(swc-plugin): rewrite anonymous export default class to const declaration

When an anonymous class with serde/step methods is exported as a default
export (`export default class { ... }`), the generated registration code
(registerStepFunction, class registry IIFE) would reference a nonexistent
variable at module scope, causing a ReferenceError at runtime.

Fix: detect anonymous default class exports in visit_mut_export_default_decl
and visit_mut_export_default_expr, generate a unique binding name
(__defaultClass), and defer a rewrite in visit_mut_module_items that
transforms the export into:
  const __defaultClass = class __defaultClass { ... };
  export default __defaultClass;

Named default class exports (export default class Foo { ... }) are
handled by setting current_class_binding_name so the transformer
uses the existing class name for registration code.

* refactor(swc-plugin): rename __defaultClass to __DefaultClass for class naming convention

* fix(swc-plugin): fix panic for step-only anonymous default class exports

Address review feedback:
- Remove expect() that panicked when anonymous default class had step
  methods but no serde methods (ident was not re-inserted). Keep
  const_name in a local variable instead of relying on class_expr.ident.
- Add debug_assert for single default class export invariant.
- Update spec.md and test fixture inputs to use new this() instead of
  referencing the generated binding name.
- Add step-only anonymous default class fixture to cover the bug path.

* refactor(swc-plugin): address review feedback for export default class handling

- Remove dead Expr::Class handler in visit_mut_export_default_expr
  (SWC wraps parenthesized form in Expr::Paren, so it never fires)
- Extract class_needs_binding_rewrite() helper, eliminating duplicated
  detection logic and unnecessary body clones
- Add debug_assert for mutual exclusivity of default_workflow_exports
  and default_class_exports
- Clarify spec.md on self-name behavior difference between serde and
  step-only classes
2026-04-03 19:54:20 +00:00
Peter Wielander b30b0dcab6 [world] Asymmetric ULID timestamp validation thresholds (#1605) 2026-04-03 12:54:09 -07:00
Pranay Prakash d1330cfebc Fix node-module-error plugin matching identifiers in multi-line comments (#1554)
* Fix node-module-error plugin matching identifiers in multi-line comments

The findIdentifierUsage function only stripped single-line comments and
same-line block comments, but didn't track multi-line block comment state.
Lines inside JSDoc/block comments (e.g. ` * ... Writable stream`) passed
through unstripped, causing the plugin to point at comments instead of
actual code usage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Strip string literals before scanning for comment delimiters

Move string stripping before comment detection so that comment delimiters
inside string literals (e.g. `const s = "/*"`) don't incorrectly trigger
block comment mode. Adds regression test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-04-03 11:55:11 -07:00
Nathan Rajlich 5d22e61446 fix(swc-plugin): use binding name for class expression method registrations (#1599)
* fix(builders): override sideEffects:false for discovered workflow/step/serde entries

When node_modules packages include "sideEffects": false in their
package.json, esbuild drops bare imports from the virtual-entry.js
file. This is incorrect because the SWC compiler transform injects
side-effectful registration code (workflow IDs, step IDs, class
serialization) into these modules.

Fix: return the resolved path alongside sideEffects: true from the
onResolve handler so esbuild uses the plugin's resolution result
instead of re-reading the package.json.

* refactor(builders): normalize sideEffectEntries with realpaths for symlink compatibility

Extract withRealpaths() helper and use it for both normalizedEntriesToBundle
and sideEffectEntries at all three bundle sites. This ensures the
sideEffects override works correctly under pnpm/workspace symlinked
layouts where enhanced-resolve may return realpaths that differ from
the original discovered file paths.

* perf(builders): skip enhanced-resolve for transitive imports when only sideEffectEntries is set

When entriesToBundle is not set (workflow/client bundles), only top-level
import statements need the sideEffects override — transitive imports
from deep within the bundle are not bare imports and don't need resolution.
Skip enhanced-resolve for non-import-statement kinds to reduce overhead.

* fix(swc-plugin): use binding name for class expression method registrations

When a pre-bundled package (e.g. via tsup) contains class expressions
like `var Foo = class _Foo { ... }`, the internal name `_Foo` is only
scoped inside the class body. The SWC plugin was incorrectly using the
internal name for method step registrations and class serialization
registrations emitted at module scope, causing ReferenceError at runtime.

Fix: always use the binding name (registration_name) for
current_class_name in visit_mut_class_expr, consistent with the existing
handling for anonymous class expressions. This ensures:
- registerStepFunction calls reference the binding name (Foo)
- Only one class registration IIFE is emitted (not duplicates for both
  Foo and _Foo)
- Step IDs use the binding name in their qualified path

* refactor(swc-plugin): rename internal_class_name to tracked_class_name for clarity

The variable no longer represents the internal class expression identifier
after being reassigned to the binding name. Rename to tracked_class_name
and eliminate the intermediate registration_name variable to make the
intent clearer and reduce confusion for future readers.
2026-04-03 18:36:27 +00:00
Nathan Rajlich 443a9e62f9 fix(builders): override sideEffects: false for discovered workflow/step/serde entries (#1598)
* fix(builders): override sideEffects:false for discovered workflow/step/serde entries

When node_modules packages include "sideEffects": false in their
package.json, esbuild drops bare imports from the virtual-entry.js
file. This is incorrect because the SWC compiler transform injects
side-effectful registration code (workflow IDs, step IDs, class
serialization) into these modules.

Fix: return the resolved path alongside sideEffects: true from the
onResolve handler so esbuild uses the plugin's resolution result
instead of re-reading the package.json.

* refactor(builders): normalize sideEffectEntries with realpaths for symlink compatibility

Extract withRealpaths() helper and use it for both normalizedEntriesToBundle
and sideEffectEntries at all three bundle sites. This ensures the
sideEffects override works correctly under pnpm/workspace symlinked
layouts where enhanced-resolve may return realpaths that differ from
the original discovered file paths.

* perf(builders): skip enhanced-resolve for transitive imports when only sideEffectEntries is set

When entriesToBundle is not set (workflow/client bundles), only top-level
import statements need the sideEffects override — transitive imports
from deep within the bundle are not bare imports and don't need resolution.
Skip enhanced-resolve for non-import-statement kinds to reduce overhead.
2026-04-03 18:11:37 +00:00
Karthik Kalyan 760ebf161b [world-vercel] align header names to x-vercel-workflow-* convention (#1602)
* update x-vercel-workflow-run-id and x-vercel-workflow-step-id

* Update dark-olives-fix.md

Signed-off-by: Karthik Kalyan <105607645+karthikscale3@users.noreply.github.com>

---------

Signed-off-by: Karthik Kalyan <105607645+karthikscale3@users.noreply.github.com>
2026-04-03 10:35:22 -07:00
Pranay Prakash 047c01bc15 Make start() types unknown when deploymentId is provided (#1367)
* fix: update types and documentation for start function overloads

Ensure types are 'unknown[]' and 'unknown' for 'deploymentId' and update exports and documentation.

Slack-Thread: https://vercel.slack.com/archives/C09G3EQAL84/p1773368990070059?thread_ts=1773368990.070059&cid=C09G3EQAL84
Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com>

* fix: use generics in deploymentId overloads to avoid contravariance issue

Addresses PR review feedback: typed workflows like
WorkflowFunction<[string], number> were not assignable to
WorkflowFunction<unknown[], unknown> under strictFunctionTypes.
Changed to generic parameters while keeping Run<unknown> return type.
Also adds type-level tests for overload resolution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: add changeset for start() deploymentId type changes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: v0 <v0[bot]@users.noreply.github.com>
Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 19:52:47 -07:00
Nathan Rajlich 44290785e1 fix(next): stop force-setting WORKFLOW_PUBLIC_MANIFEST=1 during next dev (#1597) 2026-04-02 22:49:14 +00:00
workflow-devkit-release-bot[bot] 45fd831b1d Version Packages (beta) (#1593) 2026-04-02 21:25:14 +00:00
Nathan Rajlich 7e33c62736 Rename 'Workflow Development Kit' / 'DevKit' to 'Workflow SDK' (#1595)
* Rename 'Workflow Development Kit' / 'DevKit' to 'Workflow SDK' across docs, code, and config

Follow-up to cdf90d5a38 (#1541)

* Fix missing </h1> closing tag and add article 'the' before 'Workflow SDK' in docs
2026-04-02 19:39:02 +00:00
Peter Wielander ef2218ab22 [world] Use zod/v4 in queue files to match @workflow/world schemas (#1588) 2026-04-02 12:38:13 -07:00
Peter Wielander c0f07a985c [ai] Fix fatal stream errors surfacing as [object Object] (#1589) 2026-04-02 12:37:15 -07:00
Peter Wielander 74c4cdb651 [web] Fix server crash on unmatched routes (#1590) 2026-04-02 10:57:41 -07:00
workflow-devkit-release-bot[bot] 8462c5df89 Version Packages (beta) (#1563) 2026-04-01 16:08:49 -07:00
Peter Wielander 6dc1b78582 [core] Extend flow route duration to "max" and fail runs where replay takes too long (#1567) 2026-04-01 21:33:39 +00:00
Nathan Rajlich d38114bff1 fix: check target run capabilities before encrypting hook payloads (#1572)
* fix: check target run capabilities before encrypting hook payloads

When resumeHook()/resumeWebhook() is called on a newer deployment that
supports encryption, it would encode the payload with the 'encr' format.
If the target workflow run was created by an older deployment that
predates encryption support, the run would fail with:
  Error: Unknown serialization format: "encr". Known formats: devl

Add a capabilities table that maps @workflow/core versions to supported
serialization formats. Before encoding, resumeHook() now checks the
target run's workflowCoreVersion and suppresses encryption when the
run's deployment doesn't support it.

* address review: guard against invalid/non-string workflowCoreVersion

- Validate with semver.valid() before comparing, falling back to
  baseline formats for malformed version strings
- Add typeof guard at the call site in resumeHook() since
  executionContext is Record<string, any>
- Add tests for invalid version strings (dev, empty, partial, etc.)
- Add encryption commit reference to capabilities module header
2026-04-01 21:32:51 +00:00
Peter Wielander a98f8de53f [core] Combine initial run fetch, event fetch, and run_started event creation (#1569) 2026-04-01 14:23:29 -07:00
comfuture 0e8a880b6b [nitro] Preserve workflow step registration side effects (#1386)
In Nitro/Nuxt local production builds, the generated workflow/steps.mjs bundle could be treated as a pure export provider when the virtual handler only imports { POST }. That allowed the step bundle's top-level registerStepFunction(...) calls to disappear from the final Nitro server bundle, which led to runtime failures like Step \"...\" not found when a queued step executes.

Signed-off-by: comfuture <comfuture@gmail.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-04-01 09:28:47 -07:00
Peter Wielander 329cdb3e1b [world] Re-enqueue active runs on world restart (#1534) 2026-03-30 13:48:09 -07:00