Skip to content
ChatfuelSDK
Concepts

Vendoring, not depending

The design system, the API client and the proxy arrive as source you own — the trade that makes, and the two rules that keep the copies working.

Nothing in your package.json points back at our repository. That is not an accident of packaging; it is the decision the rest of this page explains.

The design system, the API client and the proxy are copied into your project as source:

In this repositoryIn your appImported as
packages/ui/srcsrc/vendor/ui~ui
packages/api-client/srcsrc/vendor/api~api
packages/vite-plugin-proxy/srcvendor/chatfuel-proxyrelative paths

The proxy is copied recursively, with no file list, so a file added beside the others travels without anyone remembering to name it. Three marked import lines — in vite.config.ts, server/entry.ts and api/chatfuel.ts — are rewritten to point at the copy.

What source you own is good for

A component you disagree with is a file you can edit. There is no upstream package to fight, no !important to win an argument with, and no wrapper to write around a prop we did not expose.

The visual identity is CSS custom properties in vendor/ui/styles/tokens.css — colors, radii, shadows, durations, the type ramp — so a rebrand is a token edit that every component follows, rather than a search through class names. And deleting is as available as editing: a module you do not want is a directory you remove, and the import boundary keeps that deletion local.

The other half of the trade is that nothing we publish can reach you. There is no version of the design system that can be pushed under your app and change how it renders on a Tuesday.

Two rules keep the copies portable

A tree that is going to be moved onto somebody else's disk has to survive the move byte for byte. Two rules make that true, and both break quietly when you break them:

  1. Every internal import is relative. A path alias that resolves in this repository does not resolve after the copy lands somewhere else.
  2. ~ui may import nothing but react and react-dom. Those are the design system's only peer dependencies; anything else would be a package your app never agreed to install.

The API client's contract is the same shape with a different list: its source imports graphql, graphql-ws and @graphql-typed-document-node/core, so a scaffolded app declares exactly those three as dependencies. The proxy adds ws, undici and https-proxy-agent, and every relative import inside it carries an explicit .js extension because Vercel deploys those files as they are.

So "vendored" does not mean dependency-free. It means the dependency list is short, visible in your own package.json, and yours.

What does not travel

Our unit tests for the vendored packages stay behind. Shipping them would hand you failing vitest runs for code you did not write, and a dev dependency you never asked for.

Neither does the manifest tooling: module manifests are read while the wizard runs and have no presence in the app afterwards.

The cost, stated plainly

An upgrade is not npm update. It is a re-run of the wizard into a fresh directory and a diff you apply, or a patch you take by hand from the repository. A fix we make after your run does not reach you until you go and get it.

That is the price of the guarantee above, and it is the same price either way — you cannot have code that we cannot break and also code that we can fix.

For the tree this leaves on disk, see what the wizard writes.

On this page