Skip to content

Node workflow build fails on dynamic require of Node built-ins #2

@mihaipxm

Description

@mihaipxm

Problem

voyant workflows build --platform node can produce a bundle successfully, then fail while loading that bundle to emit the workflow manifest.

Observed deployment log:

Detected workflow entry src/workflows.ts
Workflow manifest generated with 6 workflow(s)
Building node workflow bundle
loading bundle failed: Dynamic require of "stream" is not supported
Command failed with exit code 1: pnpm exec voyant workflows build --file 'src/workflows.ts' --out '.voyant/build/node' --platform node --no-sourcemap

Expected behavior: a node-platform workflow bundle that depends on packages using Node built-ins should be loadable by the CLI for manifest extraction, and should produce bundle.mjs plus manifest.json without requiring app developers to debug bundler internals.

Reproduction shape:

  1. Create a workflow entry with defaultRuntime: "node".
  2. Import code that pulls in a CommonJS dependency path that dynamically requires a Node built-in such as stream.
  3. Run voyant workflows build --platform node.
  4. The generated ESM bundle can contain a dynamic CommonJS require helper, and the CLI load step fails because the bundle is imported as ESM without an ambient CommonJS require.

Root Cause Analysis

The node workflow build currently uses esbuild to produce a single ESM bundle, then immediately imports that bundle in the CLI process to discover registered workflows and emit the manifest.

When a dependency in the workflow graph is CommonJS and dynamically requires a Node built-in, esbuild can preserve that access through a dynamic require helper. Because the generated artifact is ESM, Node does not provide require at import time, so bundle loading fails even though the target runtime is Node and the dependency is valid in Node.

A tempting fix is to inject createRequire(import.meta.url) into the generated bundle, but that should be considered carefully: platform runners may import workflow bundles from non-file module URLs. The generated node bundle contract should be explicit about whether it requires a file URL loader, or should avoid ambient CommonJS require in the artifact altogether.

TDD Fix Plan

  1. RED: Add a workflow build test where a node-runtime workflow imports a dependency fixture that dynamically requires a Node built-in such as stream. Assert that voyant workflows build --platform node completes and writes both bundle and manifest.
    GREEN: Adjust the node bundle/load strategy so the generated node bundle can be imported successfully during CLI manifest extraction.

  2. RED: Add a test that imports the generated node bundle through the same supported loader contract used by the runtime and verifies workflows register successfully.
    GREEN: Make the runtime loader contract explicit and compatible with the generated node bundle, either by avoiding dynamic require in the bundle or by requiring/importing the bundle through a file-backed Node loader that can support CommonJS built-ins.

  3. RED: Add a regression test ensuring browser/neutral workflow builds do not accidentally gain Node-only require behavior.
    GREEN: Scope any CommonJS/Node built-in handling to node-platform builds only.

REFACTOR: Document the node bundle import contract so the CLI and managed node runner use the same assumptions.

Acceptance Criteria

  • voyant workflows build --platform node succeeds for workflow graphs that include valid Node dependencies using dynamic require of Node built-ins.
  • The generated manifest is still emitted from the registered workflows.
  • The generated node bundle can be loaded by the documented runtime loader contract.
  • Neutral/browser builds do not gain Node-only behavior.
  • Regression tests cover the dynamic stream require case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions