Skip to content

[WIP] fix(alchemy): make provider dependencies optional peers#312

Draft
aeterno-caspian wants to merge 2 commits into
alchemy-run:mainfrom
aeterno-caspian:provider-neutral-optional-peers
Draft

[WIP] fix(alchemy): make provider dependencies optional peers#312
aeterno-caspian wants to merge 2 commits into
alchemy-run:mainfrom
aeterno-caspian:provider-neutral-optional-peers

Conversation

@aeterno-caspian
Copy link
Copy Markdown
Contributor

@aeterno-caspian aeterno-caspian commented May 12, 2026

Overview

alchemy already exposes provider-specific entry points such as alchemy/AWS, alchemy/Cloudflare, alchemy/GitHub, alchemy/Axiom, alchemy/Neon, alchemy/Drizzle, and alchemy/SQLite.

This PR makes the package dependency model match that module surface. Provider and integration packages now live as optional peer dependencies, so consumers install the packages for the provider surfaces their project imports.

Behavior

A project that installs alchemy for core functionality now receives the core dependency graph.

A project that imports a provider subpath declares that provider package group explicitly. For example, a project importing alchemy/Cloudflare declares the Cloudflare resource/runtime packages, while a project importing alchemy/AWS declares the AWS package set.

This removes install-time side effects where consumers receive provider runtime graphs for integrations their project never imports.

CLI Loading

The alchemy aws ... and alchemy cloudflare ... command groups now lazy-load their provider implementation modules when those command groups run.

That keeps provider command availability in the CLI while preventing unrelated CLI commands from requiring AWS or Cloudflare provider packages at startup.

Provider module load failures now surface as a typed ProviderCommandLoadError carrying:

  • provider name
  • install command
  • original cause

Documentation And Examples

The README, getting-started guide, tutorial install steps, CLI guide, and runnable example manifests now declare provider packages beside the provider imports that require them.

This keeps copy-pasted examples aligned with the dependency model.

Verification

  • bun tsc -p packages/alchemy/tsconfig.json --noEmit
  • bun run --filter alchemy build
  • Checked example manifests so every alchemy/<provider> import has matching provider package declarations.
  • Installed a sanitized local package manifest in a temporary pnpm project and confirmed provider packages such as workerd, libsodium-wrappers, and @types/libsodium-wrappers do not resolve for a core-only consumer.

bun run --filter alchemy build still emits the existing externalized import warnings for alchemy/Cli and alchemy/Util/PlatformServices; the build exits successfully.

Move provider-specific packages out of the core dependency set so projects install the provider packages matching the alchemy subpaths they import. Lazy-load AWS and Cloudflare CLI provider modules so unrelated CLI commands do not require those optional peer packages at startup.
Represent lazy provider command module load failures with a tagged error that carries the provider, install command, and original cause.
@aeterno-caspian
Copy link
Copy Markdown
Contributor Author

This PR intentionally keeps the existing alchemy/<provider> import surfaces intact and only changes dependency ownership. Provider packages become optional peers so projects install the integrations they import, while existing code such as import * as Cloudflare from "alchemy/Cloudflare" continues to work after the matching provider packages are present.

A possible follow-up would be a guided provider install command such as alchemy add-provider cloudflare, or a larger package split into provider packages. Those changes would add new CLI behavior or new package surfaces, so this PR keeps them out of scope and focuses on reducing the current install graph side effects without changing public imports.

Copy link
Copy Markdown
Contributor

@Mkassabov Mkassabov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the usecase driving this change?

We thought about this early on and it was a very conscious de scion to not do this because we need providers to be able to reference each other (e.g. for cross cloud bindings). it would also force us to have another set of packages since distilled is just raw sdks not the providers themselves

@aeterno-caspian
Copy link
Copy Markdown
Contributor Author

aeterno-caspian commented May 12, 2026

I think we may be talking past each other on scope.

This PR does not split provider source packages, remove cross-provider references, or change the existing alchemy/<provider> import surfaces. The boundary it changes is dependency ownership: projects install the provider package groups for the provider surfaces they import.

