@beignet/cli 0.0.21 → 0.0.23
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 +19 -0
- package/README.md +25 -3
- package/dist/choices.d.ts +8 -0
- package/dist/choices.d.ts.map +1 -1
- package/dist/choices.js +4 -0
- package/dist/choices.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/inspect.js +152 -0
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +2 -2
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +1 -1
- package/dist/lib.js.map +1 -1
- package/dist/make.d.ts +4 -3
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +333 -30
- package/dist/make.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +6 -2
- package/dist/mcp.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +9 -5
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +1 -0
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +1 -0
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts +1 -0
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +30 -0
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shell.d.ts.map +1 -1
- package/dist/templates/shell.js +63 -29
- package/dist/templates/shell.js.map +1 -1
- package/package.json +2 -2
- package/src/choices.ts +10 -0
- package/src/index.ts +11 -0
- package/src/inspect.ts +266 -0
- package/src/lib.ts +2 -1
- package/src/make.ts +433 -38
- package/src/mcp.ts +7 -1
- package/src/templates/agents.ts +9 -5
- package/src/templates/base.ts +1 -0
- package/src/templates/index.ts +1 -0
- package/src/templates/server.ts +30 -0
- package/src/templates/shell.ts +64 -29
package/src/inspect.ts
CHANGED
|
@@ -1894,6 +1894,23 @@ type ProviderPackageMetadataSource = {
|
|
|
1894
1894
|
metadata: unknown;
|
|
1895
1895
|
};
|
|
1896
1896
|
|
|
1897
|
+
const readinessRelevantProviderPackages = new Set([
|
|
1898
|
+
"@beignet/provider-auth-better-auth",
|
|
1899
|
+
"@beignet/provider-db-drizzle",
|
|
1900
|
+
"@beignet/provider-error-reporting-sentry",
|
|
1901
|
+
"@beignet/provider-flags-openfeature",
|
|
1902
|
+
"@beignet/provider-jobs-bullmq",
|
|
1903
|
+
"@beignet/provider-jobs-inngest",
|
|
1904
|
+
"@beignet/provider-locks-redis",
|
|
1905
|
+
"@beignet/provider-mail-resend",
|
|
1906
|
+
"@beignet/provider-mail-smtp",
|
|
1907
|
+
"@beignet/provider-payments-stripe",
|
|
1908
|
+
"@beignet/provider-rate-limit-upstash",
|
|
1909
|
+
"@beignet/provider-redis",
|
|
1910
|
+
"@beignet/provider-search-meilisearch",
|
|
1911
|
+
"@beignet/provider-storage-s3",
|
|
1912
|
+
]);
|
|
1913
|
+
|
|
1897
1914
|
async function inspectProductionReadiness(
|
|
1898
1915
|
targetDir: string,
|
|
1899
1916
|
files: string[],
|
|
@@ -2074,9 +2091,53 @@ async function inspectProductionReadiness(
|
|
|
2074
2091
|
}
|
|
2075
2092
|
}
|
|
2076
2093
|
|
|
2094
|
+
const installedReadinessProviders = [...installedPackages].filter(
|
|
2095
|
+
(packageName) => readinessRelevantProviderPackages.has(packageName),
|
|
2096
|
+
);
|
|
2097
|
+
if (
|
|
2098
|
+
installedReadinessProviders.length > 0 &&
|
|
2099
|
+
!(await hasReadinessCheck(targetDir, files, config, sourceCache))
|
|
2100
|
+
) {
|
|
2101
|
+
diagnostics.push({
|
|
2102
|
+
severity: "hint",
|
|
2103
|
+
code: "BEIGNET_READINESS_ROUTE_MISSING",
|
|
2104
|
+
file: directoryPath(config.paths.routes),
|
|
2105
|
+
message: `${installedReadinessProviders.join(", ")} ${installedReadinessProviders.length === 1 ? "is" : "are"} installed, but no app-owned readiness route or dependency health checks were found. Consider exposing /api/ready with bounded dependency checks before deploying provider-backed infrastructure.`,
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2077
2109
|
return diagnostics;
|
|
2078
2110
|
}
|
|
2079
2111
|
|
|
2112
|
+
async function hasReadinessCheck(
|
|
2113
|
+
targetDir: string,
|
|
2114
|
+
files: string[],
|
|
2115
|
+
config: ResolvedBeignetConfig,
|
|
2116
|
+
sourceCache: Map<string, string>,
|
|
2117
|
+
): Promise<boolean> {
|
|
2118
|
+
const routesDir = directoryPath(config.paths.routes);
|
|
2119
|
+
if (files.includes(`${routesDir}/ready/route.ts`)) return true;
|
|
2120
|
+
|
|
2121
|
+
for (const file of files) {
|
|
2122
|
+
if (!file.endsWith(".ts") || file.endsWith(".test.ts")) continue;
|
|
2123
|
+
if (!isRouteServerOrInfraFile(file, config)) continue;
|
|
2124
|
+
|
|
2125
|
+
const source = await readCachedSource(targetDir, file, sourceCache);
|
|
2126
|
+
if (containsReadinessCheck(source)) {
|
|
2127
|
+
return true;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
return false;
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
function containsReadinessCheck(source: string): boolean {
|
|
2135
|
+
if (containsCallExpression(source, "runHealthChecks")) return true;
|
|
2136
|
+
if (!containsCallExpression(source, "createHealthHandler")) return false;
|
|
2137
|
+
|
|
2138
|
+
return /\bchecks\s*:/.test(source) || /\.checkHealth\s*\(/.test(source);
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2080
2141
|
async function inspectEntitlementsProductionReadiness(
|
|
2081
2142
|
targetDir: string,
|
|
2082
2143
|
files: string[],
|
|
@@ -4030,6 +4091,7 @@ async function inspectDatabaseLifecycleDrift(
|
|
|
4030
4091
|
if (!convention.resourceGenerator) return [];
|
|
4031
4092
|
|
|
4032
4093
|
const diagnostics: InspectDiagnostic[] = [];
|
|
4094
|
+
const sourceCache = new Map<string, string>();
|
|
4033
4095
|
const resources = await collectResourceNames(targetDir, files, config);
|
|
4034
4096
|
const infrastructurePath = directoryPath(
|
|
4035
4097
|
path.dirname(config.paths.infrastructurePorts),
|
|
@@ -4088,6 +4150,17 @@ async function inspectDatabaseLifecycleDrift(
|
|
|
4088
4150
|
message: `This app has Drizzle database artifacts, but package.json does not define "${script}". Add an app-owned ${script} script so beignet db ${script.replace("db:", "")} can run the database lifecycle command.`,
|
|
4089
4151
|
});
|
|
4090
4152
|
}
|
|
4153
|
+
|
|
4154
|
+
diagnostics.push(
|
|
4155
|
+
...(await inspectDurableDrizzleTableDrift(
|
|
4156
|
+
targetDir,
|
|
4157
|
+
files,
|
|
4158
|
+
config,
|
|
4159
|
+
infrastructurePath,
|
|
4160
|
+
schemaDir,
|
|
4161
|
+
sourceCache,
|
|
4162
|
+
)),
|
|
4163
|
+
);
|
|
4091
4164
|
}
|
|
4092
4165
|
|
|
4093
4166
|
const schemaFilePath = `${infrastructurePath}/db/schema.ts`;
|
|
@@ -4244,6 +4317,199 @@ async function inspectDatabaseLifecycleDrift(
|
|
|
4244
4317
|
return diagnostics;
|
|
4245
4318
|
}
|
|
4246
4319
|
|
|
4320
|
+
type DurableDrizzleTableRequirement = {
|
|
4321
|
+
capability: string;
|
|
4322
|
+
defaultTableName: string;
|
|
4323
|
+
code: string;
|
|
4324
|
+
factoryPattern: string;
|
|
4325
|
+
setupPattern?: string;
|
|
4326
|
+
};
|
|
4327
|
+
|
|
4328
|
+
const durableDrizzleTableRequirements: readonly DurableDrizzleTableRequirement[] =
|
|
4329
|
+
[
|
|
4330
|
+
{
|
|
4331
|
+
capability: "idempotency",
|
|
4332
|
+
defaultTableName: "idempotency_records",
|
|
4333
|
+
code: "BEIGNET_IDEMPOTENCY_TABLE_MISSING",
|
|
4334
|
+
factoryPattern: "createDrizzle[A-Za-z]+IdempotencyPort",
|
|
4335
|
+
setupPattern: "createDrizzle[A-Za-z]+IdempotencySetupStatements",
|
|
4336
|
+
},
|
|
4337
|
+
{
|
|
4338
|
+
capability: "outbox",
|
|
4339
|
+
defaultTableName: "outbox_messages",
|
|
4340
|
+
code: "BEIGNET_OUTBOX_TABLE_MISSING",
|
|
4341
|
+
factoryPattern: "createDrizzle[A-Za-z]+OutboxPort",
|
|
4342
|
+
setupPattern: "createDrizzle[A-Za-z]+OutboxSetupStatements",
|
|
4343
|
+
},
|
|
4344
|
+
{
|
|
4345
|
+
capability: "audit",
|
|
4346
|
+
defaultTableName: "audit_log",
|
|
4347
|
+
code: "BEIGNET_AUDIT_TABLE_MISSING",
|
|
4348
|
+
factoryPattern: "createDrizzle(?:[A-Za-z]+AuditLogPort|AuditLog)",
|
|
4349
|
+
setupPattern: "createDrizzle[A-Za-z]+AuditLogSetupStatements",
|
|
4350
|
+
},
|
|
4351
|
+
];
|
|
4352
|
+
|
|
4353
|
+
async function inspectDurableDrizzleTableDrift(
|
|
4354
|
+
targetDir: string,
|
|
4355
|
+
files: string[],
|
|
4356
|
+
config: ResolvedBeignetConfig,
|
|
4357
|
+
infrastructurePath: string,
|
|
4358
|
+
schemaDir: string,
|
|
4359
|
+
sourceCache: Map<string, string>,
|
|
4360
|
+
): Promise<InspectDiagnostic[]> {
|
|
4361
|
+
const diagnostics: InspectDiagnostic[] = [];
|
|
4362
|
+
const usageFiles = durableDrizzleUsageFiles(files, config);
|
|
4363
|
+
|
|
4364
|
+
for (const requirement of durableDrizzleTableRequirements) {
|
|
4365
|
+
const tableNames = await configuredDurableDrizzleTableNames(
|
|
4366
|
+
targetDir,
|
|
4367
|
+
usageFiles,
|
|
4368
|
+
requirement,
|
|
4369
|
+
sourceCache,
|
|
4370
|
+
);
|
|
4371
|
+
if (tableNames.size === 0) continue;
|
|
4372
|
+
|
|
4373
|
+
for (const tableName of tableNames) {
|
|
4374
|
+
if (
|
|
4375
|
+
await durableDrizzleTableExists(
|
|
4376
|
+
targetDir,
|
|
4377
|
+
files,
|
|
4378
|
+
infrastructurePath,
|
|
4379
|
+
schemaDir,
|
|
4380
|
+
requirement,
|
|
4381
|
+
tableName,
|
|
4382
|
+
sourceCache,
|
|
4383
|
+
)
|
|
4384
|
+
) {
|
|
4385
|
+
continue;
|
|
4386
|
+
}
|
|
4387
|
+
|
|
4388
|
+
diagnostics.push({
|
|
4389
|
+
severity: "warning",
|
|
4390
|
+
code: requirement.code,
|
|
4391
|
+
file: schemaDir,
|
|
4392
|
+
message: `This app wires a Drizzle-backed ${requirement.capability} port, but no ${tableName} table setup was found in ${schemaDir}/, drizzle/, or app database setup files. Add the table to your Drizzle schema/migrations, call the provider setup statements, or remove the stale ${requirement.capability} wiring before deployment.`,
|
|
4393
|
+
});
|
|
4394
|
+
}
|
|
4395
|
+
}
|
|
4396
|
+
|
|
4397
|
+
return diagnostics;
|
|
4398
|
+
}
|
|
4399
|
+
|
|
4400
|
+
function durableDrizzleUsageFiles(
|
|
4401
|
+
files: string[],
|
|
4402
|
+
config: ResolvedBeignetConfig,
|
|
4403
|
+
): string[] {
|
|
4404
|
+
return files.filter(
|
|
4405
|
+
(file) =>
|
|
4406
|
+
/\.(?:ts|tsx|mts|cts)$/.test(file) &&
|
|
4407
|
+
!isTestSourceFile(file) &&
|
|
4408
|
+
isInfraOrServerFile(file, config),
|
|
4409
|
+
);
|
|
4410
|
+
}
|
|
4411
|
+
|
|
4412
|
+
async function configuredDurableDrizzleTableNames(
|
|
4413
|
+
targetDir: string,
|
|
4414
|
+
files: string[],
|
|
4415
|
+
requirement: DurableDrizzleTableRequirement,
|
|
4416
|
+
sourceCache: Map<string, string>,
|
|
4417
|
+
): Promise<Set<string>> {
|
|
4418
|
+
const tableNames = new Set<string>();
|
|
4419
|
+
|
|
4420
|
+
for (const file of files) {
|
|
4421
|
+
const source = await readCachedSource(targetDir, file, sourceCache);
|
|
4422
|
+
for (const tableName of tableNamesFromCalls(
|
|
4423
|
+
source,
|
|
4424
|
+
requirement.factoryPattern,
|
|
4425
|
+
requirement.defaultTableName,
|
|
4426
|
+
)) {
|
|
4427
|
+
tableNames.add(tableName);
|
|
4428
|
+
}
|
|
4429
|
+
}
|
|
4430
|
+
|
|
4431
|
+
return tableNames;
|
|
4432
|
+
}
|
|
4433
|
+
|
|
4434
|
+
async function durableDrizzleTableExists(
|
|
4435
|
+
targetDir: string,
|
|
4436
|
+
files: string[],
|
|
4437
|
+
infrastructurePath: string,
|
|
4438
|
+
schemaDir: string,
|
|
4439
|
+
requirement: DurableDrizzleTableRequirement,
|
|
4440
|
+
tableName: string,
|
|
4441
|
+
sourceCache: Map<string, string>,
|
|
4442
|
+
): Promise<boolean> {
|
|
4443
|
+
const schemaFiles = files.filter(
|
|
4444
|
+
(file) =>
|
|
4445
|
+
file.startsWith(`${schemaDir}/`) &&
|
|
4446
|
+
/\.(?:ts|mts|cts)$/.test(file) &&
|
|
4447
|
+
!isTestSourceFile(file),
|
|
4448
|
+
);
|
|
4449
|
+
for (const file of schemaFiles) {
|
|
4450
|
+
const source = await readCachedSource(targetDir, file, sourceCache);
|
|
4451
|
+
if (source.includes(tableName)) return true;
|
|
4452
|
+
}
|
|
4453
|
+
|
|
4454
|
+
const setupFiles = files.filter(
|
|
4455
|
+
(file) =>
|
|
4456
|
+
!isTestSourceFile(file) &&
|
|
4457
|
+
(file.startsWith("drizzle/") ||
|
|
4458
|
+
file.startsWith(`${infrastructurePath}/db/`)) &&
|
|
4459
|
+
/\.(?:ts|js|mts|mjs|cts|cjs|sql)$/.test(file),
|
|
4460
|
+
);
|
|
4461
|
+
for (const file of setupFiles) {
|
|
4462
|
+
const source = await readCachedSource(targetDir, file, sourceCache);
|
|
4463
|
+
if (sourceCreatesTable(source, tableName)) return true;
|
|
4464
|
+
|
|
4465
|
+
if (!requirement.setupPattern) continue;
|
|
4466
|
+
if (
|
|
4467
|
+
tableNamesFromCalls(
|
|
4468
|
+
source,
|
|
4469
|
+
requirement.setupPattern,
|
|
4470
|
+
requirement.defaultTableName,
|
|
4471
|
+
).has(tableName)
|
|
4472
|
+
) {
|
|
4473
|
+
return true;
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
|
|
4477
|
+
return false;
|
|
4478
|
+
}
|
|
4479
|
+
|
|
4480
|
+
function sourceCreatesTable(source: string, tableName: string): boolean {
|
|
4481
|
+
return new RegExp(
|
|
4482
|
+
`\\bCREATE\\s+TABLE\\b[\\s\\S]{0,200}${escapeRegExp(tableName)}\\b`,
|
|
4483
|
+
"i",
|
|
4484
|
+
).test(source);
|
|
4485
|
+
}
|
|
4486
|
+
|
|
4487
|
+
function tableNamesFromCalls(
|
|
4488
|
+
source: string,
|
|
4489
|
+
calleePattern: string,
|
|
4490
|
+
defaultTableName: string,
|
|
4491
|
+
): Set<string> {
|
|
4492
|
+
const tableNames = new Set<string>();
|
|
4493
|
+
const callPattern = new RegExp(
|
|
4494
|
+
`\\b${calleePattern}\\s*\\(([\\s\\S]{0,800}?)\\)`,
|
|
4495
|
+
"g",
|
|
4496
|
+
);
|
|
4497
|
+
|
|
4498
|
+
for (const match of source.matchAll(callPattern)) {
|
|
4499
|
+
const matchIndex = match.index ?? 0;
|
|
4500
|
+
const prefix = source.slice(Math.max(0, matchIndex - 32), matchIndex);
|
|
4501
|
+
if (/\bfunction\s+$/.test(prefix)) continue;
|
|
4502
|
+
|
|
4503
|
+
const argsSource = match[1] ?? "";
|
|
4504
|
+
const tableName = /\btableName\s*:\s*["'`]([^"'`]+)["'`]/.exec(
|
|
4505
|
+
argsSource,
|
|
4506
|
+
)?.[1];
|
|
4507
|
+
tableNames.add(tableName ?? defaultTableName);
|
|
4508
|
+
}
|
|
4509
|
+
|
|
4510
|
+
return tableNames;
|
|
4511
|
+
}
|
|
4512
|
+
|
|
4247
4513
|
function hasRepositoryAdapterFile(
|
|
4248
4514
|
files: string[],
|
|
4249
4515
|
infrastructureDir: string,
|
package/src/lib.ts
CHANGED
|
@@ -18,7 +18,7 @@ export {
|
|
|
18
18
|
inspectApp,
|
|
19
19
|
} from "./inspect.js";
|
|
20
20
|
export { formatLint, formatLintGithub, lintApp } from "./lint.js";
|
|
21
|
-
export type { MakeFeatureAddon } from "./make.js";
|
|
21
|
+
export type { MakeFeatureAddon, MakeFeatureRecipe } from "./make.js";
|
|
22
22
|
export {
|
|
23
23
|
makeAdapter,
|
|
24
24
|
makeContract,
|
|
@@ -26,6 +26,7 @@ export {
|
|
|
26
26
|
makeFactory,
|
|
27
27
|
makeFeature,
|
|
28
28
|
makeFeatureAddonChoices,
|
|
29
|
+
makeFeatureRecipeChoices,
|
|
29
30
|
makeJob,
|
|
30
31
|
makeListener,
|
|
31
32
|
makeNotification,
|