@beignet/cli 0.0.38 → 0.0.40
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/CHANGELOG.md +18 -0
- package/README.md +154 -102
- package/dist/choices.d.ts +17 -21
- package/dist/choices.d.ts.map +1 -1
- package/dist/choices.js +21 -67
- package/dist/choices.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -2
- package/dist/config.js.map +1 -1
- package/dist/create-prompts.d.ts +5 -6
- package/dist/create-prompts.d.ts.map +1 -1
- package/dist/create-prompts.js +15 -15
- package/dist/create-prompts.js.map +1 -1
- package/dist/create.d.ts +11 -2
- package/dist/create.d.ts.map +1 -1
- package/dist/create.js +38 -28
- package/dist/create.js.map +1 -1
- package/dist/framework.d.ts +3 -0
- package/dist/framework.d.ts.map +1 -0
- package/dist/framework.js +6 -0
- package/dist/framework.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +149 -47
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +4 -2
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +174 -34
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/make/inbox.js +3 -3
- package/dist/make/inbox.js.map +1 -1
- package/dist/make/payments.d.ts.map +1 -1
- package/dist/make/payments.js +5 -3
- package/dist/make/payments.js.map +1 -1
- package/dist/make/shared.d.ts +10 -3
- package/dist/make/shared.d.ts.map +1 -1
- package/dist/make/shared.js +7 -8
- package/dist/make/shared.js.map +1 -1
- package/dist/make.d.ts +3 -2
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +549 -186
- package/dist/make.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +15 -11
- package/dist/mcp.js.map +1 -1
- package/dist/provider-add.d.ts +21 -3
- package/dist/provider-add.d.ts.map +1 -1
- package/dist/provider-add.js +101 -65
- package/dist/provider-add.js.map +1 -1
- package/dist/task.js +1 -1
- package/dist/task.js.map +1 -1
- package/dist/templates/base.d.ts +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +15 -15
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/index.d.ts +2 -2
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +5 -5
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +17 -13
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shadcn.js +1 -1
- package/dist/templates/shared.d.ts +4 -4
- package/dist/templates/shared.d.ts.map +1 -1
- package/dist/templates/shared.js +6 -6
- package/dist/templates/shared.js.map +1 -1
- package/package.json +2 -2
- package/skills/app-structure/SKILL.md +29 -9
- package/src/choices.ts +38 -88
- package/src/config.ts +7 -3
- package/src/create-prompts.ts +20 -21
- package/src/create.ts +54 -36
- package/src/framework.ts +13 -0
- package/src/index.ts +193 -65
- package/src/inspect.ts +290 -39
- package/src/lib.ts +1 -1
- package/src/make/inbox.ts +3 -3
- package/src/make/payments.ts +10 -3
- package/src/make/shared.ts +16 -10
- package/src/make.ts +720 -176
- package/src/mcp.ts +20 -13
- package/src/provider-add.ts +159 -79
- package/src/task.ts +1 -1
- package/src/templates/base.ts +16 -16
- package/src/templates/index.ts +6 -6
- package/src/templates/server.ts +17 -13
- package/src/templates/shadcn.ts +1 -1
- package/src/templates/shared.ts +10 -10
- package/src/test-helpers/generated-app.ts +2 -1
|
@@ -34,19 +34,25 @@ Use:
|
|
|
34
34
|
```bash
|
|
35
35
|
beignet make feature projects
|
|
36
36
|
beignet make feature projects --recipe full-slice
|
|
37
|
-
beignet make resource projects
|
|
38
|
-
beignet make event posts
|
|
39
|
-
beignet make listener posts
|
|
40
|
-
beignet make job billing
|
|
41
|
-
beignet make schedule billing
|
|
42
|
-
beignet make task admin
|
|
43
|
-
beignet make seed demo
|
|
44
|
-
beignet make
|
|
37
|
+
beignet make resource projects --authorization --tenant-scoped
|
|
38
|
+
beignet make event posts.published
|
|
39
|
+
beignet make listener posts.enqueue-published-email --event posts.published
|
|
40
|
+
beignet make job billing.sync
|
|
41
|
+
beignet make schedule billing.nightly --cron "0 2 * * *"
|
|
42
|
+
beignet make task admin.backfill
|
|
43
|
+
beignet make seed demo.users
|
|
44
|
+
beignet make outbox
|
|
45
|
+
beignet make upload issues.attachment
|
|
46
|
+
beignet make upload issues.attachment --ui
|
|
45
47
|
```
|
|
46
48
|
|
|
47
49
|
Generators place files under the owning feature, update central registries
|
|
48
50
|
when possible, and keep `doctor` clean.
|
|
49
51
|
|
|
52
|
+
Use `make upload --ui` in full-stack apps when the upload should include a
|
|
53
|
+
shared client/server constraint manifest, typed React upload client, uploader,
|
|
54
|
+
and component test. Omit `--ui` for API-only or backend-only workflows.
|
|
55
|
+
|
|
50
56
|
Use the `full-slice` recipe when you need a richer reference feature with
|
|
51
57
|
policy, client helpers, workflow artifacts, events, listener registration,
|
|
52
58
|
jobs, schedules, notifications, uploads, seeds, and outbox wiring. After
|
|
@@ -70,6 +76,12 @@ Hand-written files do not run until registered:
|
|
|
70
76
|
every job worker or outbox registry that can receive it
|
|
71
77
|
- seeds -> the app-owned seed entrypoint, usually `server/seed.ts`
|
|
72
78
|
|
|
79
|
+
For generated Next.js 15.1+ apps, `make outbox` also adds a server-local
|
|
80
|
+
provider that wraps the database Unit of Work with a push-assisted `after()`
|
|
81
|
+
trigger. Keep the generated cron route: it is the recovery path for missed
|
|
82
|
+
callbacks, delayed messages, retries, and additional batches. Older Next.js
|
|
83
|
+
apps remain cron-only.
|
|
84
|
+
|
|
73
85
|
When an app opts into runtime integrity, keep `server/runtime-integrity.ts`
|
|
74
86
|
aligned with those central registries. The boot check is pure and
|
|
75
87
|
serverless-safe: it compares declared listeners, schedules, tasks, and outbox
|
|
@@ -84,7 +96,11 @@ low-risk route-group, schedule, task, outbox, and listener registry repair.
|
|
|
84
96
|
|
|
85
97
|
## Path Config
|
|
86
98
|
|
|
87
|
-
`beignet.config.*` moves paths; it
|
|
99
|
+
`beignet.config.*` selects the inspected server adapter and moves paths; it
|
|
100
|
+
does not change the application architecture. `framework: "next"` is the
|
|
101
|
+
default. Use `framework: "web"` for `@beignet/web` Fetch servers so `routes`
|
|
102
|
+
and `doctor` inspect the central `defineRoutes(...)` registry instead of
|
|
103
|
+
requiring Next.js `app/api/` handlers.
|
|
88
104
|
|
|
89
105
|
Common `src/` apps configure:
|
|
90
106
|
|
|
@@ -102,6 +118,10 @@ export default defineConfig({
|
|
|
102
118
|
});
|
|
103
119
|
```
|
|
104
120
|
|
|
121
|
+
Framework-neutral generators work in either profile. `make payments`,
|
|
122
|
+
`make upload`, `make outbox`, and `make schedule --route` still generate
|
|
123
|
+
Next.js handlers and reject the web profile before writing files.
|
|
124
|
+
|
|
105
125
|
When `appContext` moves under `src/`, root client setup follows that source
|
|
106
126
|
root at `src/client/index.ts`. Other registries and app-bound builders are not
|
|
107
127
|
relocated implicitly; override each path the app has moved, such as
|
package/src/choices.ts
CHANGED
|
@@ -14,18 +14,21 @@ export type PackageManager = "bun" | "npm" | "pnpm" | "yarn";
|
|
|
14
14
|
*/
|
|
15
15
|
export type TemplateName = "next";
|
|
16
16
|
/**
|
|
17
|
-
* Optional
|
|
17
|
+
* Optional providers rendered directly by the starter template.
|
|
18
18
|
*
|
|
19
19
|
* Better Auth, Drizzle/libSQL, and pino ship in every starter, so only
|
|
20
|
-
*
|
|
20
|
+
* providers that add an external service remain selectable.
|
|
21
21
|
*/
|
|
22
|
-
export type
|
|
22
|
+
export type StarterProviderName =
|
|
23
|
+
| "jobs-inngest"
|
|
24
|
+
| "mail-resend"
|
|
25
|
+
| "rate-limit-upstash";
|
|
23
26
|
/**
|
|
24
27
|
* Databases supported by the starter's Drizzle persistence layer.
|
|
25
28
|
*/
|
|
26
29
|
export type DatabaseName = "sqlite" | "postgres" | "mysql";
|
|
27
30
|
/**
|
|
28
|
-
* Provider setup presets supported by `beignet
|
|
31
|
+
* Provider setup presets supported by `beignet provider add`.
|
|
29
32
|
*/
|
|
30
33
|
export type ProviderPresetName =
|
|
31
34
|
| "flags-openfeature"
|
|
@@ -35,13 +38,13 @@ export type ProviderPresetName =
|
|
|
35
38
|
| "mail-smtp"
|
|
36
39
|
| "payments-stripe"
|
|
37
40
|
| "search-meilisearch"
|
|
38
|
-
| "sentry"
|
|
39
|
-
| "
|
|
40
|
-
| "redis
|
|
41
|
+
| "error-reporting-sentry"
|
|
42
|
+
| "rate-limit-upstash"
|
|
43
|
+
| "cache-redis"
|
|
41
44
|
| "event-bus-redis"
|
|
42
|
-
| "redis
|
|
43
|
-
| "s3
|
|
44
|
-
| "vercel-blob
|
|
45
|
+
| "locks-redis"
|
|
46
|
+
| "storage-s3"
|
|
47
|
+
| "storage-vercel-blob";
|
|
45
48
|
|
|
46
49
|
/**
|
|
47
50
|
* Supported scaffold template choices.
|
|
@@ -61,13 +64,13 @@ export const packageManagerChoices = [
|
|
|
61
64
|
] as const satisfies readonly PackageManager[];
|
|
62
65
|
|
|
63
66
|
/**
|
|
64
|
-
* Supported
|
|
67
|
+
* Supported providers rendered directly by the starter.
|
|
65
68
|
*/
|
|
66
|
-
export const
|
|
67
|
-
"inngest",
|
|
68
|
-
"resend",
|
|
69
|
-
"
|
|
70
|
-
] as const satisfies readonly
|
|
69
|
+
export const starterProviderChoices = [
|
|
70
|
+
"jobs-inngest",
|
|
71
|
+
"mail-resend",
|
|
72
|
+
"rate-limit-upstash",
|
|
73
|
+
] as const satisfies readonly StarterProviderName[];
|
|
71
74
|
|
|
72
75
|
/**
|
|
73
76
|
* Supported scaffold database choices.
|
|
@@ -89,89 +92,45 @@ export const providerPresetChoices = [
|
|
|
89
92
|
"mail-smtp",
|
|
90
93
|
"payments-stripe",
|
|
91
94
|
"search-meilisearch",
|
|
92
|
-
"sentry",
|
|
93
|
-
"
|
|
94
|
-
"redis
|
|
95
|
+
"error-reporting-sentry",
|
|
96
|
+
"rate-limit-upstash",
|
|
97
|
+
"cache-redis",
|
|
95
98
|
"event-bus-redis",
|
|
96
|
-
"redis
|
|
97
|
-
"s3
|
|
98
|
-
"vercel-blob
|
|
99
|
+
"locks-redis",
|
|
100
|
+
"storage-s3",
|
|
101
|
+
"storage-vercel-blob",
|
|
99
102
|
] as const satisfies readonly ProviderPresetName[];
|
|
100
103
|
|
|
101
104
|
/**
|
|
102
|
-
* Values accepted by `beignet create --
|
|
103
|
-
* integrations plus the full `beignet providers add` preset catalog.
|
|
105
|
+
* Values accepted by `beignet create --providers`.
|
|
104
106
|
*/
|
|
105
|
-
export type
|
|
107
|
+
export type CreateProviderName = ProviderPresetName;
|
|
106
108
|
|
|
107
109
|
/**
|
|
108
|
-
* Supported `beignet create --
|
|
109
|
-
* come first; the rest of the provider preset catalog follows.
|
|
110
|
+
* Supported `beignet create --providers` choices.
|
|
110
111
|
*/
|
|
111
|
-
export const
|
|
112
|
-
"inngest",
|
|
113
|
-
"resend",
|
|
114
|
-
"upstash-rate-limit",
|
|
115
|
-
"flags-openfeature",
|
|
116
|
-
"jobs-bullmq",
|
|
117
|
-
"jobs-inngest",
|
|
118
|
-
"mail-resend",
|
|
119
|
-
"mail-smtp",
|
|
120
|
-
"payments-stripe",
|
|
121
|
-
"redis-cache",
|
|
122
|
-
"event-bus-redis",
|
|
123
|
-
"redis-locks",
|
|
124
|
-
"s3-storage",
|
|
125
|
-
"search-meilisearch",
|
|
126
|
-
"sentry",
|
|
127
|
-
"vercel-blob-storage",
|
|
128
|
-
] as const satisfies readonly CreateIntegrationName[];
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Provider presets whose create-time wiring already ships as a starter
|
|
132
|
-
* template integration. Selecting the preset name at create time scaffolds
|
|
133
|
-
* the equivalent template integration, so preset and integration stay
|
|
134
|
-
* consistent and `beignet providers add` remains a no-op afterwards.
|
|
135
|
-
*/
|
|
136
|
-
const presetTemplateIntegrationAliases: Partial<
|
|
137
|
-
Record<ProviderPresetName, IntegrationName>
|
|
138
|
-
> = {
|
|
139
|
-
"jobs-inngest": "inngest",
|
|
140
|
-
"mail-resend": "resend",
|
|
141
|
-
"upstash-rate-limit": "upstash-rate-limit",
|
|
142
|
-
};
|
|
112
|
+
export const createProviderChoices = providerPresetChoices;
|
|
143
113
|
|
|
144
114
|
/**
|
|
145
|
-
* Split `--
|
|
146
|
-
*
|
|
147
|
-
* the scaffold is written. Preset names with a template equivalent are
|
|
148
|
-
* normalized to the template integration; duplicates collapse.
|
|
115
|
+
* Split `--providers` selections into providers rendered directly by the
|
|
116
|
+
* starter and presets applied after the scaffold is written.
|
|
149
117
|
*/
|
|
150
|
-
export function
|
|
151
|
-
|
|
152
|
-
): {
|
|
153
|
-
integrations: IntegrationName[];
|
|
118
|
+
export function splitCreateProviders(values: readonly CreateProviderName[]): {
|
|
119
|
+
starterProviders: StarterProviderName[];
|
|
154
120
|
presets: ProviderPresetName[];
|
|
155
121
|
} {
|
|
156
|
-
const
|
|
122
|
+
const starterProviders = new Set<StarterProviderName>();
|
|
157
123
|
const presets = new Set<ProviderPresetName>();
|
|
158
124
|
|
|
159
125
|
for (const value of values) {
|
|
160
|
-
if ((
|
|
161
|
-
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const preset = value as ProviderPresetName;
|
|
166
|
-
const alias = presetTemplateIntegrationAliases[preset];
|
|
167
|
-
if (alias) {
|
|
168
|
-
integrations.add(alias);
|
|
126
|
+
if ((starterProviderChoices as readonly string[]).includes(value)) {
|
|
127
|
+
starterProviders.add(value as StarterProviderName);
|
|
169
128
|
} else {
|
|
170
|
-
presets.add(
|
|
129
|
+
presets.add(value);
|
|
171
130
|
}
|
|
172
131
|
}
|
|
173
132
|
|
|
174
|
-
return {
|
|
133
|
+
return { starterProviders: [...starterProviders], presets: [...presets] };
|
|
175
134
|
}
|
|
176
135
|
|
|
177
136
|
/**
|
|
@@ -246,24 +205,15 @@ export const completionShellChoices = [
|
|
|
246
205
|
export const makeFeatureAddonChoices = [
|
|
247
206
|
"policy",
|
|
248
207
|
"factory",
|
|
249
|
-
"factories",
|
|
250
208
|
"event",
|
|
251
|
-
"events",
|
|
252
209
|
"job",
|
|
253
|
-
"jobs",
|
|
254
210
|
"listener",
|
|
255
|
-
"listeners",
|
|
256
211
|
"notification",
|
|
257
|
-
"notifications",
|
|
258
212
|
"schedule",
|
|
259
|
-
"schedules",
|
|
260
213
|
"seed",
|
|
261
|
-
"seeds",
|
|
262
214
|
"task",
|
|
263
|
-
"tasks",
|
|
264
215
|
"ui",
|
|
265
216
|
"upload",
|
|
266
|
-
"uploads",
|
|
267
217
|
] as const;
|
|
268
218
|
|
|
269
219
|
/**
|
package/src/config.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { createJiti } from "jiti";
|
|
|
6
6
|
/**
|
|
7
7
|
* Supported application framework targets for Beignet config.
|
|
8
8
|
*/
|
|
9
|
-
export type BeignetFramework = "next";
|
|
9
|
+
export type BeignetFramework = "next" | "web";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Configurable app paths used by CLI generators, inspection, linting, and
|
|
@@ -306,11 +306,15 @@ function assertConfigObject(
|
|
|
306
306
|
throw new Error(`${configFile} must export a Beignet config object.`);
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
-
if (
|
|
309
|
+
if (
|
|
310
|
+
config.framework !== undefined &&
|
|
311
|
+
config.framework !== "next" &&
|
|
312
|
+
config.framework !== "web"
|
|
313
|
+
) {
|
|
310
314
|
throw new Error(
|
|
311
315
|
`${configFile} uses unsupported framework "${String(
|
|
312
316
|
config.framework,
|
|
313
|
-
)}". Supported
|
|
317
|
+
)}". Supported frameworks: "next", "web".`,
|
|
314
318
|
);
|
|
315
319
|
}
|
|
316
320
|
|
package/src/create-prompts.ts
CHANGED
|
@@ -7,26 +7,25 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
|
-
type
|
|
10
|
+
type CreateProviderName,
|
|
11
11
|
type DatabaseName,
|
|
12
12
|
databaseChoices,
|
|
13
|
-
type IntegrationName,
|
|
14
|
-
integrationChoices,
|
|
15
13
|
type PackageManager,
|
|
14
|
+
type StarterProviderName,
|
|
15
|
+
starterProviderChoices,
|
|
16
16
|
} from "./choices.js";
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Selections shared by create flags and interactive prompt answers.
|
|
20
20
|
*
|
|
21
|
-
* `
|
|
22
|
-
*
|
|
23
|
-
* curated template list and points at the wider catalog.
|
|
21
|
+
* `providers` accepts the full `beignet provider add` preset catalog; the
|
|
22
|
+
* interactive prompt shows the providers rendered directly by the starter.
|
|
24
23
|
*/
|
|
25
24
|
export type CreateSelections = {
|
|
26
25
|
directory?: string;
|
|
27
26
|
api?: boolean;
|
|
28
27
|
db?: DatabaseName;
|
|
29
|
-
|
|
28
|
+
providers?: readonly CreateProviderName[];
|
|
30
29
|
packageManager?: PackageManager;
|
|
31
30
|
};
|
|
32
31
|
|
|
@@ -43,7 +42,7 @@ type CreatePromptTty = {
|
|
|
43
42
|
* Decide whether `beignet create` should run the interactive prompt flow.
|
|
44
43
|
*
|
|
45
44
|
* Prompts run only on a full TTY, only when `--yes` is absent, and only when
|
|
46
|
-
* no selection flag was passed. Any explicit api, database,
|
|
45
|
+
* no selection flag was passed. Any explicit api, database, provider, or
|
|
47
46
|
* package manager selection means the invocation is scripted, so the
|
|
48
47
|
* non-interactive path stays byte-identical for agents and CI.
|
|
49
48
|
*/
|
|
@@ -57,16 +56,16 @@ export function shouldPromptInteractively(
|
|
|
57
56
|
const selectionFlagPassed =
|
|
58
57
|
flags.api !== undefined ||
|
|
59
58
|
flags.db !== undefined ||
|
|
60
|
-
(flags.
|
|
59
|
+
(flags.providers?.length ?? 0) > 0 ||
|
|
61
60
|
flags.packageManager !== undefined;
|
|
62
61
|
|
|
63
62
|
return !selectionFlagPassed;
|
|
64
63
|
}
|
|
65
64
|
|
|
66
|
-
const
|
|
67
|
-
inngest: "Inngest-backed background jobs",
|
|
68
|
-
resend: "Resend-backed mail provider",
|
|
69
|
-
"
|
|
65
|
+
const providerHints: Record<StarterProviderName, string> = {
|
|
66
|
+
"jobs-inngest": "Inngest-backed background jobs",
|
|
67
|
+
"mail-resend": "Resend-backed mail provider",
|
|
68
|
+
"rate-limit-upstash": "Upstash-backed rate limiting",
|
|
70
69
|
};
|
|
71
70
|
|
|
72
71
|
const databaseHints: Record<DatabaseName, string> = {
|
|
@@ -118,17 +117,17 @@ export async function promptCreateSelections(
|
|
|
118
117
|
});
|
|
119
118
|
if (prompts.isCancel(db)) return undefined;
|
|
120
119
|
|
|
121
|
-
const
|
|
120
|
+
const providers = await prompts.multiselect<StarterProviderName>({
|
|
122
121
|
message:
|
|
123
|
-
"Add
|
|
124
|
-
options:
|
|
125
|
-
value:
|
|
126
|
-
label:
|
|
127
|
-
hint:
|
|
122
|
+
"Add providers? (press enter to skip; the full catalog is available via --providers or beignet provider add)",
|
|
123
|
+
options: starterProviderChoices.map((provider) => ({
|
|
124
|
+
value: provider,
|
|
125
|
+
label: provider,
|
|
126
|
+
hint: providerHints[provider],
|
|
128
127
|
})),
|
|
129
128
|
required: false,
|
|
130
129
|
});
|
|
131
|
-
if (prompts.isCancel(
|
|
130
|
+
if (prompts.isCancel(providers)) return undefined;
|
|
132
131
|
|
|
133
132
|
prompts.outro(`Creating ${directory}`);
|
|
134
133
|
|
|
@@ -136,7 +135,7 @@ export async function promptCreateSelections(
|
|
|
136
135
|
directory,
|
|
137
136
|
api,
|
|
138
137
|
db,
|
|
139
|
-
|
|
138
|
+
providers,
|
|
140
139
|
packageManager: initial.packageManager,
|
|
141
140
|
};
|
|
142
141
|
}
|
package/src/create.ts
CHANGED
|
@@ -4,9 +4,10 @@ import {
|
|
|
4
4
|
type DatabaseName,
|
|
5
5
|
databaseChoices,
|
|
6
6
|
getTemplateFiles,
|
|
7
|
-
type IntegrationName,
|
|
8
|
-
integrationChoices,
|
|
9
7
|
type PackageManager,
|
|
8
|
+
type StarterProviderName,
|
|
9
|
+
starterProviderChoices,
|
|
10
|
+
type TemplateFile,
|
|
10
11
|
type TemplateName,
|
|
11
12
|
} from "./templates/index.js";
|
|
12
13
|
import { getCliVersion } from "./version.js";
|
|
@@ -23,7 +24,7 @@ export type CreateOptions = {
|
|
|
23
24
|
*/
|
|
24
25
|
api?: boolean;
|
|
25
26
|
packageManager?: PackageManager;
|
|
26
|
-
|
|
27
|
+
providers?: StarterProviderName[];
|
|
27
28
|
/**
|
|
28
29
|
* Database backing the starter's Drizzle persistence layer.
|
|
29
30
|
* Defaults to "sqlite".
|
|
@@ -47,19 +48,46 @@ export type CreateResult = {
|
|
|
47
48
|
database: DatabaseName;
|
|
48
49
|
};
|
|
49
50
|
|
|
51
|
+
export type CreateProjectPlan = {
|
|
52
|
+
result: CreateResult;
|
|
53
|
+
files: TemplateFile[];
|
|
54
|
+
};
|
|
55
|
+
|
|
50
56
|
/**
|
|
51
57
|
* Create a Beignet app from the starter template.
|
|
52
58
|
*/
|
|
53
59
|
export async function createProject(
|
|
54
60
|
options: CreateOptions,
|
|
55
61
|
): Promise<CreateResult> {
|
|
62
|
+
const plan = await planCreateProject(options);
|
|
63
|
+
|
|
64
|
+
if (!plan.result.dryRun) {
|
|
65
|
+
await mkdir(plan.result.targetDir, { recursive: true });
|
|
66
|
+
|
|
67
|
+
for (const file of plan.files) {
|
|
68
|
+
const destination = path.join(plan.result.targetDir, file.path);
|
|
69
|
+
await mkdir(path.dirname(destination), { recursive: true });
|
|
70
|
+
await writeFile(destination, file.content);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return plan.result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Render a scaffold without writing it so create-time provider presets can
|
|
79
|
+
* compose against the exact same file contents as a real create.
|
|
80
|
+
*/
|
|
81
|
+
export async function planCreateProject(
|
|
82
|
+
options: CreateOptions,
|
|
83
|
+
): Promise<CreateProjectPlan> {
|
|
56
84
|
const cwd = options.cwd ?? process.cwd();
|
|
57
85
|
const name = normalizeProjectName(options.name);
|
|
58
86
|
const targetDir = path.resolve(cwd, options.name);
|
|
59
87
|
const packageManager = options.packageManager ?? "bun";
|
|
60
88
|
const template = options.template ?? "next";
|
|
61
89
|
const api = Boolean(options.api);
|
|
62
|
-
const
|
|
90
|
+
const providers = resolveStarterProviders(options.providers ?? []);
|
|
63
91
|
const database = options.db ?? "sqlite";
|
|
64
92
|
|
|
65
93
|
if (template !== "next") {
|
|
@@ -83,58 +111,48 @@ export async function createProject(
|
|
|
83
111
|
packageManager,
|
|
84
112
|
beignetVersion: options.beignetVersion ?? getDefaultBeignetVersion(),
|
|
85
113
|
api,
|
|
86
|
-
|
|
114
|
+
providers,
|
|
87
115
|
database,
|
|
88
116
|
});
|
|
89
117
|
|
|
90
|
-
if (!dryRun) {
|
|
91
|
-
await mkdir(targetDir, { recursive: true });
|
|
92
|
-
|
|
93
|
-
for (const file of files) {
|
|
94
|
-
const destination = path.join(targetDir, file.path);
|
|
95
|
-
await mkdir(path.dirname(destination), { recursive: true });
|
|
96
|
-
await writeFile(destination, file.content);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
118
|
return {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
119
|
+
result: {
|
|
120
|
+
schemaVersion: 1,
|
|
121
|
+
name,
|
|
122
|
+
targetDir,
|
|
123
|
+
dryRun,
|
|
124
|
+
files: files.map((file) => file.path),
|
|
125
|
+
packageManager,
|
|
126
|
+
database,
|
|
127
|
+
},
|
|
128
|
+
files,
|
|
108
129
|
};
|
|
109
130
|
}
|
|
110
131
|
|
|
111
132
|
/**
|
|
112
|
-
* Providers that ship in every starter
|
|
113
|
-
* choices, so passing them gets a pointed error instead of a generic one.
|
|
133
|
+
* Providers that ship in every starter are not selectable additions.
|
|
114
134
|
*/
|
|
115
135
|
const baseProviderNames = new Set(["better-auth", "drizzle-sqlite", "pino"]);
|
|
116
136
|
|
|
117
|
-
function
|
|
118
|
-
|
|
119
|
-
):
|
|
120
|
-
for (const
|
|
121
|
-
const
|
|
122
|
-
if (baseProviderNames.has(
|
|
137
|
+
function resolveStarterProviders(
|
|
138
|
+
explicitProviders: StarterProviderName[],
|
|
139
|
+
): StarterProviderName[] {
|
|
140
|
+
for (const provider of explicitProviders) {
|
|
141
|
+
const providerName: string = provider;
|
|
142
|
+
if (baseProviderNames.has(providerName)) {
|
|
123
143
|
const databaseHint =
|
|
124
|
-
|
|
144
|
+
providerName === "drizzle-sqlite"
|
|
125
145
|
? " Choose the starter database with --db sqlite|postgres|mysql instead."
|
|
126
146
|
: "";
|
|
127
147
|
throw new Error(
|
|
128
|
-
`${
|
|
148
|
+
`${provider} is part of every Beignet starter and is not a --providers choice.${databaseHint}`,
|
|
129
149
|
);
|
|
130
150
|
}
|
|
131
151
|
}
|
|
132
152
|
|
|
133
|
-
const
|
|
153
|
+
const providers = new Set<StarterProviderName>(explicitProviders);
|
|
134
154
|
|
|
135
|
-
return
|
|
136
|
-
integrations.has(integration),
|
|
137
|
-
);
|
|
155
|
+
return starterProviderChoices.filter((provider) => providers.has(provider));
|
|
138
156
|
}
|
|
139
157
|
|
|
140
158
|
function getDefaultBeignetVersion(): string {
|
package/src/framework.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ResolvedBeignetConfig } from "./config.js";
|
|
2
|
+
|
|
3
|
+
export function assertNextFramework(
|
|
4
|
+
config: ResolvedBeignetConfig,
|
|
5
|
+
command: string,
|
|
6
|
+
generatedSurface: string,
|
|
7
|
+
): void {
|
|
8
|
+
if (config.framework === "next") return;
|
|
9
|
+
|
|
10
|
+
throw new Error(
|
|
11
|
+
`${command} currently supports framework: "next" because it generates ${generatedSurface}. For framework: "web", define the framework-neutral artifact directly and mount its HTTP entrypoint through @beignet/web.`,
|
|
12
|
+
);
|
|
13
|
+
}
|