Skip to content

@workflow/builders resolves workflow/internal/builtins without declaring workflow #3580

Description

@PropellerHead23

Packages: @workflow/builders@4.1.3, @workflow/next@4.1.2, workflow@4.6.2
Environment: npm 11.12.1, Node v24.14.1, Next.js ^16.2.11, no Yarn/PnP involved

Summary

@workflow/builders hardcodes the bare specifier workflow/internal/builtins and resolves it at
build time via enhanced-resolve, but declares workflow in neither dependencies nor
peerDependencies. @workflow/next, which depends on @workflow/builders, does not declare it
either. The dependency is therefore satisfied only by accident — when the consumer happens to have
installed the workflow meta-package itself and the package manager happens to hoist it.

Impact

A consumer who imports withWorkflow from @workflow/next (the documented Next.js entry point)
and does not also depend on workflow gets a build failure:

Failed to resolve built-in steps sources.
hint: run `npm install workflow` to resolve this issue.

Concretely, this makes it impossible to depend on @workflow/next alone. In our case the
meta-package pulls in the Astro, Nest, Nitro, Nuxt, Rollup, SvelteKit, CLI and TypeScript-plugin
subtrees, none of which we use — measured at 11 additional advisories (15 raw → 4 raw, 13 high →
2 high)
that we cannot shed. Dropping workflow breaks the build; keeping it means shipping and
auditing eight unused framework integrations.

Reproduce

npm init -y && npm i next @workflow/next
# next.config.ts
#   import { withWorkflow } from '@workflow/next'
#   export default withWorkflow({})
npx next build   # → Failed to resolve built-in steps sources
npm i workflow   # → now succeeds

Where

@workflow/builders@4.1.3/dist/base-builder.js:

// line 509
const builtInSteps = 'workflow/internal/builtins';
const resolvedBuiltInSteps = (await enhancedResolve(dirname(outfile), builtInSteps).catch((err) => {
    throw new WorkflowBuildError(`Failed to resolve built-in steps sources.\n\nCaused by: ${String(err)}`, {
        hint: 'run `pnpm install workflow` to resolve this issue.',
        cause: err,
    });
}));

Same specifier again at lines 614–615 (and referenced in the comment at line 67).

Suggested fix

Add workflow to peerDependencies of @workflow/builders (and surface it through
@workflow/next) so the requirement is declared rather than discovered at build time. A
peerDependenciesMeta.optional entry plus a graceful skip would work too if built-in steps are
meant to be optional. Either way the current in-hint instruction to npm install workflow is doing
a manifest's job.

Note for triage

This is not a duplicate of #33 — that was traced to stray Yarn PnP files in the reporter's home
directory. This reproduces on a clean npm install with no PnP present, and is about the published
manifests rather than a local resolver state.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions