create-basalt 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Machize Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,256 @@
1
+ # create-basalt
2
+
3
+ Basalt project generator: a single command (`npm create basalt my-app`) creates a complete, ready-to-run SaaS application — typed API, authentication, multi-tenancy, and optionally billing, a web frontend, and the `basalt` CLI. It's the framework's starting point: use it whenever you want to start a new project.
4
+
5
+ ## What this module solves
6
+
7
+ Starting a backend project from scratch involves dozens of decisions and files before you write the first useful line: setting up TypeScript, choosing the HTTP server, organizing folders, wiring up authentication, preparing tests… A **scaffolder** (project generator) does that work for you: it generates the initial structure with good practices already applied, so you can start building your product right away.
8
+
9
+ `create-basalt` generates a **SaaS** application (Software as a Service — software sold by subscription, usually with several customers/organizations in the same installation) shaped the way mature Basalt projects look in production: typed routes with Zod validation, structured logging, domain events, and, depending on the options, **multi-tenancy** (several isolated customers in the same application), authentication (register/login/refresh), subscriptions with plans, a React frontend, and the `basalt` command-line tool with code generators.
10
+
11
+ It works in two ways: interactive mode (answers questions in the terminal) or direct mode with flags (ideal for scripts). It doesn't install dependencies or touch git unless you ask it to (`--install`, `--git`).
12
+
13
+ ## Installation
14
+
15
+ You don't need to install anything — your package manager's `create` command downloads and runs the package on the spot:
16
+
17
+ ```bash
18
+ npm create basalt my-app
19
+ # or
20
+ pnpm create basalt my-app
21
+ # or
22
+ yarn create basalt my-app
23
+ # or
24
+ bun create basalt my-app
25
+ ```
26
+
27
+ > Requirements: Node.js 18+ and a package manager. Projects with `--ui` require **pnpm** (explained below).
28
+
29
+ ## Get started in 5 minutes
30
+
31
+ 1. Create the project (interactive mode — run without a name and answer the questions):
32
+
33
+ ```bash
34
+ npm create basalt
35
+ ```
36
+
37
+ ```
38
+ Project name: my-app
39
+ Multi-tenancy? (Y/n) y
40
+ Authentication? (Y/n) y
41
+ Subscriptions / billing? (y/N) n
42
+ Web UI (React + shadcn)? (y/N) n
43
+ 'basalt' CLI (code generators)? (y/N) n
44
+ Install dependencies now? (y/N) y
45
+ Initialize a git repository? (y/N) y
46
+ ```
47
+
48
+ 2. Go into the folder and start it up:
49
+
50
+ ```bash
51
+ cd my-app
52
+ npm install # if you didn't choose to install in the previous step
53
+ npm run dev # API at http://localhost:3000
54
+ ```
55
+
56
+ 3. Try it out:
57
+
58
+ ```bash
59
+ curl http://localhost:3000/ # index listing the available endpoints
60
+ curl http://localhost:3000/health # { ok: true, requestId: ..., tenant: null }
61
+ ```
62
+
63
+ 4. Run the included tests:
64
+
65
+ ```bash
66
+ npm test
67
+ ```
68
+
69
+ ## Usage guide
70
+
71
+ ### All CLI flags
72
+
73
+ ```
74
+ Usage: npm create basalt <name> [options]
75
+ ```
76
+
77
+ | Flag | Default | What it does |
78
+ | --- | --- | --- |
79
+ | `<name>` | — (asked in interactive mode) | Project name (and folder name, unless `--dir`) |
80
+ | `--dir=<path>` | `./<name>` | Destination folder |
81
+ | `--no-tenancy` | tenancy **on** | Removes multi-tenancy (`@basaltkit/tenancy`) |
82
+ | `--no-auth` | auth **on** | Removes authentication (`@basaltkit/auth`, `APP_SECRET`, `/auth/*` routes) |
83
+ | `--billing` | off | Includes subscriptions/plans (`@basaltkit/subscriptions`, example `free` and `pro` plans) |
84
+ | `--ui` | off | Generates the `web/` frontend (React + shadcn via `@basaltkit/admin-shadcn` + `@basaltkit/sdk`). **Forces pnpm** — see note below |
85
+ | `--cli` | off | Generates the `basalt` CLI (`bin/basalt.ts`, `pnpm basalt` script, `make:*` generators from `@basaltkit/generator`) |
86
+ | `--install` | off | Installs dependencies at the end (with the detected/chosen manager) |
87
+ | `--git` | off | Runs `git init` + first commit ("Initial commit from create-basalt") |
88
+ | `--pm=<manager>` | autodetect | Package manager: `pnpm` \| `npm` \| `yarn` \| `bun` |
89
+ | `-y`, `--yes` | — | Skips the questions and accepts the defaults |
90
+ | `-h`, `--help` | — | Shows help and exits |
91
+
92
+ Behavior notes (faithful to the code):
93
+
94
+ - **Package manager detection**: by default, detects who invoked the command via the `npm_config_user_agent` variable (set by npm/pnpm/yarn/bun); unknown managers fall back to `npm`. `--pm=` overrides this.
95
+ - **`--ui` forces pnpm**: the `web/` frontend is a member of a pnpm *workspace* (declared in the generated `pnpm-workspace.yaml`). npm, yarn, and bun can't install or run that structure, so if you request `--ui` with another manager, you'll see `Note: --ui projects are pnpm workspaces — using pnpm instead of <manager>.` and pnpm is used.
96
+ - **Interactive mode**: only happens when you don't pass a name, you're in a terminal (TTY), and you didn't use `--yes`. Ctrl+C during a question ends cleanly with "Cancelled." (exit code 130).
97
+ - **Occupied folder**: if the destination folder exists and isn't empty, the command refuses with `Target directory "<dir>" already exists and is not empty.` and exits with code 1.
98
+ - At the end, it prints the created files and the "Next steps" appropriate to your choices.
99
+
100
+ ### Invocation examples
101
+
102
+ ```bash
103
+ # Full project, no questions, with everything:
104
+ pnpm create basalt my-app --billing --ui --cli --install --git
105
+
106
+ # Minimal API (no tenancy or auth), in another folder:
107
+ npm create basalt service-api --no-tenancy --no-auth --dir=./apps/service-api
108
+
109
+ # Accept all defaults with no questions:
110
+ npm create basalt my-app -y
111
+
112
+ # Force yarn as the manager:
113
+ npm create basalt my-app --pm=yarn --install
114
+ ```
115
+
116
+ ### What gets generated
117
+
118
+ Always:
119
+
120
+ ```
121
+ my-app/
122
+ ├── package.json # scripts: dev, start, test, typecheck (+ basalt with --cli)
123
+ ├── tsconfig.json # strict TypeScript, ESM
124
+ ├── .env.example # PORT, HOST, LOG_LEVEL, NODE_ENV (+ APP_SECRET with auth)
125
+ ├── .gitignore
126
+ ├── README.md # instructions adapted to the chosen options
127
+ ├── pnpm-workspace.yaml # esbuild allowBuilds (+ "web" member with --ui)
128
+ ├── src/
129
+ │ ├── env.ts # environment variables validated with Zod (@basaltkit/env)
130
+ │ ├── app.ts # buildApp() with the chosen plugins
131
+ │ ├── routes.ts # GET / (friendly index) and GET /health
132
+ │ └── server.ts # startup + clean shutdown on SIGINT/SIGTERM
133
+ └── tests/app.test.ts # smoke test adapted to the options
134
+ ```
135
+
136
+ With `--cli`, adds `bin/basalt.ts` and the `"basalt": "tsx bin/basalt.ts"` script. With `--ui`, adds the `web/` folder (Vite + React + Tailwind + shadcn, with `web/src/api.ts` built on top of `@basaltkit/sdk`; with auth on it includes a login/register screen).
137
+
138
+ ### With `--ui`: running the API and frontend
139
+
140
+ ```bash
141
+ pnpm install
142
+ pnpm dev # terminal 1 — API on :3000
143
+ pnpm --filter my-app-web dev # terminal 2 — UI at http://localhost:5180
144
+ ```
145
+
146
+ Vite's dev server proxies `/api` to the API — there's no CORS to configure.
147
+
148
+ ### With `--cli`: the `basalt` command line
149
+
150
+ ```bash
151
+ pnpm basalt list # available commands
152
+ pnpm basalt routes # registered HTTP routes
153
+ pnpm basalt make:resource Project # generates schema → repository → service → plugin → routes → test
154
+ ```
155
+
156
+ ### Programmatic usage (Advanced)
157
+
158
+ The package also exports the API used by the executable, for your own scripts:
159
+
160
+ ```typescript
161
+ import { createProject, detectPackageManager, TargetNotEmptyError } from 'create-basalt'
162
+
163
+ const result = await createProject({
164
+ name: 'my-app',
165
+ dir: './output/my-app', // optional; default: ./<name>
166
+ tenancy: true,
167
+ auth: true,
168
+ billing: false,
169
+ ui: false,
170
+ cli: true,
171
+ })
172
+ console.log(result.dir) // absolute path created
173
+ console.log(result.files) // relative paths, sorted
174
+ console.log(detectPackageManager()) // 'pnpm' | 'npm' | 'yarn' | 'bun'
175
+ ```
176
+
177
+ Note: `createProject` **only writes files** — it doesn't install dependencies or initialize git (that's the executable's job, with `--install`/`--git`).
178
+
179
+ ## API reference
180
+
181
+ Exported from `create-basalt` (in addition to the `create-basalt` executable):
182
+
183
+ ### `createProject(input): Promise<CreateProjectResult>`
184
+
185
+ `CreateProjectInput`:
186
+
187
+ | Field | Type | Required? | Default | Description |
188
+ | --- | --- | --- | --- | --- |
189
+ | `name` | `string` | Yes | — | Project name |
190
+ | `dir` | `string` | No | `./<name>` (relative to cwd) | Destination folder |
191
+ | `tenancy` | `boolean` | No | `true` | Include multi-tenancy |
192
+ | `auth` | `boolean` | No | `true` | Include authentication |
193
+ | `billing` | `boolean` | No | `false` | Include subscriptions |
194
+ | `ui` | `boolean` | No | `false` | Generate the `web/` frontend |
195
+ | `cli` | `boolean` | No | `false` | Generate the `basalt` CLI |
196
+
197
+ `CreateProjectResult`:
198
+
199
+ | Field | Type | Description |
200
+ | --- | --- | --- |
201
+ | `dir` | `string` | Absolute path of the created folder |
202
+ | `files` | `string[]` | Files written (relative, sorted) |
203
+ | `options` | `ProjectOptions` | The options actually applied (with defaults resolved) |
204
+
205
+ Throws `TargetNotEmptyError` if the destination folder exists and isn't empty.
206
+
207
+ ### `detectPackageManager(userAgent?): PackageManager`
208
+
209
+ Detects the manager that invoked the command from `npm_config_user_agent` (or the string passed in). Returns `'pnpm' | 'yarn' | 'bun'` when recognized; otherwise `'npm'`.
210
+
211
+ ### `TargetNotEmptyError`
212
+
213
+ Error (extends `Error`) with the message `Target directory "<dir>" already exists and is not empty.`
214
+
215
+ ### Exported types
216
+
217
+ | Type | Description |
218
+ | --- | --- |
219
+ | `PackageManager` | `'pnpm' \| 'npm' \| 'yarn' \| 'bun'` |
220
+ | `ProjectOptions` | `{ name, tenancy, auth, billing, ui, cli }` — all resolved (no optionals) |
221
+ | `CreateProjectInput`, `CreateProjectResult` | Described above |
222
+
223
+ ## Common errors and solutions (FAQ)
224
+
225
+ **I created a project with `--ui` and `npm install` fails (`web/` dependencies don't resolve).**
226
+ This is the classic error: the `web/` frontend is a member of a **pnpm** *workspace* (`pnpm-workspace.yaml`). npm (like yarn and bun) doesn't read that file, so it doesn't install `web/`'s dependencies or manage to launch its dev server. Solution: use pnpm in that project — `pnpm install` at the root and `pnpm --filter <name>-web dev` for the UI. (This is why the CLI itself switches to pnpm when you request `--ui` with another manager.)
227
+
228
+ **`Target directory "…" already exists and is not empty.`**
229
+ The destination folder already has content. Choose another name, point to another folder with `--dir=`, or empty it first. The generator never overwrites anything.
230
+
231
+ **I ran the command in a script/CI and it hung or didn't ask anything.**
232
+ Outside an interactive terminal (no TTY) the questions are skipped. Always pass the name and flags explicitly — and use `-y` to make sure no prompt appears.
233
+
234
+ **`(skipped — git unavailable or already a repo)` after `--git`.**
235
+ `git init`/commit failed — either git isn't installed, or the folder already belongs to a repository. The project is still created; handle git by hand.
236
+
237
+ **`(install failed — run "pnpm install" yourself)`.**
238
+ Automatic installation failed (network, Node version, etc.). Go into the folder and run `pnpm install` (or the indicated manager) to see the real error.
239
+
240
+ **I started the app and `GET /auth/login` gives a secret error.**
241
+ With auth on, `src/env.ts` requires `APP_SECRET` with at least 16 characters (there's a development default `change-me-in-production--`). Copy `.env.example` to `.env` and set your own secret before going to production.
242
+
243
+ **I want to change my mind after generating (e.g. add billing).**
244
+ There's no "re-scaffold" command. Either generate a new project with the right flags and compare, or add it by hand: install `@basaltkit/subscriptions` and add the `subscriptionsPlugin` to `src/app.ts` (the generated README and the templates serve as reference).
245
+
246
+ ## How it connects to other modules
247
+
248
+ `create-basalt` isn't used *by* the application — it writes the application that uses the other packages:
249
+
250
+ - **`@basaltkit/core`, `@basaltkit/config`, `@basaltkit/env`, `@basaltkit/events`, `@basaltkit/fastify`, `@basaltkit/logger`** — the foundation of any generated project (`createApp` + plugins in `src/app.ts`).
251
+ - **`@basaltkit/tenancy`** — included by default (remove with `--no-tenancy`): header and subdomain resolvers, with a demo `MemoryTenantSource`.
252
+ - **`@basaltkit/auth`** — included by default (remove with `--no-auth`): `/auth/*` routes and `APP_SECRET` validated in `env.ts`.
253
+ - **`@basaltkit/subscriptions`** — with `--billing`: example `free`/`pro` plans with trial and feature limits.
254
+ - **`@basaltkit/cli` + `@basaltkit/generator`** — with `--cli`: `bin/basalt.ts` calls `runCli`, and `commandsPlugin(generatorCommands())` registers the `make:*` generators.
255
+ - **`@basaltkit/sdk` + `@basaltkit/admin-shadcn` + `@basaltkit/admin`** — with `--ui`: the `web/` frontend calls the API through a typed client and uses the shadcn components.
256
+ - **`@basaltkit/testing`** — always present in `devDependencies`, with a generated smoke test in `tests/app.test.ts`.