@beignet/cli 0.0.1 → 0.0.4

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.
Files changed (98) hide show
  1. package/CHANGELOG.md +231 -0
  2. package/README.md +497 -90
  3. package/dist/ansi.d.ts +10 -0
  4. package/dist/ansi.d.ts.map +1 -0
  5. package/dist/ansi.js +20 -0
  6. package/dist/ansi.js.map +1 -0
  7. package/dist/choices.d.ts +72 -0
  8. package/dist/choices.d.ts.map +1 -0
  9. package/dist/choices.js +88 -0
  10. package/dist/choices.js.map +1 -0
  11. package/dist/completion.d.ts +47 -0
  12. package/dist/completion.d.ts.map +1 -0
  13. package/dist/completion.js +123 -0
  14. package/dist/completion.js.map +1 -0
  15. package/dist/config.d.ts +39 -0
  16. package/dist/config.d.ts.map +1 -1
  17. package/dist/config.js +26 -0
  18. package/dist/config.js.map +1 -1
  19. package/dist/create-prompts.d.ts +42 -0
  20. package/dist/create-prompts.d.ts.map +1 -0
  21. package/dist/create-prompts.js +136 -0
  22. package/dist/create-prompts.js.map +1 -0
  23. package/dist/create.d.ts +12 -0
  24. package/dist/create.d.ts.map +1 -1
  25. package/dist/create.js +19 -24
  26. package/dist/create.js.map +1 -1
  27. package/dist/db.d.ts +37 -0
  28. package/dist/db.d.ts.map +1 -0
  29. package/dist/db.js +146 -0
  30. package/dist/db.js.map +1 -0
  31. package/dist/github-annotations.d.ts +18 -0
  32. package/dist/github-annotations.d.ts.map +1 -0
  33. package/dist/github-annotations.js +22 -0
  34. package/dist/github-annotations.js.map +1 -0
  35. package/dist/index.d.ts +1 -7
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +710 -400
  38. package/dist/index.js.map +1 -1
  39. package/dist/inspect.d.ts +45 -2
  40. package/dist/inspect.d.ts.map +1 -1
  41. package/dist/inspect.js +2191 -100
  42. package/dist/inspect.js.map +1 -1
  43. package/dist/lib.d.ts +20 -0
  44. package/dist/lib.d.ts.map +1 -0
  45. package/dist/lib.js +17 -0
  46. package/dist/lib.js.map +1 -0
  47. package/dist/lint.d.ts +22 -1
  48. package/dist/lint.d.ts.map +1 -1
  49. package/dist/lint.js +370 -38
  50. package/dist/lint.js.map +1 -1
  51. package/dist/make.d.ts +109 -1
  52. package/dist/make.d.ts.map +1 -1
  53. package/dist/make.js +2225 -333
  54. package/dist/make.js.map +1 -1
  55. package/dist/outbox.d.ts +24 -0
  56. package/dist/outbox.d.ts.map +1 -0
  57. package/dist/outbox.js +138 -0
  58. package/dist/outbox.js.map +1 -0
  59. package/dist/schedule.d.ts +36 -0
  60. package/dist/schedule.d.ts.map +1 -0
  61. package/dist/schedule.js +155 -0
  62. package/dist/schedule.js.map +1 -0
  63. package/dist/task.d.ts +26 -0
  64. package/dist/task.d.ts.map +1 -0
  65. package/dist/task.js +106 -0
  66. package/dist/task.js.map +1 -0
  67. package/dist/templates.d.ts +12 -8
  68. package/dist/templates.d.ts.map +1 -1
  69. package/dist/templates.js +2144 -385
  70. package/dist/templates.js.map +1 -1
  71. package/dist/version.d.ts +8 -0
  72. package/dist/version.d.ts.map +1 -0
  73. package/dist/version.js +18 -0
  74. package/dist/version.js.map +1 -0
  75. package/package.json +9 -8
  76. package/src/ansi.ts +30 -0
  77. package/src/choices.ts +137 -0
  78. package/src/completion.ts +169 -0
  79. package/src/config.ts +47 -0
  80. package/src/create-prompts.ts +182 -0
  81. package/src/create.ts +32 -28
  82. package/src/db.ts +222 -0
  83. package/src/github-annotations.ts +37 -0
  84. package/src/index.ts +1119 -535
  85. package/src/inspect.ts +3372 -134
  86. package/src/lib.ts +45 -0
  87. package/src/lint.ts +533 -45
  88. package/src/make.ts +3010 -397
  89. package/src/outbox.ts +249 -0
  90. package/src/schedule.ts +272 -0
  91. package/src/task.ts +169 -0
  92. package/src/templates.ts +2282 -462
  93. package/src/version.ts +20 -0
  94. package/dist/create-bin.d.ts +0 -3
  95. package/dist/create-bin.d.ts.map +0 -1
  96. package/dist/create-bin.js +0 -9
  97. package/dist/create-bin.js.map +0 -1
  98. package/src/create-bin.ts +0 -11
package/README.md CHANGED
@@ -1,23 +1,59 @@
1
1
  # @beignet/cli
2
2
 
3
+ > [!CAUTION]
4
+ > Beignet is experimental alpha software. The `0.0.x` package line is for early
5
+ > evaluation, and APIs may change between releases while the framework settles.
6
+
3
7
  Command-line tools for creating and maintaining Beignet apps.
4
8
 
5
- ## Create an app
9
+ The package ships a single `beignet` bin. Generated apps install
10
+ `@beignet/cli` as a dev dependency and add a `beignet` package script, so the
11
+ day-to-day form inside an app is:
6
12
 
7
13
  ```bash
8
- bunx -p @beignet/cli beignet create my-app
14
+ bun beignet doctor --strict
9
15
  ```
10
16
 
