@beignet/cli 0.0.26 → 0.0.28
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 +26 -0
- package/README.md +59 -15
- package/dist/check.d.ts +61 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +164 -0
- package/dist/check.js.map +1 -0
- package/dist/choices.d.ts +8 -0
- package/dist/choices.d.ts.map +1 -1
- package/dist/choices.js +14 -0
- package/dist/choices.js.map +1 -1
- package/dist/db.d.ts +8 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +4 -4
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +156 -21
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +121 -3
- package/dist/inspect.js.map +1 -1
- package/dist/make/inbox.d.ts +63 -0
- package/dist/make/inbox.d.ts.map +1 -0
- package/dist/make/inbox.js +1747 -0
- package/dist/make/inbox.js.map +1 -0
- package/dist/make/payments.d.ts +61 -0
- package/dist/make/payments.d.ts.map +1 -0
- package/dist/make/payments.js +1900 -0
- package/dist/make/payments.js.map +1 -0
- package/dist/make/shared.d.ts +592 -0
- package/dist/make/shared.d.ts.map +1 -0
- package/dist/make/shared.js +2233 -0
- package/dist/make/shared.js.map +1 -0
- package/dist/make/tenancy.d.ts +101 -0
- package/dist/make/tenancy.d.ts.map +1 -0
- package/dist/make/tenancy.js +4409 -0
- package/dist/make/tenancy.js.map +1 -0
- package/dist/make.d.ts +8 -44
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +57 -3689
- package/dist/make.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +45 -4
- package/dist/mcp.js.map +1 -1
- package/dist/provider-add.d.ts +19 -0
- package/dist/provider-add.d.ts.map +1 -0
- package/dist/provider-add.js +691 -0
- package/dist/provider-add.js.map +1 -0
- package/dist/provider-audit.d.ts.map +1 -1
- package/dist/provider-audit.js +77 -1
- package/dist/provider-audit.js.map +1 -1
- package/dist/registry-edits.d.ts +9 -0
- package/dist/registry-edits.d.ts.map +1 -1
- package/dist/registry-edits.js +32 -0
- package/dist/registry-edits.js.map +1 -1
- package/dist/task.d.ts +2 -0
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +2 -0
- package/dist/task.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +29 -11
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +42 -12
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/db/sqlite.d.ts.map +1 -1
- package/dist/templates/db/sqlite.js +7 -1
- package/dist/templates/db/sqlite.js.map +1 -1
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +44 -37
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shared.d.ts +5 -0
- package/dist/templates/shared.d.ts.map +1 -1
- package/dist/templates/shared.js +5 -0
- package/dist/templates/shared.js.map +1 -1
- package/package.json +2 -2
- package/skills/app-structure/SKILL.md +1 -1
- package/src/check.ts +246 -0
- package/src/choices.ts +28 -0
- package/src/db.ts +4 -4
- package/src/index.ts +232 -21
- package/src/inspect.ts +168 -2
- package/src/make/inbox.ts +2015 -0
- package/src/make/payments.ts +2182 -0
- package/src/make/shared.ts +3638 -0
- package/src/make/tenancy.ts +4809 -0
- package/src/make.ts +388 -5355
- package/src/mcp.ts +66 -3
- package/src/provider-add.ts +926 -0
- package/src/provider-audit.ts +95 -3
- package/src/registry-edits.ts +47 -0
- package/src/task.ts +8 -10
- package/src/templates/agents.ts +29 -11
- package/src/templates/base.ts +43 -12
- package/src/templates/db/sqlite.ts +7 -1
- package/src/templates/server.ts +44 -37
- package/src/templates/shared.ts +5 -0
- package/src/test-helpers/generated-app.ts +168 -0
package/src/check.ts
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createPainter } from "./ansi.js";
|
|
4
|
+
import { detectPackageManager, spawnCommand } from "./db.js";
|
|
5
|
+
import {
|
|
6
|
+
applyDoctorFixes,
|
|
7
|
+
formatDoctor,
|
|
8
|
+
type InspectFix,
|
|
9
|
+
inspectApp,
|
|
10
|
+
} from "./inspect.js";
|
|
11
|
+
import { formatLint, lintApp } from "./lint.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* One step of `beignet check`.
|
|
15
|
+
*
|
|
16
|
+
* `output` carries the failing step's formatted diagnostics or captured
|
|
17
|
+
* script output so the summary is actionable without a re-run.
|
|
18
|
+
*/
|
|
19
|
+
export type CheckStep = {
|
|
20
|
+
name: string;
|
|
21
|
+
command: string;
|
|
22
|
+
status: "passed" | "failed" | "skipped";
|
|
23
|
+
detail?: string;
|
|
24
|
+
output?: string;
|
|
25
|
+
durationMs: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type CheckAppResult = {
|
|
29
|
+
schemaVersion: 1;
|
|
30
|
+
targetDir: string;
|
|
31
|
+
runner: string;
|
|
32
|
+
ok: boolean;
|
|
33
|
+
fixes: InspectFix[];
|
|
34
|
+
steps: CheckStep[];
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type CheckAppOptions = {
|
|
38
|
+
cwd?: string;
|
|
39
|
+
fix?: boolean;
|
|
40
|
+
color?: boolean;
|
|
41
|
+
/** Called after each step completes, for incremental progress output. */
|
|
42
|
+
onStep?: (step: CheckStep) => void;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const scriptStepNames = ["lint", "typecheck", "test"] as const;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Run the whole app validation loop as one command: `beignet lint`,
|
|
49
|
+
* `beignet doctor --strict` (optionally with fixes), and the app's own
|
|
50
|
+
* `lint`, `typecheck`, and `test` package scripts through the detected
|
|
51
|
+
* package manager.
|
|
52
|
+
*
|
|
53
|
+
* Every step runs even when an earlier one fails, so a single run reports
|
|
54
|
+
* everything that needs fixing. Missing package scripts are reported as
|
|
55
|
+
* skipped, never as failures.
|
|
56
|
+
*/
|
|
57
|
+
export async function checkApp(
|
|
58
|
+
options: CheckAppOptions = {},
|
|
59
|
+
): Promise<CheckAppResult> {
|
|
60
|
+
const targetDir = path.resolve(options.cwd ?? process.cwd());
|
|
61
|
+
const runner = await detectPackageManager(targetDir);
|
|
62
|
+
const steps: CheckStep[] = [];
|
|
63
|
+
const record = (step: CheckStep): void => {
|
|
64
|
+
steps.push(step);
|
|
65
|
+
options.onStep?.(step);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
record(await lintStep(targetDir, options));
|
|
69
|
+
const { step: doctorStep, fixes } = await runDoctorStep(targetDir, options);
|
|
70
|
+
record(doctorStep);
|
|
71
|
+
|
|
72
|
+
const scripts = await readPackageScripts(targetDir);
|
|
73
|
+
for (const script of scriptStepNames) {
|
|
74
|
+
record(await scriptStep(targetDir, runner, script, scripts));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
schemaVersion: 1,
|
|
79
|
+
targetDir,
|
|
80
|
+
runner,
|
|
81
|
+
ok: steps.every((step) => step.status !== "failed"),
|
|
82
|
+
fixes,
|
|
83
|
+
steps,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Format a completed step as a single status line, with the failing step's
|
|
89
|
+
* output indented underneath.
|
|
90
|
+
*/
|
|
91
|
+
export function formatCheckStep(
|
|
92
|
+
step: CheckStep,
|
|
93
|
+
options: { color?: boolean } = {},
|
|
94
|
+
): string {
|
|
95
|
+
const paint = createPainter(options.color);
|
|
96
|
+
const mark =
|
|
97
|
+
step.status === "passed"
|
|
98
|
+
? paint("✓", "green")
|
|
99
|
+
: step.status === "failed"
|
|
100
|
+
? paint("✗", "red")
|
|
101
|
+
: paint("-", "cyan");
|
|
102
|
+
const detail = step.detail ? ` (${step.detail})` : "";
|
|
103
|
+
const line = `${mark} ${step.name}${detail}`;
|
|
104
|
+
if (step.status !== "failed" || !step.output?.trim()) return line;
|
|
105
|
+
|
|
106
|
+
const indented = step.output
|
|
107
|
+
.trimEnd()
|
|
108
|
+
.split("\n")
|
|
109
|
+
.map((outputLine) => ` ${outputLine}`)
|
|
110
|
+
.join("\n");
|
|
111
|
+
return `${line}\n${indented}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Format the closing summary line, e.g. `3 passed, 1 failed, 1 skipped`.
|
|
116
|
+
*/
|
|
117
|
+
export function formatCheckSummary(
|
|
118
|
+
result: CheckAppResult,
|
|
119
|
+
options: { color?: boolean } = {},
|
|
120
|
+
): string {
|
|
121
|
+
const paint = createPainter(options.color);
|
|
122
|
+
const counts = {
|
|
123
|
+
passed: result.steps.filter((step) => step.status === "passed").length,
|
|
124
|
+
failed: result.steps.filter((step) => step.status === "failed").length,
|
|
125
|
+
skipped: result.steps.filter((step) => step.status === "skipped").length,
|
|
126
|
+
};
|
|
127
|
+
const parts = [`${counts.passed} passed`];
|
|
128
|
+
if (counts.failed > 0) parts.push(paint(`${counts.failed} failed`, "red"));
|
|
129
|
+
if (counts.skipped > 0) parts.push(`${counts.skipped} skipped`);
|
|
130
|
+
return parts.join(", ");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Format a full result at once (JSON-free callers and tests).
|
|
135
|
+
*/
|
|
136
|
+
export function formatCheck(
|
|
137
|
+
result: CheckAppResult,
|
|
138
|
+
options: { color?: boolean } = {},
|
|
139
|
+
): string {
|
|
140
|
+
const lines = result.steps.map((step) => formatCheckStep(step, options));
|
|
141
|
+
return `${lines.join("\n")}\n\n${formatCheckSummary(result, options)}`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function lintStep(
|
|
145
|
+
targetDir: string,
|
|
146
|
+
options: CheckAppOptions,
|
|
147
|
+
): Promise<CheckStep> {
|
|
148
|
+
const startedAt = Date.now();
|
|
149
|
+
const result = await lintApp({ cwd: targetDir });
|
|
150
|
+
const failed = result.diagnostics.length > 0;
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
name: "beignet lint",
|
|
154
|
+
command: "beignet lint",
|
|
155
|
+
status: failed ? "failed" : "passed",
|
|
156
|
+
...(failed ? { output: formatLint(result, { color: options.color }) } : {}),
|
|
157
|
+
durationMs: Date.now() - startedAt,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function runDoctorStep(
|
|
162
|
+
targetDir: string,
|
|
163
|
+
options: CheckAppOptions,
|
|
164
|
+
): Promise<{ step: CheckStep; fixes: InspectFix[] }> {
|
|
165
|
+
const startedAt = Date.now();
|
|
166
|
+
const fixes = options.fix
|
|
167
|
+
? await applyDoctorFixes({ cwd: targetDir, strict: true })
|
|
168
|
+
: [];
|
|
169
|
+
const result = await inspectApp({ cwd: targetDir, strict: true });
|
|
170
|
+
result.fixes = fixes;
|
|
171
|
+
const failed = result.diagnostics.some(
|
|
172
|
+
(diagnostic) =>
|
|
173
|
+
diagnostic.severity === "error" || diagnostic.severity === "warning",
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
step: {
|
|
178
|
+
name: "beignet doctor --strict",
|
|
179
|
+
command: "beignet doctor --strict",
|
|
180
|
+
status: failed ? "failed" : "passed",
|
|
181
|
+
...(fixes.length > 0
|
|
182
|
+
? {
|
|
183
|
+
detail: `${fixes.length} ${fixes.length === 1 ? "fix" : "fixes"} applied`,
|
|
184
|
+
}
|
|
185
|
+
: {}),
|
|
186
|
+
...(failed
|
|
187
|
+
? { output: formatDoctor(result, { color: options.color }) }
|
|
188
|
+
: {}),
|
|
189
|
+
durationMs: Date.now() - startedAt,
|
|
190
|
+
},
|
|
191
|
+
fixes,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function scriptStep(
|
|
196
|
+
targetDir: string,
|
|
197
|
+
runner: string,
|
|
198
|
+
script: (typeof scriptStepNames)[number],
|
|
199
|
+
scripts: Record<string, string>,
|
|
200
|
+
): Promise<CheckStep> {
|
|
201
|
+
const command = `${runner} run ${script}`;
|
|
202
|
+
if (!scripts[script]) {
|
|
203
|
+
return {
|
|
204
|
+
name: script,
|
|
205
|
+
command,
|
|
206
|
+
status: "skipped",
|
|
207
|
+
detail: `no "${script}" script in package.json`,
|
|
208
|
+
durationMs: 0,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const startedAt = Date.now();
|
|
213
|
+
const result = await spawnCommand(runner, ["run", script], targetDir, {
|
|
214
|
+
captureOutput: true,
|
|
215
|
+
});
|
|
216
|
+
const failed = result.exitCode !== 0;
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
name: script,
|
|
220
|
+
command,
|
|
221
|
+
status: failed ? "failed" : "passed",
|
|
222
|
+
detail: command,
|
|
223
|
+
...(failed
|
|
224
|
+
? { output: [result.stdout, result.stderr].filter(Boolean).join("\n") }
|
|
225
|
+
: {}),
|
|
226
|
+
durationMs: Date.now() - startedAt,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function readPackageScripts(
|
|
231
|
+
targetDir: string,
|
|
232
|
+
): Promise<Record<string, string>> {
|
|
233
|
+
let source: string;
|
|
234
|
+
try {
|
|
235
|
+
source = await readFile(path.join(targetDir, "package.json"), "utf8");
|
|
236
|
+
} catch {
|
|
237
|
+
throw new Error(
|
|
238
|
+
"Could not find package.json. Run beignet check from the app root.",
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const parsed = JSON.parse(source) as {
|
|
243
|
+
scripts?: Record<string, string>;
|
|
244
|
+
};
|
|
245
|
+
return parsed.scripts ?? {};
|
|
246
|
+
}
|
package/src/choices.ts
CHANGED
|
@@ -24,6 +24,19 @@ export type IntegrationName = "inngest" | "resend" | "upstash-rate-limit";
|
|
|
24
24
|
* Databases supported by the starter's Drizzle persistence layer.
|
|
25
25
|
*/
|
|
26
26
|
export type DatabaseName = "sqlite" | "postgres" | "mysql";
|
|
27
|
+
/**
|
|
28
|
+
* Provider setup presets supported by `beignet providers add`.
|
|
29
|
+
*/
|
|
30
|
+
export type ProviderPresetName =
|
|
31
|
+
| "flags-openfeature"
|
|
32
|
+
| "mail-resend"
|
|
33
|
+
| "mail-smtp"
|
|
34
|
+
| "search-meilisearch"
|
|
35
|
+
| "sentry"
|
|
36
|
+
| "upstash-rate-limit"
|
|
37
|
+
| "redis-cache"
|
|
38
|
+
| "redis-locks"
|
|
39
|
+
| "s3-storage";
|
|
27
40
|
|
|
28
41
|
/**
|
|
29
42
|
* Supported scaffold template choices.
|
|
@@ -60,6 +73,21 @@ export const databaseChoices = [
|
|
|
60
73
|
"mysql",
|
|
61
74
|
] as const satisfies readonly DatabaseName[];
|
|
62
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Supported provider setup preset choices.
|
|
78
|
+
*/
|
|
79
|
+
export const providerPresetChoices = [
|
|
80
|
+
"flags-openfeature",
|
|
81
|
+
"mail-resend",
|
|
82
|
+
"mail-smtp",
|
|
83
|
+
"search-meilisearch",
|
|
84
|
+
"sentry",
|
|
85
|
+
"upstash-rate-limit",
|
|
86
|
+
"redis-cache",
|
|
87
|
+
"redis-locks",
|
|
88
|
+
"s3-storage",
|
|
89
|
+
] as const satisfies readonly ProviderPresetName[];
|
|
90
|
+
|
|
63
91
|
/**
|
|
64
92
|
* Local database name derived from the app name.
|
|
65
93
|
*
|
package/src/db.ts
CHANGED
|
@@ -212,7 +212,7 @@ async function assertDatabaseCommandPreflight(
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
const standardEntrypoints: Partial<Record<DatabaseCommand, string>> = {
|
|
215
|
-
seed: "
|
|
215
|
+
seed: "server/seed.ts",
|
|
216
216
|
reset: "infra/db/reset.ts",
|
|
217
217
|
};
|
|
218
218
|
const standardEntrypoint = standardEntrypoints[command];
|
|
@@ -463,7 +463,7 @@ function missingDatabaseScriptMessage(
|
|
|
463
463
|
}
|
|
464
464
|
|
|
465
465
|
const entrypoint =
|
|
466
|
-
command === "seed" ? "
|
|
466
|
+
command === "seed" ? "server/seed.ts" : "infra/db/reset.ts";
|
|
467
467
|
return `Missing package.json script "${script}". Standard Drizzle apps use "${script}": "bun ${entrypoint}" so beignet db ${command} can run the app-owned ${command} entrypoint.`;
|
|
468
468
|
}
|
|
469
469
|
|
|
@@ -482,7 +482,7 @@ async function readPackageJson(cwd: string): Promise<PackageJson> {
|
|
|
482
482
|
}
|
|
483
483
|
}
|
|
484
484
|
|
|
485
|
-
async function detectPackageManager(cwd: string): Promise<string> {
|
|
485
|
+
export async function detectPackageManager(cwd: string): Promise<string> {
|
|
486
486
|
if (await exists(path.join(cwd, "bun.lock"))) return "bun";
|
|
487
487
|
if (await exists(path.join(cwd, "bun.lockb"))) return "bun";
|
|
488
488
|
if (await exists(path.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
@@ -500,7 +500,7 @@ async function exists(filePath: string): Promise<boolean> {
|
|
|
500
500
|
}
|
|
501
501
|
}
|
|
502
502
|
|
|
503
|
-
function spawnCommand(
|
|
503
|
+
export function spawnCommand(
|
|
504
504
|
command: string,
|
|
505
505
|
args: readonly string[],
|
|
506
506
|
cwd: string,
|