@beignet/cli 0.0.50 → 0.0.52
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 +32 -0
- package/README.md +64 -22
- package/dist/analysis/layers.d.ts.map +1 -1
- package/dist/analysis/layers.js +4 -2
- package/dist/analysis/layers.js.map +1 -1
- package/dist/analysis/port-wiring.d.ts.map +1 -1
- package/dist/analysis/port-wiring.js +2 -1
- package/dist/analysis/port-wiring.js.map +1 -1
- package/dist/analysis/source-files.d.ts +15 -0
- package/dist/analysis/source-files.d.ts.map +1 -0
- package/dist/analysis/source-files.js +52 -0
- package/dist/analysis/source-files.js.map +1 -0
- package/dist/analysis/workspace.d.ts.map +1 -1
- package/dist/analysis/workspace.js +21 -25
- package/dist/analysis/workspace.js.map +1 -1
- package/dist/app-map-changes.d.ts.map +1 -1
- package/dist/app-map-changes.js +5 -7
- package/dist/app-map-changes.js.map +1 -1
- package/dist/app-map.d.ts.map +1 -1
- package/dist/app-map.js +33 -30
- package/dist/app-map.js.map +1 -1
- package/dist/check.d.ts +12 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +92 -25
- package/dist/check.js.map +1 -1
- package/dist/git-changes.d.ts +17 -1
- package/dist/git-changes.d.ts.map +1 -1
- package/dist/git-changes.js +118 -84
- package/dist/git-changes.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -6
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +436 -112
- package/dist/inspect.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +3 -6
- package/dist/lint.js.map +1 -1
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +0 -2
- package/dist/make.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +23 -5
- package/dist/mcp.js.map +1 -1
- package/dist/operational-process.d.ts +1 -0
- package/dist/operational-process.d.ts.map +1 -1
- package/dist/operational-process.js.map +1 -1
- package/dist/operational-runner.js +1 -0
- package/dist/operational-runner.js.map +1 -1
- package/dist/outbox.d.ts +1 -0
- package/dist/outbox.d.ts.map +1 -1
- package/dist/outbox.js +58 -12
- package/dist/outbox.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +43 -15
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.js +5 -5
- package/dist/templates/db/mysql.d.ts.map +1 -1
- package/dist/templates/db/mysql.js +51 -22
- package/dist/templates/db/mysql.js.map +1 -1
- package/dist/templates/db/postgres.d.ts.map +1 -1
- package/dist/templates/db/postgres.js +60 -20
- package/dist/templates/db/postgres.js.map +1 -1
- package/dist/templates/db/sqlite.d.ts.map +1 -1
- package/dist/templates/db/sqlite.js +51 -23
- package/dist/templates/db/sqlite.js.map +1 -1
- package/dist/templates/shadcn.js +1 -1
- package/dist/templates/shadcn.js.map +1 -1
- package/dist/templates/shared.js +4 -4
- package/dist/templates/shared.js.map +1 -1
- package/dist/templates/testing.d.ts.map +1 -1
- package/dist/templates/testing.js +7 -19
- package/dist/templates/testing.js.map +1 -1
- package/dist/test-discovery.d.ts +9 -0
- package/dist/test-discovery.d.ts.map +1 -0
- package/dist/test-discovery.js +40 -0
- package/dist/test-discovery.js.map +1 -0
- package/package.json +7 -5
- package/skills/app-structure/SKILL.md +37 -10
- package/src/analysis/layers.ts +4 -2
- package/src/analysis/port-wiring.ts +2 -1
- package/src/analysis/source-files.ts +61 -0
- package/src/analysis/workspace.ts +27 -31
- package/src/app-map-changes.ts +10 -8
- package/src/app-map.ts +43 -32
- package/src/check.ts +115 -25
- package/src/git-changes.ts +218 -86
- package/src/index.ts +53 -8
- package/src/inspect.ts +627 -146
- package/src/lint.ts +6 -5
- package/src/make.ts +0 -2
- package/src/mcp.ts +34 -11
- package/src/operational-process.ts +1 -0
- package/src/operational-runner.ts +1 -0
- package/src/outbox.ts +63 -12
- package/src/templates/agents.ts +43 -15
- package/src/templates/base.ts +5 -5
- package/src/templates/db/mysql.ts +51 -22
- package/src/templates/db/postgres.ts +60 -20
- package/src/templates/db/sqlite.ts +51 -23
- package/src/templates/shadcn.ts +1 -1
- package/src/templates/shared.ts +4 -4
- package/src/templates/testing.ts +12 -19
- package/src/test-discovery.ts +53 -0
package/src/lint.ts
CHANGED
|
@@ -6,6 +6,10 @@ import {
|
|
|
6
6
|
classifySourcePath as classifyPath,
|
|
7
7
|
type SourceLayer,
|
|
8
8
|
} from "./analysis/layers.js";
|
|
9
|
+
import {
|
|
10
|
+
isAnalysisSourceFile,
|
|
11
|
+
stripSourceFileExtension,
|
|
12
|
+
} from "./analysis/source-files.js";
|
|
9
13
|
import {
|
|
10
14
|
type ModuleReference,
|
|
11
15
|
moduleReferences,
|
|
@@ -202,10 +206,7 @@ export function formatLintGithub(result: LintAppResult): string {
|
|
|
202
206
|
}
|
|
203
207
|
|
|
204
208
|
function lintSourceFiles(files: string[]): string[] {
|
|
205
|
-
return files.filter(
|
|
206
|
-
if (/\.d\.(c|m)?ts$/.test(file)) return false;
|
|
207
|
-
return /\.(c|m)?tsx?$/.test(file);
|
|
208
|
-
});
|
|
209
|
+
return files.filter(isAnalysisSourceFile);
|
|
209
210
|
}
|
|
210
211
|
|
|
211
212
|
async function parseImports(
|
|
@@ -237,7 +238,7 @@ function packageName(importPath: string): string {
|
|
|
237
238
|
}
|
|
238
239
|
|
|
239
240
|
function stripKnownExtension(filePath: string): string {
|
|
240
|
-
return filePath
|
|
241
|
+
return stripSourceFileExtension(filePath);
|
|
241
242
|
}
|
|
242
243
|
|
|
243
244
|
function lintImport(
|
package/src/make.ts
CHANGED
|
@@ -2225,7 +2225,6 @@ async function updateOutboxDrainProvider(
|
|
|
2225
2225
|
\t\t\tdefer: after,
|
|
2226
2226
|
\t\t\tcreateContext: () => createServiceContext(undefined),
|
|
2227
2227
|
\t\t\tregistry: async () => (await import("${outboxModule}")).outboxRegistry,
|
|
2228
|
-
\t\t\tbatchSize: 100,
|
|
2229
2228
|
\t\t});
|
|
2230
2229
|
|
|
2231
2230
|
\t\treturn {
|
|
@@ -5921,7 +5920,6 @@ export const { GET, POST } = createOutboxDrainRoute({
|
|
|
5921
5920
|
\tserver: getServer,
|
|
5922
5921
|
\tregistry: outboxRegistry,
|
|
5923
5922
|
\tsecret: env.CRON_SECRET,
|
|
5924
|
-
\tbatchSize: 100,
|
|
5925
5923
|
});
|
|
5926
5924
|
`;
|
|
5927
5925
|
}
|
package/src/mcp.ts
CHANGED
|
@@ -285,6 +285,7 @@ const outboxRunInputSchema = z.discriminatedUnion("operation", [
|
|
|
285
285
|
z.object({
|
|
286
286
|
operation: z.literal("drain"),
|
|
287
287
|
batchSize: z.number().int().positive().optional(),
|
|
288
|
+
concurrency: z.number().int().positive().optional(),
|
|
288
289
|
module: operationalModuleSchema,
|
|
289
290
|
timeoutMs: operationalTimeoutSchema,
|
|
290
291
|
}),
|
|
@@ -349,17 +350,36 @@ function jsonResult(value: unknown): McpToolResult {
|
|
|
349
350
|
};
|
|
350
351
|
}
|
|
351
352
|
|
|
352
|
-
function operationalJsonResult(
|
|
353
|
+
function operationalJsonResult(
|
|
354
|
+
value: unknown,
|
|
355
|
+
options: { isError?: boolean } = {},
|
|
356
|
+
): McpToolResult {
|
|
353
357
|
const formatted = JSON.stringify(value, null, 2);
|
|
354
358
|
const text =
|
|
355
359
|
Buffer.byteLength(formatted) <= defaultOperationalResultMaxBytes
|
|
356
360
|
? formatted
|
|
357
361
|
: JSON.stringify(value);
|
|
358
362
|
return {
|
|
363
|
+
...(options.isError ? { isError: true } : {}),
|
|
359
364
|
content: [{ type: "text", text }],
|
|
360
365
|
};
|
|
361
366
|
}
|
|
362
367
|
|
|
368
|
+
function isUncertainOutboxDrainReport(value: unknown): boolean {
|
|
369
|
+
if (typeof value !== "object" || value === null || !("result" in value)) {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
const result = value.result;
|
|
373
|
+
if (typeof result !== "object" || result === null) return false;
|
|
374
|
+
const settlementFailed =
|
|
375
|
+
"settlementFailed" in result ? result.settlementFailed : undefined;
|
|
376
|
+
const leaseLost = "leaseLost" in result ? result.leaseLost : undefined;
|
|
377
|
+
return (
|
|
378
|
+
(typeof settlementFailed === "number" && settlementFailed > 0) ||
|
|
379
|
+
(typeof leaseLost === "number" && leaseLost > 0)
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
363
383
|
function errorResult(error: unknown): McpToolResult {
|
|
364
384
|
return {
|
|
365
385
|
isError: true,
|
|
@@ -667,7 +687,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
667
687
|
"check",
|
|
668
688
|
{
|
|
669
689
|
description:
|
|
670
|
-
"Run the full Beignet app validation loop: Beignet lint, strict doctor, optional disconnected or connected preflight, and the app's lint, typecheck, and test scripts. Returns the same versioned JSON as beignet check --json, including every step, bounded failure output, durations, and overall ok status. Does not apply Beignet fixes, but app-owned scripts run with their normal side effects.",
|
|
690
|
+
"Run the full Beignet app validation loop: Beignet lint, strict doctor, optional disconnected or connected preflight, and the app's lint, typecheck, and test scripts. Returns the same versioned JSON as beignet check --json, including every step, bounded failure output, durations, source-state provenance, and overall ok status. An ending fingerprint that differs from the baseline makes the result stale and unsuccessful. Does not apply Beignet fixes, but app-owned scripts run with their normal side effects.",
|
|
671
691
|
inputSchema: {
|
|
672
692
|
preflight: z
|
|
673
693
|
.boolean()
|
|
@@ -972,7 +992,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
972
992
|
"outbox_run",
|
|
973
993
|
{
|
|
974
994
|
description:
|
|
975
|
-
"Run a state-changing outbox drain, requeue, purge, or prune operation and return exactly the matching versioned beignet outbox <operation> --json report. Purge and prune support dryRun. Runs in an isolated process with bounded structured output, cancellation, and a timeout.",
|
|
995
|
+
"Run a state-changing outbox drain, requeue, purge, or prune operation and return exactly the matching versioned beignet outbox <operation> --json report. Drain supports bounded unordered concurrency and returns an error-bearing report when settlement or lease state is uncertain. Purge and prune support dryRun. Runs in an isolated process with bounded structured output, cancellation, and a timeout.",
|
|
976
996
|
inputSchema: outboxRunInputSchema,
|
|
977
997
|
annotations: {
|
|
978
998
|
readOnlyHint: false,
|
|
@@ -988,6 +1008,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
988
1008
|
? {
|
|
989
1009
|
command: "outbox:drain" as const,
|
|
990
1010
|
batchSize: input.batchSize,
|
|
1011
|
+
concurrency: input.concurrency,
|
|
991
1012
|
modulePath: input.module,
|
|
992
1013
|
}
|
|
993
1014
|
: input.operation === "requeue"
|
|
@@ -1014,14 +1035,16 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
1014
1035
|
dryRun: input.dryRun,
|
|
1015
1036
|
modulePath: input.module,
|
|
1016
1037
|
};
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1038
|
+
const result = await runIsolatedOperationalCommand({
|
|
1039
|
+
cwd,
|
|
1040
|
+
request,
|
|
1041
|
+
signal: ctx.mcpReq.signal,
|
|
1042
|
+
timeoutMs: input.timeoutMs ?? defaultOperationalCommandTimeoutMs,
|
|
1043
|
+
});
|
|
1044
|
+
return operationalJsonResult(result, {
|
|
1045
|
+
isError:
|
|
1046
|
+
input.operation === "drain" && isUncertainOutboxDrainReport(result),
|
|
1047
|
+
});
|
|
1025
1048
|
}),
|
|
1026
1049
|
);
|
|
1027
1050
|
|
package/src/outbox.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
OutboxMessageStatus,
|
|
11
11
|
OutboxRegistry,
|
|
12
12
|
} from "@beignet/core/outbox";
|
|
13
|
+
import type { TracingPort } from "@beignet/core/tracing";
|
|
13
14
|
import { createJiti } from "jiti";
|
|
14
15
|
import { loadBeignetConfig, normalizePath } from "./config.js";
|
|
15
16
|
import {
|
|
@@ -26,6 +27,7 @@ export type RunOutboxDrainOptions = {
|
|
|
26
27
|
cwd?: string;
|
|
27
28
|
modulePath?: string;
|
|
28
29
|
batchSize?: number;
|
|
30
|
+
concurrency?: number;
|
|
29
31
|
};
|
|
30
32
|
|
|
31
33
|
/**
|
|
@@ -175,6 +177,7 @@ type OutboxDrainPorts = {
|
|
|
175
177
|
logger?: OutboxDrainLogger;
|
|
176
178
|
devtools?: OutboxDrainInstrumentation;
|
|
177
179
|
instrumentation?: OutboxDrainInstrumentation;
|
|
180
|
+
tracing?: TracingPort;
|
|
178
181
|
errorReporter?: ErrorReporterPort;
|
|
179
182
|
};
|
|
180
183
|
|
|
@@ -187,6 +190,7 @@ type OutboxDrainContext = {
|
|
|
187
190
|
type OutboxDrainContextArgs = {
|
|
188
191
|
registry: OutboxRegistry;
|
|
189
192
|
batchSize?: number;
|
|
193
|
+
concurrency?: number;
|
|
190
194
|
};
|
|
191
195
|
|
|
192
196
|
type OutboxAdminOperation = "list" | "show" | "requeue" | "purge" | "prune";
|
|
@@ -219,7 +223,11 @@ export async function runOutboxDrain(
|
|
|
219
223
|
const config = await loadBeignetConfig(cwd);
|
|
220
224
|
const modulePath = normalizePath(options.modulePath ?? config.paths.outbox);
|
|
221
225
|
const batchSize = options.batchSize;
|
|
226
|
+
const concurrency = options.concurrency;
|
|
222
227
|
if (batchSize !== undefined) assertPositiveInteger("batchSize", batchSize);
|
|
228
|
+
if (concurrency !== undefined) {
|
|
229
|
+
assertPositiveInteger("concurrency", concurrency);
|
|
230
|
+
}
|
|
223
231
|
|
|
224
232
|
const startedAt = performance.now();
|
|
225
233
|
const outboxModule = await loadOutboxModule(cwd, modulePath);
|
|
@@ -227,7 +235,7 @@ export async function runOutboxDrain(
|
|
|
227
235
|
outboxModule.outboxRegistry,
|
|
228
236
|
modulePath,
|
|
229
237
|
);
|
|
230
|
-
const contextArgs = { registry, batchSize };
|
|
238
|
+
const contextArgs = { registry, batchSize, concurrency };
|
|
231
239
|
const rawContext = outboxModule.createOutboxDrainContext
|
|
232
240
|
? await outboxModule.createOutboxDrainContext(contextArgs)
|
|
233
241
|
: undefined;
|
|
@@ -247,7 +255,11 @@ export async function runOutboxDrain(
|
|
|
247
255
|
eventBus: ctx.ports.eventBus,
|
|
248
256
|
jobs: ctx.ports.jobs,
|
|
249
257
|
batchSize,
|
|
250
|
-
|
|
258
|
+
concurrency,
|
|
259
|
+
instrumentation: {
|
|
260
|
+
instrumentation: ctx.ports.instrumentation ?? ctx.ports.devtools,
|
|
261
|
+
tracing: ctx.ports.tracing,
|
|
262
|
+
},
|
|
251
263
|
instrumentationContext: {
|
|
252
264
|
requestId: ctx.requestId,
|
|
253
265
|
traceId: ctx.traceId,
|
|
@@ -288,27 +300,65 @@ export async function runOutboxDrain(
|
|
|
288
300
|
},
|
|
289
301
|
});
|
|
290
302
|
},
|
|
291
|
-
|
|
303
|
+
onLeaseError(failure) {
|
|
304
|
+
const outcome =
|
|
305
|
+
failure.state === "lost"
|
|
306
|
+
? "leaseLost"
|
|
307
|
+
: failure.state === "recovered"
|
|
308
|
+
? "leaseRecovered"
|
|
309
|
+
: "leaseDegraded";
|
|
292
310
|
return reportOperationalFailure({
|
|
293
311
|
ctx,
|
|
294
|
-
error:
|
|
312
|
+
error: failure.error,
|
|
313
|
+
reportOptions: {
|
|
314
|
+
level: failure.state === "lost" ? "error" : "warning",
|
|
315
|
+
mechanism: "beignet.outbox.cli",
|
|
316
|
+
handled: failure.state !== "lost",
|
|
317
|
+
tags: {
|
|
318
|
+
"beignet.kind": "outbox",
|
|
319
|
+
"beignet.outbox.kind": failure.message.kind,
|
|
320
|
+
"beignet.outbox.name": failure.message.name,
|
|
321
|
+
"beignet.outbox.outcome": outcome,
|
|
322
|
+
},
|
|
323
|
+
contexts: {
|
|
324
|
+
outbox: {
|
|
325
|
+
messageId: failure.message.id,
|
|
326
|
+
kind: failure.message.kind,
|
|
327
|
+
name: failure.message.name,
|
|
328
|
+
attempts: failure.message.attempts,
|
|
329
|
+
maxAttempts: failure.message.maxAttempts,
|
|
330
|
+
operation: failure.operation,
|
|
331
|
+
state: failure.state,
|
|
332
|
+
confirmedLost: failure.confirmedLost,
|
|
333
|
+
outcome,
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
},
|
|
339
|
+
onSettlementError(failure) {
|
|
340
|
+
return reportOperationalFailure({
|
|
341
|
+
ctx,
|
|
342
|
+
error: failure.error,
|
|
295
343
|
reportOptions: {
|
|
296
344
|
level: "error",
|
|
297
345
|
mechanism: "beignet.outbox.cli",
|
|
298
346
|
handled: false,
|
|
299
347
|
tags: {
|
|
300
348
|
"beignet.kind": "outbox",
|
|
301
|
-
"beignet.outbox.kind": message.kind,
|
|
302
|
-
"beignet.outbox.name": message.name,
|
|
349
|
+
"beignet.outbox.kind": failure.message.kind,
|
|
350
|
+
"beignet.outbox.name": failure.message.name,
|
|
303
351
|
"beignet.outbox.outcome": "settlementFailed",
|
|
304
352
|
},
|
|
305
353
|
contexts: {
|
|
306
354
|
outbox: {
|
|
307
|
-
messageId: message.id,
|
|
308
|
-
kind: message.kind,
|
|
309
|
-
name: message.name,
|
|
310
|
-
attempts: message.attempts,
|
|
311
|
-
maxAttempts: message.maxAttempts,
|
|
355
|
+
messageId: failure.message.id,
|
|
356
|
+
kind: failure.message.kind,
|
|
357
|
+
name: failure.message.name,
|
|
358
|
+
attempts: failure.message.attempts,
|
|
359
|
+
maxAttempts: failure.message.maxAttempts,
|
|
360
|
+
operation: failure.operation,
|
|
361
|
+
deliverySucceeded: failure.deliverySucceeded,
|
|
312
362
|
outcome: "settlementFailed",
|
|
313
363
|
},
|
|
314
364
|
},
|
|
@@ -620,6 +670,7 @@ function isOutboxPort(value: unknown): value is DrainOutboxOptions["outbox"] {
|
|
|
620
670
|
isRecord(value) &&
|
|
621
671
|
typeof value.enqueue === "function" &&
|
|
622
672
|
typeof value.claimBatch === "function" &&
|
|
673
|
+
typeof value.renewClaim === "function" &&
|
|
623
674
|
typeof value.markDelivered === "function" &&
|
|
624
675
|
typeof value.markFailed === "function"
|
|
625
676
|
);
|
|
@@ -677,7 +728,7 @@ async function recordOutboxDrain(
|
|
|
677
728
|
watcher: "outbox",
|
|
678
729
|
name: "outbox.drain",
|
|
679
730
|
label: "Outbox drain",
|
|
680
|
-
summary: `${result.delivered} delivered, ${result.retried} retried, ${result.deadLettered} dead-lettered`,
|
|
731
|
+
summary: `${result.delivered} delivered, ${result.retried} retried, ${result.deadLettered} dead-lettered, ${result.settlementFailed} settlement-uncertain, ${result.leaseLost} lease-lost`,
|
|
681
732
|
requestId: ctx.requestId,
|
|
682
733
|
traceId: ctx.traceId,
|
|
683
734
|
details: result,
|
package/src/templates/agents.ts
CHANGED
|
@@ -41,8 +41,8 @@ https://beignetjs.com (agent-friendly index at https://beignetjs.com/llms.txt).
|
|
|
41
41
|
README.md covers setup and the app map; this file covers what is not
|
|
42
42
|
discoverable from the code.
|
|
43
43
|
|
|
44
|
-
This app requires Node.js 22.12 or newer. Bun
|
|
45
|
-
Beignet commands.
|
|
44
|
+
This app requires Node.js 22.12 or newer. Bun is optional; npm, pnpm, Yarn, and
|
|
45
|
+
Bun can run Beignet commands.
|
|
46
46
|
|
|
47
47
|
## Registration is explicit
|
|
48
48
|
|
|
@@ -71,6 +71,12 @@ is a silent failure — the file exists but never runs:
|
|
|
71
71
|
- Apps using runtime integrity must list workflow registries in
|
|
72
72
|
\`defineRuntimeManifest({...})\`.
|
|
73
73
|
|
|
74
|
+
Event schemas must output canonical JSON transport data. Use null, strings,
|
|
75
|
+
finite numbers, booleans, arrays, and plain objects. Keep validation
|
|
76
|
+
deterministic and side-effect-free, keep transforms idempotent, and represent
|
|
77
|
+
timestamps as strings or numbers rather than \`Date\` objects.
|
|
78
|
+
Beignet rejects payloads that would change when decoded and validated again.
|
|
79
|
+
|
|
74
80
|
The starter ships no workflow registries — generators create them on first
|
|
75
81
|
use, so their absence is fine. \`${cli} make event\`, \`${cli} make job\`,
|
|
76
82
|
\`${cli} make listener\`, \`${cli} make schedule\`, \`${cli} make task\`,
|
|
@@ -107,7 +113,9 @@ re-exports and \`db\` for \`generate\`, \`migrate\`, \`seed\`, or \`reset\`
|
|
|
107
113
|
instead of falling back to a shell. Use the read-only \`db_status\` tool before
|
|
108
114
|
deploying. Use \`task_run\`, \`schedule_run\`,
|
|
109
115
|
\`outbox_inspect\`, and \`outbox_run\` for registered operational workflows
|
|
110
|
-
and outbox recovery.
|
|
116
|
+
and outbox recovery. Treat a drain with nonzero \`settlementFailed\` or
|
|
117
|
+
\`leaseLost\` as an operational failure. Parallel delivery is opt-in and does
|
|
118
|
+
not preserve message order.
|
|
111
119
|
|
|
112
120
|
## The framework already solves these
|
|
113
121
|
|
|
@@ -144,20 +152,37 @@ formatting. The individual commands (\`${lint}\`, \`${cli} lint\`,
|
|
|
144
152
|
\`${cli} doctor --strict\`, \`${test}\`, \`${typecheck}\`) still work when you
|
|
145
153
|
need one check alone.
|
|
146
154
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
155
|
+
Inside a Git worktree, \`${cli} check\` fingerprints HEAD, the index, the
|
|
156
|
+
worktree, and non-ignored untracked files before and after validation. If the
|
|
157
|
+
ending fingerprint differs from the baseline, the result is stale and
|
|
158
|
+
unsuccessful even when every step passed; rerun the check against the resulting
|
|
159
|
+
source. Outside Git, the same validation runs with source provenance marked
|
|
160
|
+
unavailable; report that limitation instead of describing the result as
|
|
161
|
+
source-current.
|
|
162
|
+
|
|
163
|
+
Beignet's architecture lint parses JavaScript and TypeScript imports and
|
|
164
|
+
re-exports, \`require()\`, import assignments, and literal dynamic imports, then
|
|
165
|
+
follows local value imports through feature-root helpers. This includes JSX
|
|
166
|
+
and the \`.mts\`, \`.cts\`, \`.mjs\`, and \`.cjs\` module variants. When lint
|
|
167
|
+
cannot verify a computed module reference inside a constrained layer, use a
|
|
168
|
+
string-literal specifier or move runtime loading behind an allowed boundary.
|
|
169
|
+
Move near-miss feature layers to the canonical path named in the diagnostic.
|
|
153
170
|
|
|
154
171
|
Before exploring unfamiliar code, run \`${cli} map\` for the human inventory
|
|
155
172
|
or \`${cli} map --json --feature <name>\` for an evidence-backed graph scoped
|
|
156
|
-
to one feature. The graph
|
|
157
|
-
\`
|
|
158
|
-
\`
|
|
159
|
-
|
|
160
|
-
|
|
173
|
+
to one feature. The graph analyzes JavaScript and TypeScript source and honors
|
|
174
|
+
the app's complete \`baseUrl\` and \`paths\` configuration from
|
|
175
|
+
\`tsconfig.json\`, or \`jsconfig.json\` when no TypeScript config exists. Fix
|
|
176
|
+
\`local_import_unresolved\` and \`contract_declaration_unresolved\` entries
|
|
177
|
+
rather than treating their missing edges as proof that no relationship exists.
|
|
178
|
+
Registration diagnostics expose the declaration as \`subject.file\` and
|
|
179
|
+
\`subject.exportName\`.
|
|
180
|
+
|
|
181
|
+
The graph includes JavaScript and TypeScript test/spec files under canonical
|
|
182
|
+
and configured app test roots. Feature tests retain feature ownership;
|
|
183
|
+
adjacent app, infra, server, and top-level tests remain app-level nodes and can
|
|
184
|
+
appear as reverse-import consumers in changed mode. Treat that as impact
|
|
185
|
+
evidence, not proof of behavioral coverage.
|
|
161
186
|
|
|
162
187
|
After editing in a Git worktree, run \`${cli} map --changed\` before the full
|
|
163
188
|
check. It reports direct changes, reverse imports, one-hop semantic consumers,
|
|
@@ -242,7 +267,10 @@ and reports the script without executing it; it does not simulate SQL or data
|
|
|
242
267
|
changes. Use \`task_run\` and \`schedule_run\` for registered operational
|
|
243
268
|
workflows, \`outbox_inspect\` for read-only \`list\` and \`show\`, and
|
|
244
269
|
\`outbox_run\` for \`drain\`, \`requeue\`, \`purge\`, or \`prune\`; purge and
|
|
245
|
-
prune support \`dryRun\`.
|
|
270
|
+
prune support \`dryRun\`. A drain with nonzero \`settlementFailed\` or
|
|
271
|
+
\`leaseLost\` remains a complete JSON report but is an error result; inspect
|
|
272
|
+
storage health and clock synchronization before retrying. Operational commands
|
|
273
|
+
run in isolated process trees
|
|
246
274
|
with bounded results, cancellation, and timeouts. Optional \`module\` overrides
|
|
247
275
|
must remain inside the app root where the MCP server started. Cancellation and
|
|
248
276
|
timeouts cannot roll back side effects that already completed. Inspect app
|
package/src/templates/base.ts
CHANGED
|
@@ -247,8 +247,8 @@ route groups such as \`app/(app)\` are not part of the URL.
|
|
|
247
247
|
|
|
248
248
|
Beignet app scaffolded with \`@beignet/cli\`.
|
|
249
249
|
|
|
250
|
-
Node.js 22.12 or newer is required. Bun
|
|
251
|
-
Beignet commands.
|
|
250
|
+
Node.js 22.12 or newer is required. Bun is optional; npm, pnpm, Yarn, and Bun
|
|
251
|
+
can run Beignet commands.
|
|
252
252
|
|
|
253
253
|
## Getting started
|
|
254
254
|
|
|
@@ -288,9 +288,9 @@ use cases, workflows, ports, providers, and tests connect. \`check\` runs the
|
|
|
288
288
|
whole validation loop in one pass: \`${cli} lint\` (dependency direction),
|
|
289
289
|
\`${cli} doctor --strict\` (route, OpenAPI, and resource drift), and the
|
|
290
290
|
\`lint\`, \`typecheck\`, and \`test\` package scripts. Use \`${format}\` to
|
|
291
|
-
apply Biome formatting. Beignet lint
|
|
292
|
-
feature-root helpers, and reports unverifiable computed module
|
|
293
|
-
constrained layers.
|
|
291
|
+
apply Biome formatting. Beignet lint analyzes JavaScript and TypeScript import
|
|
292
|
+
graphs, follows feature-root helpers, and reports unverifiable computed module
|
|
293
|
+
references in constrained layers.
|
|
294
294
|
${testingNotes}
|
|
295
295
|
## Coding agents
|
|
296
296
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// lives outside the app schema, so `db generate` never diffs against it.
|
|
16
16
|
//
|
|
17
17
|
// The Better Auth tables were generated with
|
|
18
|
-
// `bunx
|
|
18
|
+
// `bunx auth generate` against a scratch app configured with
|
|
19
19
|
// drizzleAdapter(db, { provider: "mysql" }), then normalized to the starter's
|
|
20
20
|
// conventions (tabs, no relations, no extra indexes, no column defaults).
|
|
21
21
|
// MySQL cannot put UNIQUE constraints on bare text columns, so identity
|
|
@@ -60,6 +60,7 @@ export function closeDatabaseClient(): Promise<void> {
|
|
|
60
60
|
mysqlTable,
|
|
61
61
|
text,
|
|
62
62
|
timestamp,
|
|
63
|
+
uniqueIndex,
|
|
63
64
|
varchar,
|
|
64
65
|
} from "drizzle-orm/mysql-core";
|
|
65
66
|
|
|
@@ -86,23 +87,33 @@ export const session = mysqlTable("session", {
|
|
|
86
87
|
.references(() => user.id, { onDelete: "cascade" }),
|
|
87
88
|
});
|
|
88
89
|
|
|
89
|
-
export const account = mysqlTable(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
.notNull()
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
90
|
+
export const account = mysqlTable(
|
|
91
|
+
"account",
|
|
92
|
+
{
|
|
93
|
+
id: varchar("id", { length: 36 }).primaryKey(),
|
|
94
|
+
issuer: varchar("issuer", { length: 255 }).notNull(),
|
|
95
|
+
accountId: varchar("account_id", { length: 255 }).notNull(),
|
|
96
|
+
providerId: text("provider_id").notNull(),
|
|
97
|
+
userId: varchar("user_id", { length: 36 })
|
|
98
|
+
.notNull()
|
|
99
|
+
.references(() => user.id, { onDelete: "cascade" }),
|
|
100
|
+
accessToken: text("access_token"),
|
|
101
|
+
refreshToken: text("refresh_token"),
|
|
102
|
+
idToken: text("id_token"),
|
|
103
|
+
accessTokenExpiresAt: timestamp("access_token_expires_at", { fsp: 3 }),
|
|
104
|
+
refreshTokenExpiresAt: timestamp("refresh_token_expires_at", { fsp: 3 }),
|
|
105
|
+
scope: text("scope"),
|
|
106
|
+
password: text("password"),
|
|
107
|
+
createdAt: timestamp("created_at", { fsp: 3 }).notNull(),
|
|
108
|
+
updatedAt: timestamp("updated_at", { fsp: 3 }).notNull(),
|
|
109
|
+
},
|
|
110
|
+
(table) => [
|
|
111
|
+
uniqueIndex("account_issuer_accountId_uidx").on(
|
|
112
|
+
table.issuer,
|
|
113
|
+
table.accountId,
|
|
114
|
+
),
|
|
115
|
+
],
|
|
116
|
+
);
|
|
106
117
|
|
|
107
118
|
export const verification = mysqlTable("verification", {
|
|
108
119
|
id: varchar("id", { length: 36 }).primaryKey(),
|
|
@@ -476,7 +487,8 @@ const mysqlStarterMigration = {
|
|
|
476
487
|
// above, with the provider idempotency setup statements appended.
|
|
477
488
|
starterMigrationSql: `CREATE TABLE \`account\` (
|
|
478
489
|
\`id\` varchar(36) NOT NULL,
|
|
479
|
-
\`
|
|
490
|
+
\`issuer\` varchar(255) NOT NULL,
|
|
491
|
+
\`account_id\` varchar(255) NOT NULL,
|
|
480
492
|
\`provider_id\` text NOT NULL,
|
|
481
493
|
\`user_id\` varchar(36) NOT NULL,
|
|
482
494
|
\`access_token\` text,
|
|
@@ -488,7 +500,8 @@ const mysqlStarterMigration = {
|
|
|
488
500
|
\`password\` text,
|
|
489
501
|
\`created_at\` timestamp(3) NOT NULL,
|
|
490
502
|
\`updated_at\` timestamp(3) NOT NULL,
|
|
491
|
-
CONSTRAINT \`account_id\` PRIMARY KEY(\`id\`)
|
|
503
|
+
CONSTRAINT \`account_id\` PRIMARY KEY(\`id\`),
|
|
504
|
+
CONSTRAINT \`account_issuer_accountId_uidx\` UNIQUE(\`issuer\`,\`account_id\`)
|
|
492
505
|
);
|
|
493
506
|
--> statement-breakpoint
|
|
494
507
|
CREATE TABLE \`session\` (
|
|
@@ -585,9 +598,16 @@ CREATE TABLE IF NOT EXISTS \`idempotency_records\` (
|
|
|
585
598
|
"notNull": true,
|
|
586
599
|
"autoincrement": false
|
|
587
600
|
},
|
|
601
|
+
"issuer": {
|
|
602
|
+
"name": "issuer",
|
|
603
|
+
"type": "varchar(255)",
|
|
604
|
+
"primaryKey": false,
|
|
605
|
+
"notNull": true,
|
|
606
|
+
"autoincrement": false
|
|
607
|
+
},
|
|
588
608
|
"account_id": {
|
|
589
609
|
"name": "account_id",
|
|
590
|
-
"type": "
|
|
610
|
+
"type": "varchar(255)",
|
|
591
611
|
"primaryKey": false,
|
|
592
612
|
"notNull": true,
|
|
593
613
|
"autoincrement": false
|
|
@@ -670,7 +690,16 @@ CREATE TABLE IF NOT EXISTS \`idempotency_records\` (
|
|
|
670
690
|
"autoincrement": false
|
|
671
691
|
}
|
|
672
692
|
},
|
|
673
|
-
"indexes": {
|
|
693
|
+
"indexes": {
|
|
694
|
+
"account_issuer_accountId_uidx": {
|
|
695
|
+
"name": "account_issuer_accountId_uidx",
|
|
696
|
+
"columns": [
|
|
697
|
+
"issuer",
|
|
698
|
+
"account_id"
|
|
699
|
+
],
|
|
700
|
+
"isUnique": true
|
|
701
|
+
}
|
|
702
|
+
},
|
|
674
703
|
"foreignKeys": {
|
|
675
704
|
"account_user_id_user_id_fk": {
|
|
676
705
|
"name": "account_user_id_user_id_fk",
|