11
- Or use the package binary directly:
17
+ With npm use `npm run beignet -- doctor --strict`, with pnpm
18
+ `pnpm beignet doctor --strict`, and with yarn `yarn beignet doctor --strict`.
19
+ For one-off runs outside an app, invoke the scoped package directly:
12
20
 
13
21
  ```bash
14
- bunx -p @beignet/cli create-beignet my-app
22
+ bunx @beignet/cli routes
15
23
  ```
16
24
 
25
+ Never run the unscoped `beignet` name through bunx or npx; that npm name
26
+ belongs to an unrelated package.
27
+
28
+ ## Create an app
29
+
30
+ App scaffolding lives in the `create-beignet` package:
31
+
32
+ ```bash
33
+ npm create beignet@latest my-app
34
+ # or
35
+ bun create beignet my-app
36
+ ```
37
+
38
+ Both delegate to `beignet create`, so `bunx @beignet/cli create my-app` is
39
+ equivalent.
40
+
41
+ Running create in an interactive terminal without selection flags opens a
42
+ prompt-based setup that asks for the project directory, preset, features,
43
+ integrations, and package manager. Passing any selection flag
44
+ (`--preset`, `--features`, `--integrations`, or `--package-manager`) skips the
45
+ prompts, and `--yes` forces the non-interactive defaults (the `standard`
46
+ preset with `bun`) even on a terminal. Non-interactive runs in scripts and CI
47
+ behave exactly as before.
48
+
17
49
  By default the CLI creates a Next.js app with:
18
50
 
19
51
  - Contract-first todos API routes
20
- - App-owned ports and in-memory infra adapters
52
+ - Better Auth routes and Beignet auth provider wiring
53
+ - Drizzle/libSQL persistence with a local SQLite default and Turso-ready config
54
+ - UOW, audit logging, durable idempotency, and a durable outbox drain route
55
+ - Jobs, events, listeners, schedules, mail-backed notifications, and uploads
56
+ - App-owned ports with provider-backed infra adapters
21
57
  - Validated application use cases
22
58
  - A Beignet server wired through Next.js route handlers
23
59
  - App error helpers, auth helpers, env validation, health checks, and devtools
@@ -26,65 +62,169 @@ By default the CLI creates a Next.js app with:
26
62
  - React Hook Form
27
63
  - OpenAPI output
28
64
 
65
+ Preview the planned files without writing anything:
66
+
67
+ ```bash
68
+ bunx @beignet/cli create my-app --dry-run
69
+ bunx @beignet/cli create my-app --dry-run --json
70
+ ```
71
+
29
72
  The CLI writes files only. After creation:
30
73
 
31
74
  ```bash
32
75
  cd my-app
33
76
  bun install
34
77
  cp .env.example .env.local
78
+ bun beignet db generate
79
+ bun beignet db migrate
35
80
  bun run dev
36
81
  ```
37
82
 
38
- Common follow-up commands:
83
+ Open `http://localhost:3000` for the starter UI. The generated homepage links
84
+ to the todo feature, health check, OpenAPI document, devtools, and Beignet
85
+ docs.
86
+
87
+ Inspect the app, then generate the next feature:
39
88
 
40
89
  ```bash
41
90
  # in another terminal, from my-app
42
- bunx -p @beignet/cli beignet make resource projects
43
- bunx -p @beignet/cli beignet routes
44
- bunx -p @beignet/cli beignet lint
45
- bunx -p @beignet/cli beignet doctor
91
+ bun beignet routes
92
+ bun beignet lint
93
+ bun beignet doctor
94
+ bun beignet make feature projects
95
+ bun beignet db generate
96
+ bun beignet db migrate
97
+ bun run test
98
+ bun run typecheck
99
+ bun beignet lint
100
+ bun beignet doctor
46
101
  ```
47
102
 
48
103
  ## Options
49
104
 