The use case is dependency ownership and supply-chain scope. Alchemy runs in local and CI environments that often have deploy credentials, state access, and cloud permissions. If an AWS-only project installs the Cloudflare runtime graph by default, that unused graph still enters the lockfile, install policy, and trust boundary for infrastructure execution. In our case, this included workerd, a runtime we do not import or execute.

I agree cross-provider bindings are the important edge case. My expectation is that a binding spanning AWS + Cloudflare should live behind an import surface that requires both provider groups, and consumers of that surface install both. If there is a concrete module today that must be importable under partial provider installs but eagerly imports both provider graphs, that is the implementation problem I want to identify and fix.

The lazy CLI change applies the same idea at command startup: alchemy aws and alchemy cloudflare stay registered on the root CLI, but their provider-heavy modules are imported only when those subcommands run. That preserves the command surface while avoiding startup-time requirements for unrelated provider packages.

For non-CLI imports, the UX cost is real: missing optional peers need clear install guidance. This PR starts that in docs/examples and typed CLI load errors, but I am open to tightening the error surface for provider subpath imports if there is a preferred pattern.

So the question I’m trying to answer is narrower than “can providers reference each other?” They can. The question is whether an AWS-only consumer must install the Cloudflare runtime graph by default, or whether provider and cross-provider surfaces can own the dependency groups they actually require.

@sam-goodwin
Copy link
Copy Markdown
Contributor

We were chatting about this and think the right move is:

  1. remove as many dependencies as we can (there are some in there like ai that don't need to be)
  2. merge this optional peer dependency change
  3. update our guides/tutorial docs to include bun add commands that have the right peers
  4. provide useful error messages where possible when missing deps
    People are still likely to get errors when packages are missing. Coding agents can largely fix it, but it is going to be a noticeable DX friction point.

@aeterno-caspian
Copy link
Copy Markdown
Contributor Author

Noted. I will attempt to think of ways that could potentially mitigate as much DX friction as possible.

Switching this PR to WIP for the time being.

@aeterno-caspian aeterno-caspian marked this pull request as draft May 12, 2026 09:29
@aeterno-caspian aeterno-caspian changed the title fix(alchemy): make provider dependencies optional peers [WIP] fix(alchemy): make provider dependencies optional peers May 12, 2026
} from "../../versions";

export const corePkgs = `"alchemy@${alchemyVersion}" "effect@${effectVersion}" "@effect/platform-bun@${effectVersion}" "@effect/platform-node@${effectVersion}"`;
export const cloudflarePkgs = `"@cloudflare/workers-types@${cloudflareWorkersTypesVersion}" "@distilled.cloud/cloudflare@${distilledCloudflareVersion}" "@distilled.cloud/cloudflare-runtime@${distilledCloudflareRuntimeVersion}" "@distilled.cloud/cloudflare-vite-plugin@${distilledCloudflareVitePluginVersion}" "@distilled.cloud/cloudflare-rolldown-plugin@${distilledCloudflareRolldownPluginVersion}" "ignore@^7.0.5" "sonda@${sondaVersion}" "vite@${viteVersion}"`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is excessive, how can we reduce this?

@sam-goodwin
Copy link
Copy Markdown
Contributor

As part of #321 I am removing all of the vercel AI sdk dependencies and web-tree-sitter stuff.

@sam-goodwin
Copy link
Copy Markdown
Contributor

Vendoring in the ignore package (a package with >200million downloads and one simple .js file, a prime candidate for a supply chain attack):

#322

@sam-goodwin
Copy link
Copy Markdown
Contributor

An update here. We are vendoring in more packages here https://github.com/alchemy-run/node-utils. Partly for security, but also because they're ancient and written for CJS and use weird dependencies like graceful-fs. We are dropping those and vendoring in the rest.

@aeterno-caspian
Copy link
Copy Markdown
Contributor Author

Been a busy week. Apologize for the inactiveness.

Going to catch up with the latest and get some contributions going this weekend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants