Scripts in the generated app
Every npm script the scaffolded app ships with — what it runs, when you reach for it, and what has to be true first.
Every command below is npm run <name> from the app root, except npm start and npm test,
which npm lets you type without the run.
The scripts
| Script | What it runs | When you reach for it | What has to be true first |
|---|---|---|---|
dev | vite | Day to day. Serves the app on port 5173 with the Chatfuel proxy plugin registered, so the token stays server-side. | Dependencies installed, and a .env with CHATFUEL_TOKEN. |
build | vite build && vite build -c vite.server.config.ts | Before start, and in a Docker image. Builds both halves. | The VITE_* values you build with must match the ones the server will run with. |
build:client | vite build | The browser bundle alone. This is also what Vercel runs. | Same. |
build:server | vite build -c vite.server.config.ts | The server bundle alone, into server/dist/entry.js. Targets node22, and leaves ws, undici and https-proxy-agent external. Plugin-free on purpose — it never touches the client. | — |
codegen | node scripts/codegen.mjs | After you edit a GraphQL document under src/vendor/api/operations/. | The generator toolchain. The first run prints the one command that installs it and stops. |
start | node server/dist/entry.js | Running the built app on your own host. Serves dist/ and proxies Chatfuel traffic, HTTP and WebSocket, with the token injected server-side. | build or build:server has run. Listens on PORT, default 3000, and on HOST when you set one. |
deploy | node scripts/deploy-vercel.mjs | Putting the app on Vercel, with no Git repository and no dashboard. | A .env that passes its checks. |
connect-git | node scripts/connect-git.mjs | Making git push deploy, once there is a repository. | A linked Vercel project — .vercel/project.json, written by deploy — and an origin remote. |
check | tsc --noEmit && tsc -p tsconfig.scripts.json | Before a commit. Two passes, because the app's tsconfig.json covers src, server, api and the two Vite configs and nothing else; the second pass is scripts/, which is JavaScript and is only type-checked because that config turns checkJs on. | — |
test | vitest run | Before a commit, and after any change to a module's root component. | — |
start also reads a .env sitting next to the app, so npm run build && npm start behaves like
npm run dev on a dev machine. Real environment variables win over the file, which is why the load
is safe to keep in a deployed image.
Vercel builds with build:client only. server/entry.ts is never built or run there — the
Vercel deployment answers Chatfuel traffic from api/chatfuel.ts instead, through the rewrite in
vercel.json, with a maxDuration of 300 seconds. A change you make to the Node server shows up
under npm start and in Docker, and nowhere on Vercel.
What codegen actually does
The generator is not a dependency of this app: it is 265 packages and 62 MB that nothing but this
command needs. So npm run codegen installs nothing. When the toolchain is absent it prints the one
command that adds it — with the versions already filled in, and using whichever package manager wrote
the lockfile it finds — and exits.
The versions are pinned rather than ranged because the plugins decide the shape of the generated code. A patch release that renames a generated type is not a break for graphql-codegen; it is a break for every file in the app that imports one.
npm install --save-dev --save-exact \
@graphql-codegen/cli@5.0.7 \
@graphql-codegen/typescript@4.1.6 \
@graphql-codegen/typescript-operations@4.6.1 \
@graphql-codegen/typed-document-node@5.1.2 \
@graphql-codegen/visitor-plugin-common@5.8.0 \
tsx@4.23.12Copy the line the run prints rather than this one — it is filled in for whichever package manager wrote your lockfile.
With the toolchain installed it runs graphql-codegen --config codegen.ts, then the fragment-hoisting
pass, then writes src/vendor/api/generated/.codegen-inputs.json — sha256 digests of the schema and
of each operations file, in the same encoding .chatfuel/lock.json uses. update refreshes the
schema and the documents but cannot regenerate the client, so it compares those digests against the
files on disk and tells you when the two have parted. The whole cycle is on the operations
page.
What deploy actually does
Re-running is the normal case, so every step is idempotent: an existing .vercel/project.json is
kept and the project is never re-created.
What test covers
Every module ships a render test that mounts its root component and renders the tree to a string. It runs without a browser, which is the point: nothing else in the app can catch a component that type-checks, passes every other gate and renders nothing.
The suite is the app's own. The design system and the API client are vendored as sources, but their tests are stripped on the way in — a scaffolded app should not hand you failing runs for code you did not write. Modules you did not select are deleted from the tree, so their tests go with them.
The environment the wizard reads and writes
Every variable the wizard itself reads, the .env and .env.example it leaves behind, the lock it writes beside them, what --embed appends instead of overwriting, and the guard that refuses to persist a token.
module.json
Every field of a module manifest — the required five, the two enums, the regex patterns, and what reads each one.