50
105
  ```bash
51
- beignet create <directory> [options]
106
+ beignet create [directory] [options]
107
+ beignet task run <name> [options]
108
+ beignet outbox drain [options]
109
+ beignet schedule run <name> [options]
110
+ beignet make task <feature>/<name> [options]
52
111
  beignet make contract <name> [options]
53
112
  beignet make event <feature>/<name> [options]
113
+ beignet make factory <feature>/<name> [options]
114
+ beignet make feature <name> [options]
54
115
  beignet make job <feature>/<name> [options]
116
+ beignet make notification <feature>/<name> [options]
55
117
  beignet make listener <feature>/<name> --event <feature>/<event> [options]
56
118
  beignet make schedule <feature>/<name> [options]
57
119
  beignet make port <name> [options]
58
120
  beignet make policy <name> [options]
59
121
  beignet make adapter <name> [options]
60
122
  beignet make resource <name> [options]
123
+ beignet make seed <feature>/<name> [options]
61
124
  beignet make test <feature>/<action> [options]
62
125
  beignet make use-case <feature>/<action> [options]
63
- beignet routes [--json]
64
- beignet lint [--json]
65
- beignet doctor [--json] [--strict] [--fix]
126
+ beignet db generate [--dry-run] [--json]
127
+ beignet db migrate [--dry-run] [--json]
128
+ beignet db seed [--dry-run] [--json]
129
+ beignet db reset [--dry-run] [--json]
130
+ beignet routes [--json] [--cwd <dir>]
131
+ beignet lint [--json] [--cwd <dir>] [--format human|json|github]
132
+ beignet doctor [--json] [--strict] [--fix] [--cwd <dir>] [--format human|json|github]
133
+ beignet completion install [--shell bash|zsh]
134
+ beignet completion uninstall [--shell bash|zsh]
135
+ beignet --version
66
136
 
67
137
  Options:
68
138
  --template next Template to use. Currently only `next`.
69
- --preset standard `minimal` or `standard`.
139
+ --preset standard `minimal` or `standard`. Defaults to `standard`.
70
140
  --package-manager bun Package manager shown in next steps.
71
- --feature react-query Add a starter feature. Repeatable.
72
- --features client,openapi Add features from a comma-separated list.
73
- --integration inngest Add an integration. Repeatable.
74
- --integrations inngest,pino Add integrations from a comma-separated list.
141
+ --features client,openapi Add starter features. Accepts one value or a comma-separated list.
142
+ --integrations inngest,pino Add integrations. Accepts one value or a comma-separated list.
143
+ --yes Skip interactive create prompts and use the defaults.
75
144
  --force Write into a non-empty directory.
76
- --dry-run Preview make writes without changing files.
145
+ --dry-run Preview create/make writes without changing files.
77
146
  --json Print machine-readable output.
147
+ --cwd path/to/app Run routes, lint, or doctor against another app directory.
148
+ --format github Output format for lint and doctor: human, json, or github.
149
+ --input '{"dryRun":true}' JSON input for `beignet task run`.
150
+ --payload '{"date":"..."}' JSON payload for `beignet schedule run`.
151
+ --module server/tasks.ts Override the task, outbox, or schedule registry path.
152
+ --batch-size 100 Maximum outbox messages to claim in one drain pass.
153
+ --scheduled-at 2026-01-01 Provider scheduled timestamp for schedule runs.
154
+ --triggered-at 2026-01-01 Trigger timestamp for schedule runs.
155
+ --attempt 1 One-based provider attempt number for schedule runs.
156
+ --source manual Provider or app source label for schedule runs.
157
+ --with policy,events Add optional artifacts to `make feature`; also supports tasks, jobs, notifications, ui, uploads.
158
+ --auth Add authorization metadata, policy wiring, policy tests, and use-case gate checks to `make resource`.
159
+ --tenant Add tenant-scoped schemas, repository filters, and use-case tenant checks to `make resource`.
160
+ --events Add created, updated, and deleted domain events to `make resource`.
161
+ --soft-delete Archive resource rows with deletedAt instead of hard-deleting them.
78
162
  --event posts/published Event for `make listener`.
79
163
  --cron "0 9 * * *" Cron expression for `make schedule`.
80
164
  --timezone America/Chicago Timezone for `make schedule`.
81
165
  --route Add a Next.js cron route for `make schedule`.
82
166
  --strict Include CI-oriented doctor warnings and fail on warnings.
83
167
  --fix Apply low-risk doctor fixes before reporting.
168
+ --shell zsh Shell for `completion install`/`uninstall`. Defaults to $SHELL.
84
169
  -h, --help Show help.
170
+ -v, --version Print the CLI version.
171
+ ```
172
+
173
+ ## Exit codes
174
+
175
+ Every command uses the same exit-code matrix:
176
+
177
+ | Code | Meaning |
178
+ | ---- | ------------------------------------------------------------------------ |
179
+ | 0 | Success. |
180
+ | 1 | Findings: lint diagnostics, doctor errors, or strict-mode doctor warnings. |
181
+ | 2 | Usage or internal errors: unknown commands, bad flags, missing arguments, runtime failures, or running `beignet` with no arguments. |
182
+
183
+ `--format` never changes the exit code; `lint --format github` still exits 1
184
+ when it finds diagnostics.
185
+
186
+ ## Output formats
187
+
188
+ `lint` and `doctor` support three output formats. `--json` stays the
189
+ canonical machine-readable flag; `--format` adds GitHub Actions annotations:
190
+
191
+ ```bash
192
+ beignet lint --format github
193
+ beignet doctor --strict --format github
85
194
  ```
86
195
 
87
- ## App conventions
196
+ The github format emits one `::error`, `::warning`, or `::notice` workflow
197
+ command per diagnostic, with `file`, `line`, and `col` properties when known.
198
+ When `GITHUB_ACTIONS=true` is set and no format is passed, lint and doctor
199
+ default to the github format; otherwise they default to human output.
200
+ Combining `--json` with a conflicting `--format` is an error. Human output
201
+ groups doctor diagnostics by severity, colors severity labels on TTYs, and
202
+ respects `NO_COLOR`.
203
+
204
+ All `--json` results begin with `schemaVersion: 1` so other tools can detect
205
+ output-shape changes.
206
+
207
+ ## Shell completions
208
+
209
+ Install tab completions for the `beignet` bin into your shell rc file:
210
+
211
+ ```bash
212
+ beignet completion install
213
+ beignet completion install --shell zsh
214
+ beignet completion uninstall
215
+ ```
216
+
217
+ `install` writes a managed block to `~/.bashrc` or `~/.zshrc` (detected from
218
+ `$SHELL` when `--shell` is omitted) and `uninstall` removes it. Restart your
219
+ shell or source the rc file to activate completions. Zsh completions require
220
+ `compinit` to be loaded, which most zsh setups already do.
221
+
222
+ Completions cover commands, subcommands, flags, and enum flag values such as
223
+ `--preset` and `--features`. The shell scripts call the
224
+ `beignet completion propose` helper, which prints one proposal per line for a
225
+ partial command line; it exists for the shell integration and is safe to
226
+ ignore otherwise. Completions complete whatever `beignet` resolves to on your
227
+ `PATH`, such as the app-local bin installed by generated apps.
88
228
 
89
229
  Beignet CLI commands are convention-aware. `routes`, `lint`, `doctor`, and
90
230
  generators work best in the Next.js layout created by `beignet create`:
@@ -92,14 +232,19 @@ generators work best in the Next.js layout created by `beignet create`:
92
232
  ```txt
93
233
  features/
94
234
  app/api/
235
+ server/context.ts
95
236
  server/index.ts
96
237
  server/routes.ts
97
238
  ```
98
239
 
240
+ `server/context.ts` declares the app's context blueprint with
241
+ `defineServerContext(...)`; `server/index.ts` passes it to the server and
242
+ generated route tests pass the same value to `createTestApp(...)`.
243
+
99
244
  Route inspection supports contract-group definitions and direct
100
- `createContract({ method, path })` exports.
245
+ `defineContract({ method, path })` exports.
101
246
 
102
- The standard app layout includes the files resource generators expect:
247
+ The standard app layout includes the files feature generators expect:
103
248
 
104
249
  ```txt
105
250
  app-context.ts
@@ -108,8 +253,13 @@ ports/index.ts
108
253
  lib/use-case.ts
109
254
  ```
110
255
 
256
+ Standard apps also include context-bound capability builders in `lib/`
257
+ (`lib/listeners.ts`, `lib/jobs.ts`, `lib/schedules.ts`, and
258
+ `lib/notifications.ts`); generators create missing builder files on demand,
259
+ including `lib/tasks.ts`.
260
+
111
261
  Generated apps include these files by default. The CLI uses this shape to
112
- inspect routes, wire generated resources, and report drift safely. `beignet
262
+ inspect routes, wire generated features, and report drift safely. `beignet
113
263
  doctor` reports when a directory does not match the app layout.
114
264
 
115
265
  Use `beignet.config.ts`, `beignet.config.js`,
@@ -126,6 +276,9 @@ export default defineConfig({
126
276
  features: "src/features",
127
277
  routes: "src/app/api",
128
278
  server: "src/core/server/index.ts",
279
+ tasks: "src/core/server/tasks.ts",
280
+ outbox: "src/core/server/outbox.ts",
281
+ schedules: "src/core/server/schedules.ts",
129
282
  },
130
283
  });
131
284
  ```
@@ -146,11 +299,62 @@ The command honors `beignet.config.*` path overrides and writes
146
299
  `features/projects/contracts.ts`. It creates a self-contained contract group with a
147
300
  starter list endpoint, schema, standard error response, and exported contract
148
301
  list. It does not wire route handlers, use cases, ports, or OpenAPI; use
149
- `make resource` when you want the full path from contract to tests.
302
+ `make feature` when you want the full path from contract to tests.
150
303
 
151
- Like `make resource`, `make contract` skips identical files and stops on
304
+ Like `make feature`, `make contract` skips identical files and stops on
152
305
  divergent files unless you pass `--force`.
153
306
 
307
+ ## Generate a feature
308
+
309
+ Inside a Beignet app, scaffold the standard contract-first vertical slice for a
310
+ product capability or workflow with:
311
+
312
+ ```bash
313
+ beignet make feature projects
314
+ ```
315
+
316
+ The command honors `beignet.config.*` path overrides. It writes:
317
+
318
+ - `features/projects/contracts.ts`
319
+ - `features/projects/use-cases/`
320
+ - `features/projects/ports.ts`
321
+ - `infra/projects/drizzle-project-repository.ts`
322
+ - `features/projects/routes.ts`
323
+ - `features/projects/tests/projects.test.ts`
324
+
325
+ It also wires the slice into the central route registry (`server/routes.ts` in
326
+ generated apps), `ports/index.ts`, `infra/db/repositories.ts`, and adds a
327
+ `test` script if the app does not already have one. The generated slice uses a small
328
+ `name` field by default so the app stays typecheckable immediately. Treat that
329
+ field as a placeholder and shape the schemas, use cases, repository, and tests
330
+ around the feature's real workflow.
331
+
332
+ Add common feature-owned artifacts with `--with`:
333
+
334
+ ```bash
335
+ beignet make feature projects --with policy,events,jobs,notifications,ui,uploads
336
+ ```
337
+
338
+ The optional artifacts use predictable starter names:
339
+
340
+ - `features/projects/policy.ts`
341
+ - `features/projects/domain/events/created.ts`
342
+ - `features/projects/jobs/process.ts`
343
+ - `features/projects/notifications/created.ts`
344
+ - `features/projects/components/projects-panel.tsx`
345
+ - `features/projects/uploads/attachment.ts`
346
+
347
+ Event, job, notification, and upload registries are created or updated in the
348
+ matching folder `index.ts` files. The UI addon is opt-in:
349
+
350
+ ```bash
351
+ beignet make feature projects --with ui
352
+ ```
353
+
354
+ It writes a feature-colocated React component that uses the generated
355
+ contracts through `client/index.ts` and TanStack Query, with error messages
356
+ from `contractErrorMessage` in `@beignet/core/client`.
357
+
154
358
  ## Generate a use case
155
359
 
156
360
  Use `make use-case` inside a Beignet app when you want a focused
@@ -168,7 +372,10 @@ The name uses `feature/action` format. The command writes
168
372
 
169
373
  ## Generate a test
170
374
 
171
- Use `make test` after generating a use case:
375
+ Use `make test` after generating a focused use case. Generated features and
376
+ resources already include their baseline contract, use-case, route, policy, and
377
+ repository-oriented tests; `make test` is for adding coverage around an
378
+ additional workflow that does not need a whole new resource slice.
172
379
 
173
380
  ```bash
174
381
  beignet make use-case projects/archive-project
@@ -176,7 +383,8 @@ beignet make test projects/archive-project
176
383
  ```
177
384
 
178
385
  The command writes `features/projects/tests/archive-project.test.ts`, builds the
179
- app context through `createUseCaseTester`, and asserts the starter
386
+ app context through `createTestPorts(...)`, `createTestContextFactory(...)`, and
387
+ `createUseCaseTester(...)`, and asserts the starter
180
388
  `{ ok: true }` response. It also adds a `test` script when the app does not
181
389
  already define one. Treat the output as a compiling starting point: replace the
182
390
  input, context setup, and assertion with behavior-specific coverage as the use
@@ -229,82 +437,202 @@ was already customized, the command stops instead of guessing.
229
437
 
230
438
  ## Generate a resource
231
439
 
232
- Inside a Beignet app, scaffold a new vertical slice with:
440
+ Use `make resource` when the feature you are building is CRUD-shaped and the
441
+ main concept is an entity with repository-backed persistence:
233
442
 
234
443
  ```bash
235
444
  beignet make resource projects
445
+ beignet make resource projects --auth --tenant --events --soft-delete
236
446
  ```
237
447
 
238
- The command honors `beignet.config.*` path overrides. It writes:
239
-
240
- - `features/projects/contracts.ts`
241
- - `features/projects/use-cases/`
242
- - `features/projects/ports.ts`
243
- - `infra/projects/in-memory-project-repository.ts`
244
- - `features/projects/routes.ts`
245
- - `features/projects/tests/projects.test.ts`
246
-
247
- It also wires the resource into the central route registry (`server/routes.ts`
248
- in generated apps), `ports/index.ts`, `infra/app-ports.ts`, and adds a `test`
249
- script if the app does not already have one. Apps that serve OpenAPI from
250
- `server.contracts` or `contractsFromRoutes(routes)` stay in sync through the
251
- registered route list. Legacy direct `createOpenAPIHandler([...])` arrays are
252
- updated conservatively when possible.
253
-
254
- In apps created with the `drizzle-turso` integration, the command also creates
448
+ `make resource` generates a CRUD-shaped slice with list, create, get, update,
449
+ and delete contracts, use cases, route handlers, repository methods, a policy
450
+ starter, tests, and feature-specific not-found and conflict catalog errors. Use
451
+ `make feature` for workflows and capabilities that do not map cleanly to a REST
452
+ resource.
453
+
454
+ Generated list routes use cursor pagination with `limit`, `cursor`, `name`,
455
+ `sortBy`, and `sortDirection` query parameters. The generated use case
456
+ normalizes cursor pages, validates opaque base64url cursors against the selected
457
+ sort, and passes one repository query object into memory and Drizzle adapters.
458
+ Adapters filter `name` with case-insensitive contains matching, sort only by
459
+ `createdAt` or `name` plus `id`, and fetch `limit + 1` records to derive
460
+ `nextCursor` without a count query.
461
+
462
+ Generated resources also include optimistic concurrency by default. Responses
463
+ include a numeric `version`; update request bodies must send that version back;
464
+ memory and Drizzle repositories include the version in the update check and
465
+ increment it on success. Stale updates become the generated
466
+ `<Resource>Conflict` catalog error.
467
+
468
+ Use `--auth` to generate authorization metadata, policy wiring, and use-case
469
+ `ctx.gate.authorize(...)` calls. Use `--tenant` to scope repository reads and
470
+ writes by `ctx.tenant`. Use `--events` to generate created, updated, and
471
+ deleted domain events and publish them through `ctx.ports.eventBus`. Use
472
+ `--soft-delete` when delete routes should archive rows with `deletedAt` while
473
+ list, get, and update operations continue to read only active records.
474
+
475
+ In standard apps, the command also creates
255
476
  `infra/db/schema/projects.ts`,
256
477
  `infra/projects/drizzle-project-repository.ts`, and registers the repository in
257
- `infra/db/repositories.ts`. The in-memory repository remains available as a test
258
- fake.
478
+ `infra/db/repositories.ts`.
259
479
 
260
- The generated resource uses a small `name` field by default so the app stays
261
- typecheckable immediately. Rename the fields in the generated schemas,
262
- repository, and tests to match your domain.
263
-
264
- ## Generate events, jobs, listeners, and schedules
480
+ ## Generate tasks, events, jobs, notifications, listeners, schedules, and uploads
265
481
 
266
482
  Use feature artifact generators when a workflow grows beyond a single use case:
267
483
 
268
484
  ```bash
485
+ beignet make task posts/backfill-search
269
486
  beignet make event posts/published
270
487
  beignet make job posts/send-published-email
488
+ beignet make notification posts/published
271
489
  beignet make listener posts/enqueue-published-email --event posts/published
272
490
  beignet make schedule posts/daily-summary --cron "0 9 * * *" --timezone America/Chicago --route
491
+ beignet make upload posts/attachment
273
492
  ```
274
493
 
275
494
  These commands use `feature/name` format and write colocated feature files:
276
495
 
496
+ - `features/posts/tasks/backfill-search.ts`
277
497
  - `features/posts/domain/events/published.ts`
278
498
  - `features/posts/jobs/send-published-email.ts`
499
+ - `features/posts/notifications/published.ts`
279
500
  - `features/posts/listeners/enqueue-published-email.ts`
280
501
  - `features/posts/schedules/daily-summary.ts`
502
+ - `features/posts/uploads/attachment.ts`
281
503
 
282
504
  Each command creates or updates the folder's `index.ts` with an exported
283
- registry such as `postJobs`, `postListeners`, or `postSchedules`. Event and
284
- listener generators add `@beignet/core/events`, job generators add
285
- `@beignet/core/jobs`, and schedule
286
- generators add `@beignet/core/schedules`; `--route` also writes a Next.js cron
505
+ registry such as `postTasks`, `postJobs`, `postNotifications`,
506
+ `postListeners`, `postSchedules`, or `postUploads`. Task generators also
507
+ create or update `server/tasks.ts`, which is the registry and lifecycle
508
+ boundary used by `beignet task run <name> --input '{"dryRun":true}'`.
509
+ New task registries include `createTaskContext(...)` and
510
+ `stopTaskContext(...)` placeholders so provider startup and teardown stay
511
+ out of task definitions.
512
+
513
+ Task, job, listener, schedule, and notification generators define artifacts
514
+ through app-owned context-bound builders in `lib/`. Each generator creates the
515
+ builder file when it is missing:
516
+
517
+ - `lib/tasks.ts` exports `defineTask` from `createTasks<AppContext>()`
518
+ (`@beignet/core/tasks`)
519
+ - `lib/jobs.ts` exports `defineJob` from `createJobs<AppContext>()`
520
+ (`@beignet/core/jobs`)
521
+ - `lib/listeners.ts` exports `defineListener` from
522
+ `createListeners<AppContext>()` (`@beignet/core/events`)
523
+ - `lib/schedules.ts` exports `defineSchedule` from
524
+ `createSchedules<AppContext>()` (`@beignet/core/schedules`)
525
+ - `lib/notifications.ts` exports `defineNotification` from
526
+ `createNotifications<AppContext>()` (`@beignet/core/notifications`)
527
+
528
+ Event generators use `@beignet/core/events` directly and upload generators use
529
+ `@beignet/core/uploads`.
530
+ `--route` also writes a Next.js cron
287
531
  route under `app/api/cron/<feature>/<name>/route.ts`. Generated cron routes
288
532
  require `CRON_SECRET` and record schedule start, completion, and failure events
289
- in devtools.
533
+ in devtools. Generated uploads include starter metadata, constraints,
534
+ authorization, storage key, storage metadata, and completion hooks.
535
+
536
+ Use `beignet outbox drain` when you need to drain durable events and jobs from
537
+ an app-owned CLI, CI, or worker entrypoint:
538
+
539
+ ```bash
540
+ beignet outbox drain --batch-size 100
541
+ ```
542
+
543
+ The runner loads `server/outbox.ts` or `paths.outbox`. That module should export
544
+ `outboxRegistry`, `createOutboxDrainContext(...)`, and optionally
545
+ `stopOutboxDrainContext(...)`. The context owns app ports and provider
546
+ lifecycle, so the CLI drain uses the same outbox, event bus, jobs,
547
+ instrumentation, actor, and tenant decisions as the scheduled drain route.
548
+
549
+ There is intentionally no separate `beignet jobs drain` command. Outbox-backed
550
+ jobs drain through the outbox beside durable events. Direct provider jobs should
551
+ use provider-owned worker entrypoints, such as an Inngest route built with
552
+ `createInngestJobFunction(...)`.
553
+
554
+ Use `beignet schedule run` when you need to run a schedule explicitly from a
555
+ local shell, CI job, or worker entrypoint:
556
+
557
+ ```bash
558
+ beignet schedule run posts.log-daily-summary --scheduled-at 2026-01-01T09:00:00.000Z
559
+ ```
560
+
561
+ The runner loads `server/schedules.ts` or `paths.schedules`. That module should
562
+ export a `schedules` array, `createScheduleContext(...)`, and optionally
563
+ `stopScheduleContext(...)`. Omit `--payload` when the schedule should build its
564
+ own payload from `createPayload(...)`; pass `--payload` when a provider or manual
565
+ run supplies the payload.
566
+
567
+ Operational CLI commands are bounded entrypoints. Use them from a local shell,
568
+ CI job, release job, scheduler, or worker host. Do not start outbox drains,
569
+ queue consumers, or interval polling from provider lifecycle hooks in serverless
570
+ apps.
290
571
 
291
572
  `make listener` expects the event file to exist at the canonical generated path.
292
573
  Run `make event <feature>/<event>` first, then generate listeners for that event.
293
574
 
294
- `make resource` is idempotent for unchanged generated files: repeated runs skip
295
- identical files and avoid duplicate wiring. If a generated file exists with
296
- different content, the command stops unless you pass `--force`.
575
+ `make feature` and `make resource` are idempotent for unchanged generated files:
576
+ repeated runs skip identical files and avoid duplicate wiring. If a generated
577
+ file exists with different content, the command stops unless you pass `--force`.
578
+
579
+ ## Database lifecycle
580
+
581
+ Use `beignet db` from an app root to run database workflow scripts through one
582
+ stable framework command surface:
583
+
584
+ ```bash
585
+ beignet db generate
586
+ beignet db migrate
587
+ beignet db seed
588
+ beignet db reset
589
+ ```
590
+
591
+ The CLI delegates to app-owned package scripts named `db:generate`,
592
+ `db:migrate`, `db:seed`, and `db:reset`. In standard Drizzle apps, those scripts
593
+ run Drizzle Kit and the generated `infra/db/seed.ts` / `infra/db/reset.ts`
594
+ entrypoints. This keeps schema generation and migration behavior Drizzle-native
595
+ while making the lifecycle discoverable through Beignet.
596
+
597
+ `beignet db` checks standard prerequisites before running those scripts. If the
598
+ script is missing, Drizzle Kit has no `drizzle.config.*`, or the standard
599
+ seed/reset entrypoint was removed, the error points to the exact file or
600
+ package script to restore.
601
+
602
+ `db reset` is intentionally app-owned because destructive behavior depends on
603
+ the environment. The standard starter refuses to reset non-local database URLs
604
+ unless `BEIGNET_ALLOW_DATABASE_RESET=true` is set. Drizzle starters also include
605
+ `infra/db/test-database.ts`, which creates an isolated local libSQL database for
606
+ repository and persistence tests.
607
+
608
+ Generate feature-owned test factories and local/demo seeds with:
609
+
610
+ ```bash
611
+ beignet make factory posts/post
612
+ beignet make seed posts/demo-posts
613
+ ```
614
+
615
+ Factories are written under `features/posts/tests/factories/` and persist
616
+ through app-owned repository ports. Seeds are written under
617
+ `features/posts/seeds/`; import them from the app-owned database seed entrypoint
618
+ and run them with `runSeeds(...)`.
619
+
620
+ Generated feature and use-case tests use Beignet's test context helpers so
621
+ request IDs, actors, tenants, and ports follow the same pattern as the testing
622
+ docs. Generated route tests boot `createTestApp(...)` with the app's
623
+ `server/context.ts` blueprint, so HTTP coverage exercises the same identity
624
+ resolution as production.
297
625
 
298
626
  Preview writes without changing files:
299
627
 
300
628
  ```bash
301
- beignet make resource projects --dry-run
629
+ beignet make feature projects --dry-run
302
630
  ```
303
631
 
304
632
  Use JSON output when another tool needs the exact planned changes:
305
633
 
306
634
  ```bash
307
- beignet make resource projects --dry-run --json
635
+ beignet make feature projects --dry-run --json
308
636
  ```
309
637
 
310
638
  ## Inspect app routes
@@ -329,21 +657,30 @@ Run `lint` when you want the CLI to enforce Beignet dependency direction:
329
657
  beignet lint
330
658
  ```
331
659
 
332
- The command scans static imports and fails when core feature layers reach into
333
- runtime or framework layers. It catches domain, use case, policy, port,
334
- contract, and route files importing things like `infra/`, UI components,
335
- client modules, provider packages, Next.js, React, or database vendors in the
336
- wrong direction.
660
+ The command scans static imports and fails when app layers reach into runtime
661
+ or framework layers. It catches domain, use case, workflow, policy, port,
662
+ contract, route, infra, and component files importing things like `infra/`, UI
663
+ components, route handlers, server modules, client modules, provider packages,
664
+ Next.js, React, or database vendors in the wrong direction. It also catches
665
+ feature-specific domain files importing another feature's domain unless the
666
+ target is `features/shared/domain`. Workflow folders include feature-owned
667
+ jobs, listeners, notifications, schedules, and uploads.
337
668
 
338
- Use JSON output in CI or custom scripts:
669
+ Import diagnostics include 1-based `line` and `column` positions. Human
670
+ output prints clickable `file.ts:12:3` locations; runtime-boundary
671
+ diagnostics report the chain root file and the full import chain in the
672
+ message instead.
673
+
674
+ Use JSON output in CI or custom scripts, or GitHub annotations in workflows:
339
675
 
340
676
  ```bash
341
677
  beignet lint --json
678
+ beignet lint --format github
342
679
  ```
343
680
 
344
681
  ## Check for drift
345
682
 
346
- Run `doctor` when you want quick feedback on contract wiring drift:
683
+ Run `doctor` when you want a framework integrity report:
347
684
 
348
685
  ```bash
349
686
  beignet doctor
@@ -354,28 +691,85 @@ Today it detects:
354
691
  - Contracts without a matching Next.js route handler
355
692
  - Feature route groups that are not registered in the central route list
356
693
  - Next.js route handlers that do not map to known contracts
357
- - OpenAPI drift in direct arrays and common exported contract lists
358
- - Partially wired generated resource slices
694
+ - OpenAPI drift in direct arrays, exported contract lists, and
695
+ `contractsFromRoutes(routes)` route registries
696
+ - OpenAPI entries for contracts outside the registered route surface
697
+ - Strict-mode canonical app conformance drift, including missing app context,
698
+ server route/provider registries, app ports, client helpers, typed route
699
+ groups, and local `AppContext` redeclarations
700
+ - Partially wired generated feature slices
701
+ - CRUD resource slices missing generated contracts, use cases, repository
702
+ methods, route entries, not-found catalog errors, or strict-mode test coverage
703
+ - Route-owned catalog errors declared on contracts but missing from
704
+ `features/shared/errors.ts`
705
+ - Feature runtime `appError(...)` calls that are not declared on the feature's
706
+ contracts
707
+ - Authorization metadata whose ability is missing from feature policy coverage
708
+ - Audit-required metadata without feature audit writes or test assertions
709
+ - Mixed `@beignet/*` version ranges in package.json, and mixed installed
710
+ `@beignet/*` versions in node_modules; the packages release together, so one
711
+ app should use one version
712
+ - CLI versions that differ from the app's installed `@beignet/core`, reported
713
+ as an informational hint that suggests the app-local `bun beignet`
714
+ - Malformed installed provider package metadata
359
715
  - Provider packages without matching canonical app ports
716
+ - Installed lifecycle provider packages that are not registered in
717
+ `server/providers.ts`, reported as an informational hint when the provider
718
+ declares registration as optional, such as `@beignet/devtools`
719
+ - App database providers registered before the provider that installs `db`
360
720
  - Generated ports without test fakes
361
-
362
- Use `beignet doctor --strict` for CI-oriented checks that may be noisy
363
- locally, such as missing generated resource tests. Use
364
- `beignet doctor --json` in CI or custom scripts.
721
+ - Drizzle repository adapters that are not wired through `infra/db/repositories.ts`
722
+ - Repository ports whose infra folder has no repository adapter
723
+ - Missing Drizzle config, schema index exports, database scripts, or standard seed/reset entrypoints
724
+ - Single-file `infra/db/schema.ts` schemas that should move to the
725
+ `infra/db/schema/` directory form
726
+ - Feature tests placed directly under `features/<feature>/` instead of
727
+ `features/<feature>/tests/`
728
+ - Standard database reset entrypoints that no longer guard non-local resets
729
+ - Feature seeds without a matching feature factory
730
+ - Feature seeds without a `db:seed` package script
731
+ - Upload definitions without an upload route
732
+ - Upload definitions without explicit size limits
733
+ - Devtools routes that are explicitly enabled without authorization
734
+ - Cron routes that do not use `CRON_SECRET`
735
+ - Installed provider packages without expected environment configuration
736
+ - Better Auth providers without an auth route or trusted-origin configuration
737
+ - Credentialed wildcard CORS configuration
738
+ - Notification dispatchers that bypass `ctx.ports.notifications`
739
+ - Feature artifacts in non-canonical folders
740
+
741
+ Provider package diagnostics come from package-owned `beignet.provider`
742
+ metadata in installed package manifests. The CLI does not import provider
743
+ implementation modules to discover those facts, and it reports malformed
744
+ metadata before using provider-derived doctor rules.
745
+
746
+ Use `beignet doctor --strict` for CI-oriented checks that may be noisy locally,
747
+ such as missing generated feature tests, canonical app conformance drift, and
748
+ feature-owned route errors that appear unused. Strict mode fails on warnings as
749
+ well as errors; informational hints never affect the exit code, so an
750
+ installed-but-unregistered optional provider such as `@beignet/devtools` still
751
+ passes `doctor --strict`. Use
752
+ `beignet doctor --json` in CI or custom scripts. Doctor JSON includes
753
+ `schemaVersion: 1` plus `targetDir`, `config`, `strict`, `convention`,
754
+ `contracts`, `routes`, `diagnostics`, and `fixes`.
365
755
 
366
756
  Use `beignet doctor --fix` for low-risk maintenance fixes. Today it can add
367
- a missing `test` script and repair direct `createOpenAPIHandler([...])` arrays
368
- when the missing contracts are already imported in the OpenAPI route.
757
+ a missing `test` script, register existing feature route groups in the central
758
+ route list, and repair direct `createOpenAPIHandler([...])` arrays when the
759
+ missing contracts are already imported in the OpenAPI route.
369
760
 
370
761
  Available presets:
371
762
 
372
763
  - `minimal` - choose this when you want the small contract/core/server/use-case loop without the full app conventions.
373
- - `standard` - choose this when you want the full app layout with app errors, env validation, devtools, provider wiring, health checks, and separated `ports/` + `infra/`.
764
+ - `standard` - choose this when you want the full app layout with Better Auth, Drizzle/libSQL, UOW, durable idempotency, outbox, audit logging, devtools, provider wiring, health checks, typed clients, feature-owned jobs/events/schedules/uploads/notifications, and separated `ports/` + `infra/`.
374
765
 
375
- Integrations are orthogonal to presets. Add only the services you want:
766
+ The standard preset includes the local production-shaped defaults: Better Auth,
767
+ Drizzle/libSQL, Pino logging, local storage, devtools, UOW, durable idempotency, outbox, audit
768
+ logging, and feature-owned background workflow examples. Add external service
769
+ integrations only when you want those providers:
376
770
 
377
771
  ```bash
378
- bunx -p @beignet/cli beignet create my-app --preset standard --integrations drizzle-turso,inngest,resend
772
+ bun create beignet my-app --preset standard --integrations drizzle-turso,inngest,resend
379
773
  ```
380
774
 
381
775
  Available features:
@@ -394,15 +788,28 @@ Available integrations:
394
788
  - `resend`
395
789
  - `upstash-rate-limit`
396
790
 
397
- With `--preset standard`, `drizzle-turso` also scaffolds `infra/db/schema/`, `infra/db/repositories.ts`, a Drizzle todo repository adapter, `drizzle.config.ts`, database scripts, and `TURSO_*` env examples so the generated todo resource uses durable persistence instead of the in-memory repository. Later `make resource` calls in that app generate both a Drizzle table/repository and an in-memory test fake.
398
-
399
- With `--preset standard`, the starter includes a provider-neutral `AuthPort`,
400
- anonymous auth adapter, typed `UNAUTHORIZED` and `FORBIDDEN` errors, a
401
- request-bound authorization gate, and `requireUser(ctx)`. Use hooks for HTTP
402
- boundary authentication and use cases or policies for business authorization.
403
- The `better-auth` integration adds dependencies and setup notes, then you
404
- replace the anonymous adapter once your app has a real Better Auth
405
- session/database setup.
791
+ With `--preset standard`, Drizzle/libSQL is included by default. The starter scaffolds `infra/db/schema/`, `infra/db/repositories.ts`, `infra/db/test-database.ts`, SQL-backed idempotency and outbox tables, a Drizzle todo repository adapter, `drizzle.config.ts`, database scripts, and `TURSO_*` env examples so the generated todo resource uses durable persistence instead of the in-memory repository. Later `make feature` or `make resource` calls in that app generate both a Drizzle table/repository and an in-memory test fake.
792
+
793
+ With `--preset minimal --integrations drizzle-turso`, the starter keeps the
794
+ small app shape and scaffolds the smallest durable wiring: `drizzle.config.ts`,
795
+ a todos-only schema in `infra/db/schema/`, a Drizzle todo repository in
796
+ `infra/todos/`, and an app-owned database provider in `infra/db/provider.ts`
797
+ that contributes the deferred `todos` port and creates and seeds the starter
798
+ table at startup. The generated `db:generate` and `db:migrate` scripts work
799
+ with `beignet db ...`; the `db:seed` and `db:reset` scripts are
800
+ standard-preset-only because their entrypoints are not scaffolded.
801
+
802
+ With `--preset standard`, Better Auth is included by default. The starter adds
803
+ the Better Auth Next.js route, Beignet auth provider wiring, typed
804
+ `UNAUTHORIZED` and `FORBIDDEN` errors, a request-bound authorization gate, and
805
+ `requireUser(ctx)`. Use hooks for HTTP boundary authentication and use cases or
806
+ policies for business authorization.
807
+
808
+ With `--preset minimal --integrations better-auth`, the starter wires an
809
+ app-owned `lib/better-auth.ts` backed by Better Auth's in-memory adapter, the
810
+ `app/api/auth/[...all]` route, `createAuthBetterAuthProvider(...)` behind a
811
+ deferred `auth` port, and an `auth` session on the request context. Users and
812
+ sessions reset on restart; switch to a database adapter before production.
406
813
 
407
814
  ## License
408
815