@beignet/cli 0.0.31 → 0.0.33
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 +107 -0
- package/README.md +70 -17
- package/dist/check.d.ts +2 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +16 -0
- package/dist/check.js.map +1 -1
- package/dist/choices.d.ts +22 -2
- package/dist/choices.d.ts.map +1 -1
- package/dist/choices.js +62 -0
- package/dist/choices.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/create-prompts.d.ts +6 -2
- package/dist/create-prompts.d.ts.map +1 -1
- package/dist/create-prompts.js +1 -1
- package/dist/create-prompts.js.map +1 -1
- package/dist/db.d.ts +5 -5
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +5 -5
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +386 -27
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +5 -0
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +805 -62
- package/dist/inspect.js.map +1 -1
- package/dist/make/inbox.d.ts.map +1 -1
- package/dist/make/inbox.js +5 -3
- package/dist/make/inbox.js.map +1 -1
- package/dist/make/payments.d.ts.map +1 -1
- package/dist/make/payments.js +58 -29
- package/dist/make/payments.js.map +1 -1
- package/dist/make/shared.d.ts +12 -3
- package/dist/make/shared.d.ts.map +1 -1
- package/dist/make/shared.js +52 -11
- package/dist/make/shared.js.map +1 -1
- package/dist/make/tenancy.d.ts.map +1 -1
- package/dist/make/tenancy.js +6 -4
- package/dist/make/tenancy.js.map +1 -1
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +136 -65
- package/dist/make.js.map +1 -1
- package/dist/outbox.d.ts +114 -1
- package/dist/outbox.d.ts.map +1 -1
- package/dist/outbox.js +190 -0
- package/dist/outbox.js.map +1 -1
- package/dist/preflight.d.ts +90 -0
- package/dist/preflight.d.ts.map +1 -0
- package/dist/preflight.js +423 -0
- package/dist/preflight.js.map +1 -0
- package/dist/provider-add.d.ts +10 -1
- package/dist/provider-add.d.ts.map +1 -1
- package/dist/provider-add.js +255 -16
- package/dist/provider-add.js.map +1 -1
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +2 -3
- package/dist/task.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +6 -3
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +5 -11
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +2 -0
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +10 -5
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shared.d.ts +5 -1
- package/dist/templates/shared.d.ts.map +1 -1
- package/dist/templates/shared.js +13 -1
- package/dist/templates/shared.js.map +1 -1
- package/dist/templates/testing.d.ts +5 -0
- package/dist/templates/testing.d.ts.map +1 -0
- package/dist/templates/testing.js +542 -0
- package/dist/templates/testing.js.map +1 -0
- package/dist/templates/todos.js +2 -2
- package/package.json +3 -2
- package/skills/app-structure/SKILL.md +12 -4
- package/src/check.ts +23 -0
- package/src/choices.ts +84 -0
- package/src/config.ts +4 -0
- package/src/create-prompts.ts +8 -2
- package/src/db.ts +12 -12
- package/src/index.ts +592 -37
- package/src/inspect.ts +1253 -156
- package/src/make/inbox.ts +5 -3
- package/src/make/payments.ts +58 -29
- package/src/make/shared.ts +87 -13
- package/src/make/tenancy.ts +6 -4
- package/src/make.ts +179 -65
- package/src/outbox.ts +363 -0
- package/src/preflight.ts +596 -0
- package/src/provider-add.ts +270 -16
- package/src/task.ts +2 -3
- package/src/templates/agents.ts +6 -3
- package/src/templates/base.ts +5 -13
- package/src/templates/index.ts +2 -0
- package/src/templates/server.ts +10 -5
- package/src/templates/shared.ts +15 -1
- package/src/templates/testing.ts +545 -0
- package/src/templates/todos.ts +2 -2
- package/src/test-helpers/generated-app.ts +10 -6
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { buildApplication, buildCommand, buildRouteMap, proposeCompletions, run, text_en, } from "@stricli/core";
|
|
5
|
-
import { completionShellChoices, databaseChoices, databaseStartCommand,
|
|
5
|
+
import { completionShellChoices, createIntegrationChoices, databaseChoices, databaseStartCommand, makeFeatureAddonChoices, makeFeatureRecipeChoices, packageManagerChoices, providerPresetChoices, splitCreateIntegrations, templateChoices, } from "./choices.js";
|
|
6
6
|
import { getCliVersion } from "./version.js";
|
|
7
7
|
const outputFormatChoices = [
|
|
8
8
|
"human",
|
|
@@ -19,6 +19,16 @@ const databaseSchemaTableChoices = [
|
|
|
19
19
|
"idempotency",
|
|
20
20
|
"outbox",
|
|
21
21
|
];
|
|
22
|
+
const outboxMessageStatusChoices = [
|
|
23
|
+
"pending",
|
|
24
|
+
"claimed",
|
|
25
|
+
"delivered",
|
|
26
|
+
"deadLettered",
|
|
27
|
+
];
|
|
28
|
+
const outboxMessageKindChoices = [
|
|
29
|
+
"event",
|
|
30
|
+
"job",
|
|
31
|
+
];
|
|
22
32
|
const parseString = (input) => input;
|
|
23
33
|
const parseProviderPreset = (input) => {
|
|
24
34
|
if (providerPresetChoices.includes(input)) {
|
|
@@ -33,6 +43,13 @@ const parsePositiveInteger = (input) => {
|
|
|
33
43
|
}
|
|
34
44
|
return value;
|
|
35
45
|
};
|
|
46
|
+
const parseDate = (input) => {
|
|
47
|
+
const value = new Date(input);
|
|
48
|
+
if (Number.isNaN(value.getTime())) {
|
|
49
|
+
throw new Error("Expected a valid date.");
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
};
|
|
36
53
|
const forceFlag = {
|
|
37
54
|
kind: "boolean",
|
|
38
55
|
optional: true,
|
|
@@ -45,6 +62,12 @@ const dryRunFlag = {
|
|
|
45
62
|
withNegated: false,
|
|
46
63
|
brief: "Preview generated changes without writing files.",
|
|
47
64
|
};
|
|
65
|
+
const operationDryRunFlag = {
|
|
66
|
+
kind: "boolean",
|
|
67
|
+
optional: true,
|
|
68
|
+
withNegated: false,
|
|
69
|
+
brief: "Report matching records without deleting them.",
|
|
70
|
+
};
|
|
48
71
|
const jsonFlag = {
|
|
49
72
|
kind: "boolean",
|
|
50
73
|
optional: true,
|
|
@@ -57,6 +80,12 @@ const parsedStringFlag = (brief) => ({
|
|
|
57
80
|
optional: true,
|
|
58
81
|
brief,
|
|
59
82
|
});
|
|
83
|
+
const parsedDateFlag = (brief) => ({
|
|
84
|
+
kind: "parsed",
|
|
85
|
+
parse: parseDate,
|
|
86
|
+
optional: true,
|
|
87
|
+
brief,
|
|
88
|
+
});
|
|
60
89
|
const cwdFlag = parsedStringFlag("App directory to inspect. Defaults to the current working directory.");
|
|
61
90
|
const formatFlag = {
|
|
62
91
|
kind: "enum",
|
|
@@ -91,22 +120,22 @@ const dbFlagParameters = {
|
|
|
91
120
|
json: jsonFlag,
|
|
92
121
|
dryRun: dryRunFlag,
|
|
93
122
|
};
|
|
94
|
-
const
|
|
123
|
+
const dbSchemaSyncFlagParameters = {
|
|
95
124
|
...dbFlagParameters,
|
|
96
125
|
dialect: {
|
|
97
126
|
kind: "enum",
|
|
98
127
|
values: databaseSchemaDialectChoices,
|
|
99
128
|
optional: true,
|
|
100
|
-
brief: "Drizzle dialect to
|
|
129
|
+
brief: "Drizzle dialect to sync. Inferred from server/providers.ts when omitted.",
|
|
101
130
|
},
|
|
102
131
|
tables: {
|
|
103
132
|
kind: "enum",
|
|
104
133
|
values: databaseSchemaTableChoices,
|
|
105
134
|
optional: true,
|
|
106
135
|
variadic: ",",
|
|
107
|
-
brief: "Provider tables to
|
|
136
|
+
brief: "Provider tables to sync as a comma-separated list. Defaults to audit,idempotency,outbox.",
|
|
108
137
|
},
|
|
109
|
-
output: parsedStringFlag("
|
|
138
|
+
output: parsedStringFlag("Synced schema file. Defaults to infra/db/schema/beignet.ts."),
|
|
110
139
|
};
|
|
111
140
|
const taskRunFlagParameters = {
|
|
112
141
|
json: jsonFlag,
|
|
@@ -124,6 +153,74 @@ const outboxDrainFlagParameters = {
|
|
|
124
153
|
brief: "Maximum messages to claim in one drain pass.",
|
|
125
154
|
},
|
|
126
155
|
};
|
|
156
|
+
const outboxListFlagParameters = {
|
|
157
|
+
json: jsonFlag,
|
|
158
|
+
module: parsedStringFlag("Outbox registry module. Defaults to server/outbox.ts."),
|
|
159
|
+
status: {
|
|
160
|
+
kind: "enum",
|
|
161
|
+
values: outboxMessageStatusChoices,
|
|
162
|
+
optional: true,
|
|
163
|
+
brief: "Filter by outbox message status.",
|
|
164
|
+
},
|
|
165
|
+
kind: {
|
|
166
|
+
kind: "enum",
|
|
167
|
+
values: outboxMessageKindChoices,
|
|
168
|
+
optional: true,
|
|
169
|
+
brief: "Filter by outbox message kind.",
|
|
170
|
+
},
|
|
171
|
+
name: parsedStringFlag("Filter by event or job name."),
|
|
172
|
+
limit: {
|
|
173
|
+
kind: "parsed",
|
|
174
|
+
parse: parsePositiveInteger,
|
|
175
|
+
optional: true,
|
|
176
|
+
brief: "Maximum messages to return. Defaults to 50.",
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
const outboxShowFlagParameters = {
|
|
180
|
+
json: jsonFlag,
|
|
181
|
+
module: parsedStringFlag("Outbox registry module. Defaults to server/outbox.ts."),
|
|
182
|
+
};
|
|
183
|
+
const outboxRequeueFlagParameters = {
|
|
184
|
+
json: jsonFlag,
|
|
185
|
+
module: parsedStringFlag("Outbox registry module. Defaults to server/outbox.ts."),
|
|
186
|
+
availableAt: parsedDateFlag("Earliest timestamp the requeued message may be claimed. Defaults to now."),
|
|
187
|
+
resetAttempts: {
|
|
188
|
+
kind: "boolean",
|
|
189
|
+
optional: true,
|
|
190
|
+
withNegated: false,
|
|
191
|
+
brief: "Reset attempts to zero before requeueing.",
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
const outboxPurgeFlagParameters = {
|
|
195
|
+
json: jsonFlag,
|
|
196
|
+
module: parsedStringFlag("Outbox registry module. Defaults to server/outbox.ts."),
|
|
197
|
+
before: parsedDateFlag("Only purge dead-lettered messages last updated before this timestamp."),
|
|
198
|
+
all: {
|
|
199
|
+
kind: "boolean",
|
|
200
|
+
optional: true,
|
|
201
|
+
withNegated: false,
|
|
202
|
+
brief: "Purge every dead-lettered message when --before is omitted.",
|
|
203
|
+
},
|
|
204
|
+
limit: {
|
|
205
|
+
kind: "parsed",
|
|
206
|
+
parse: parsePositiveInteger,
|
|
207
|
+
optional: true,
|
|
208
|
+
brief: "Maximum messages to purge.",
|
|
209
|
+
},
|
|
210
|
+
dryRun: operationDryRunFlag,
|
|
211
|
+
};
|
|
212
|
+
const outboxPruneFlagParameters = {
|
|
213
|
+
json: jsonFlag,
|
|
214
|
+
module: parsedStringFlag("Outbox registry module. Defaults to server/outbox.ts."),
|
|
215
|
+
before: parsedDateFlag("Prune delivered messages delivered before this timestamp."),
|
|
216
|
+
limit: {
|
|
217
|
+
kind: "parsed",
|
|
218
|
+
parse: parsePositiveInteger,
|
|
219
|
+
optional: true,
|
|
220
|
+
brief: "Maximum messages to prune.",
|
|
221
|
+
},
|
|
222
|
+
dryRun: operationDryRunFlag,
|
|
223
|
+
};
|
|
127
224
|
const scheduleRunFlagParameters = {
|
|
128
225
|
json: jsonFlag,
|
|
129
226
|
module: parsedStringFlag("Schedule registry module. Defaults to server/schedules.ts."),
|
|
@@ -149,6 +246,16 @@ const namePositional = {
|
|
|
149
246
|
},
|
|
150
247
|
],
|
|
151
248
|
};
|
|
249
|
+
const idPositional = {
|
|
250
|
+
kind: "tuple",
|
|
251
|
+
parameters: [
|
|
252
|
+
{
|
|
253
|
+
parse: parseString,
|
|
254
|
+
placeholder: "id",
|
|
255
|
+
brief: "Outbox message ID.",
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
};
|
|
152
259
|
const providerPresetPositional = {
|
|
153
260
|
kind: "tuple",
|
|
154
261
|
parameters: [
|
|
@@ -182,7 +289,11 @@ and a shadcn UI shell. Pass --api for an API-only scaffold without the
|
|
|
182
289
|
UI shell, and --db to choose the database.
|
|
183
290
|
|
|
184
291
|
Available databases: ${databaseChoices.join(", ")}
|
|
185
|
-
Available integrations: ${
|
|
292
|
+
Available integrations: ${createIntegrationChoices.join(", ")}
|
|
293
|
+
|
|
294
|
+
--integrations accepts the starter template integrations plus the full
|
|
295
|
+
beignet providers add preset catalog; presets are applied to the scaffold
|
|
296
|
+
with the same machinery as beignet providers add.
|
|
186
297
|
|
|
187
298
|
Running create on an interactive terminal without selection flags opens a
|
|
188
299
|
prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
|
|
@@ -215,10 +326,10 @@ prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
|
|
|
215
326
|
},
|
|
216
327
|
integrations: {
|
|
217
328
|
kind: "enum",
|
|
218
|
-
values:
|
|
329
|
+
values: createIntegrationChoices,
|
|
219
330
|
optional: true,
|
|
220
331
|
variadic: ",",
|
|
221
|
-
brief: "Add first-party integrations as a comma-separated list.",
|
|
332
|
+
brief: "Add first-party integrations or provider presets as a comma-separated list.",
|
|
222
333
|
},
|
|
223
334
|
yes: {
|
|
224
335
|
kind: "boolean",
|
|
@@ -271,7 +382,13 @@ prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
|
|
|
271
382
|
}
|
|
272
383
|
const api = selections.api ?? false;
|
|
273
384
|
const database = selections.db ?? "sqlite";
|
|
274
|
-
const integrations = uniqueValues([...(selections.integrations ?? [])]);
|
|
385
|
+
const { integrations, presets } = splitCreateIntegrations(uniqueValues([...(selections.integrations ?? [])]));
|
|
386
|
+
let presetsNeedEnv = false;
|
|
387
|
+
if (presets.length > 0) {
|
|
388
|
+
const { assertCompatibleCreatePresets, providerPresetNeedsEnv } = await import("./provider-add.js");
|
|
389
|
+
assertCompatibleCreatePresets(presets, integrations);
|
|
390
|
+
presetsNeedEnv = presets.some(providerPresetNeedsEnv);
|
|
391
|
+
}
|
|
275
392
|
const result = await createProject({
|
|
276
393
|
name: selections.directory,
|
|
277
394
|
template: flags.template,
|
|
@@ -282,9 +399,21 @@ prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
|
|
|
282
399
|
force: Boolean(flags.force),
|
|
283
400
|
dryRun: Boolean(flags.dryRun),
|
|
284
401
|
});
|
|
402
|
+
if (!flags.dryRun && presets.length > 0) {
|
|
403
|
+
const { addProviderPreset } = await import("./provider-add.js");
|
|
404
|
+
for (const preset of presets) {
|
|
405
|
+
await addProviderPreset({ name: preset, cwd: result.targetDir });
|
|
406
|
+
}
|
|
407
|
+
}
|
|
285
408
|
writeOutput(this, flags.json
|
|
286
|
-
? JSON.stringify(result, null, 2)
|
|
287
|
-
: nextSteps(result, {
|
|
409
|
+
? JSON.stringify({ ...result, providerPresets: presets }, null, 2)
|
|
410
|
+
: nextSteps(result, {
|
|
411
|
+
api,
|
|
412
|
+
integrations,
|
|
413
|
+
database,
|
|
414
|
+
presets,
|
|
415
|
+
presetsNeedEnv,
|
|
416
|
+
}));
|
|
288
417
|
};
|
|
289
418
|
},
|
|
290
419
|
});
|
|
@@ -367,6 +496,12 @@ const checkCommand = buildCommand({
|
|
|
367
496
|
withNegated: false,
|
|
368
497
|
brief: "Apply low-risk doctor fixes before checking.",
|
|
369
498
|
},
|
|
499
|
+
preflight: {
|
|
500
|
+
kind: "boolean",
|
|
501
|
+
optional: true,
|
|
502
|
+
withNegated: false,
|
|
503
|
+
brief: "Append a beignet preflight step after doctor.",
|
|
504
|
+
},
|
|
370
505
|
cwd: cwdFlag,
|
|
371
506
|
},
|
|
372
507
|
},
|
|
@@ -377,6 +512,7 @@ const checkCommand = buildCommand({
|
|
|
377
512
|
const result = await checkApp({
|
|
378
513
|
cwd: flags.cwd,
|
|
379
514
|
fix: flags.fix,
|
|
515
|
+
preflight: flags.preflight,
|
|
380
516
|
color,
|
|
381
517
|
...(flags.json
|
|
382
518
|
? {}
|
|
@@ -393,6 +529,47 @@ const checkCommand = buildCommand({
|
|
|
393
529
|
};
|
|
394
530
|
},
|
|
395
531
|
});
|
|
532
|
+
const preflightCommand = buildCommand({
|
|
533
|
+
docs: {
|
|
534
|
+
brief: "Validate the deploy environment before shipping.",
|
|
535
|
+
fullDescription: "Runtime production gate, unlike the static doctor checks: reads the actual environment the process runs with, verifies every env var installed provider manifests mark as required, flags values still matching .env.example or common placeholders on secret-like keys, and validates the app env schema by importing lib/env.ts. Run it in the deploy pipeline where production configuration is present. Exits 1 on any error finding.",
|
|
536
|
+
},
|
|
537
|
+
parameters: {
|
|
538
|
+
flags: {
|
|
539
|
+
json: jsonFlag,
|
|
540
|
+
envFile: parsedStringFlag("Dotenv-style file merged under the environment (existing env keys win) for local rehearsal."),
|
|
541
|
+
envModule: parsedStringFlag("App env module validated by importing it. Defaults to lib/env.ts."),
|
|
542
|
+
connect: {
|
|
543
|
+
kind: "boolean",
|
|
544
|
+
optional: true,
|
|
545
|
+
withNegated: false,
|
|
546
|
+
brief: "Boot the app server and run every port's checkHealth(). Needs network and real credentials.",
|
|
547
|
+
},
|
|
548
|
+
serverModule: parsedStringFlag("Module exporting getServer, used by --connect. Defaults to server/index.ts."),
|
|
549
|
+
cwd: cwdFlag,
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
loader: async () => {
|
|
553
|
+
const { formatPreflight, runPreflight } = await import("./preflight.js");
|
|
554
|
+
const path = await import("node:path");
|
|
555
|
+
return async function runPreflightCommand(flags) {
|
|
556
|
+
const cwd = path.resolve(flags.cwd ?? process.cwd());
|
|
557
|
+
const result = await runPreflight({
|
|
558
|
+
cwd,
|
|
559
|
+
envFile: flags.envFile,
|
|
560
|
+
envModule: flags.envModule,
|
|
561
|
+
connect: flags.connect,
|
|
562
|
+
serverModule: flags.serverModule,
|
|
563
|
+
});
|
|
564
|
+
writeOutput(this, flags.json
|
|
565
|
+
? JSON.stringify({ schemaVersion: 1, cwd, ...result }, null, 2)
|
|
566
|
+
: formatPreflight(result, cwd));
|
|
567
|
+
if (result.findings.some((finding) => finding.severity === "error")) {
|
|
568
|
+
this.process.exitCode = 1;
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
},
|
|
572
|
+
});
|
|
396
573
|
const lintCommand = buildCommand({
|
|
397
574
|
docs: {
|
|
398
575
|
brief: "Check Beignet dependency direction conventions.",
|
|
@@ -515,18 +692,18 @@ function databaseCommand(command) {
|
|
|
515
692
|
},
|
|
516
693
|
});
|
|
517
694
|
}
|
|
518
|
-
const
|
|
695
|
+
const dbSchemaSyncCommand = buildCommand({
|
|
519
696
|
docs: {
|
|
520
|
-
brief: "
|
|
521
|
-
fullDescription: "
|
|
697
|
+
brief: "Sync app-owned re-exports of Beignet provider tables.",
|
|
698
|
+
fullDescription: "Idempotently brings the app-owned schema file that re-exports Beignet provider tables in sync with the installed providers, then updates the schema index. Run beignet db generate afterward to let the app's Drizzle Kit script create migrations.",
|
|
522
699
|
},
|
|
523
700
|
parameters: {
|
|
524
|
-
flags:
|
|
701
|
+
flags: dbSchemaSyncFlagParameters,
|
|
525
702
|
},
|
|
526
703
|
loader: async () => {
|
|
527
|
-
const {
|
|
528
|
-
return async function
|
|
529
|
-
const result = await
|
|
704
|
+
const { syncDatabaseSchema } = await import("./db.js");
|
|
705
|
+
return async function runDbSchemaSync(flags) {
|
|
706
|
+
const result = await syncDatabaseSchema({
|
|
530
707
|
dialect: flags.dialect,
|
|
531
708
|
tables: flags.tables,
|
|
532
709
|
output: flags.output,
|
|
@@ -534,16 +711,16 @@ const dbSchemaGenerateCommand = buildCommand({
|
|
|
534
711
|
});
|
|
535
712
|
writeOutput(this, flags.json
|
|
536
713
|
? JSON.stringify(result, null, 2)
|
|
537
|
-
:
|
|
714
|
+
: databaseSchemaSyncNextSteps(result));
|
|
538
715
|
};
|
|
539
716
|
},
|
|
540
717
|
});
|
|
541
718
|
const dbSchemaRoutes = buildRouteMap({
|
|
542
719
|
docs: {
|
|
543
|
-
brief: "
|
|
720
|
+
brief: "Sync app-owned Beignet provider schema exports.",
|
|
544
721
|
},
|
|
545
722
|
routes: {
|
|
546
|
-
|
|
723
|
+
sync: dbSchemaSyncCommand,
|
|
547
724
|
},
|
|
548
725
|
});
|
|
549
726
|
const dbRoutes = buildRouteMap({
|
|
@@ -609,12 +786,132 @@ const outboxDrainCommand = buildCommand({
|
|
|
609
786
|
};
|
|
610
787
|
},
|
|
611
788
|
});
|
|
789
|
+
const outboxListCommand = buildCommand({
|
|
790
|
+
docs: {
|
|
791
|
+
brief: "List outbox messages for operational inspection.",
|
|
792
|
+
},
|
|
793
|
+
parameters: {
|
|
794
|
+
flags: outboxListFlagParameters,
|
|
795
|
+
},
|
|
796
|
+
loader: async () => {
|
|
797
|
+
const { runOutboxList } = await import("./outbox.js");
|
|
798
|
+
return async function runList(flags) {
|
|
799
|
+
const result = await runOutboxList({
|
|
800
|
+
modulePath: flags.module,
|
|
801
|
+
status: flags.status,
|
|
802
|
+
kind: flags.kind,
|
|
803
|
+
name: flags.name,
|
|
804
|
+
limit: flags.limit,
|
|
805
|
+
});
|
|
806
|
+
writeOutput(this, flags.json
|
|
807
|
+
? JSON.stringify(result, null, 2)
|
|
808
|
+
: outboxListNextSteps(result));
|
|
809
|
+
};
|
|
810
|
+
},
|
|
811
|
+
});
|
|
812
|
+
const outboxShowCommand = buildCommand({
|
|
813
|
+
docs: {
|
|
814
|
+
brief: "Show one outbox message.",
|
|
815
|
+
},
|
|
816
|
+
parameters: {
|
|
817
|
+
flags: outboxShowFlagParameters,
|
|
818
|
+
positional: idPositional,
|
|
819
|
+
},
|
|
820
|
+
loader: async () => {
|
|
821
|
+
const { runOutboxShow } = await import("./outbox.js");
|
|
822
|
+
return async function runShow(flags, id) {
|
|
823
|
+
const result = await runOutboxShow({
|
|
824
|
+
modulePath: flags.module,
|
|
825
|
+
id,
|
|
826
|
+
});
|
|
827
|
+
writeOutput(this, flags.json
|
|
828
|
+
? JSON.stringify(result, null, 2)
|
|
829
|
+
: outboxShowNextSteps(result));
|
|
830
|
+
};
|
|
831
|
+
},
|
|
832
|
+
});
|
|
833
|
+
const outboxRequeueCommand = buildCommand({
|
|
834
|
+
docs: {
|
|
835
|
+
brief: "Return one dead-lettered outbox message to pending state.",
|
|
836
|
+
},
|
|
837
|
+
parameters: {
|
|
838
|
+
flags: outboxRequeueFlagParameters,
|
|
839
|
+
positional: idPositional,
|
|
840
|
+
},
|
|
841
|
+
loader: async () => {
|
|
842
|
+
const { runOutboxRequeue } = await import("./outbox.js");
|
|
843
|
+
return async function runRequeue(flags, id) {
|
|
844
|
+
const result = await runOutboxRequeue({
|
|
845
|
+
modulePath: flags.module,
|
|
846
|
+
id,
|
|
847
|
+
availableAt: flags.availableAt,
|
|
848
|
+
resetAttempts: Boolean(flags.resetAttempts),
|
|
849
|
+
});
|
|
850
|
+
writeOutput(this, flags.json
|
|
851
|
+
? JSON.stringify(result, null, 2)
|
|
852
|
+
: outboxRequeueNextSteps(result));
|
|
853
|
+
};
|
|
854
|
+
},
|
|
855
|
+
});
|
|
856
|
+
const outboxPurgeCommand = buildCommand({
|
|
857
|
+
docs: {
|
|
858
|
+
brief: "Delete dead-lettered outbox messages.",
|
|
859
|
+
},
|
|
860
|
+
parameters: {
|
|
861
|
+
flags: outboxPurgeFlagParameters,
|
|
862
|
+
},
|
|
863
|
+
loader: async () => {
|
|
864
|
+
const { runOutboxPurge } = await import("./outbox.js");
|
|
865
|
+
return async function runPurge(flags) {
|
|
866
|
+
const result = await runOutboxPurge({
|
|
867
|
+
modulePath: flags.module,
|
|
868
|
+
before: flags.before,
|
|
869
|
+
all: Boolean(flags.all),
|
|
870
|
+
limit: flags.limit,
|
|
871
|
+
dryRun: Boolean(flags.dryRun),
|
|
872
|
+
});
|
|
873
|
+
writeOutput(this, flags.json
|
|
874
|
+
? JSON.stringify(result, null, 2)
|
|
875
|
+
: outboxDeleteNextSteps("Purged dead-lettered outbox messages", result));
|
|
876
|
+
};
|
|
877
|
+
},
|
|
878
|
+
});
|
|
879
|
+
const outboxPruneCommand = buildCommand({
|
|
880
|
+
docs: {
|
|
881
|
+
brief: "Delete delivered outbox messages older than a retention cutoff.",
|
|
882
|
+
},
|
|
883
|
+
parameters: {
|
|
884
|
+
flags: outboxPruneFlagParameters,
|
|
885
|
+
},
|
|
886
|
+
loader: async () => {
|
|
887
|
+
const { runOutboxPrune } = await import("./outbox.js");
|
|
888
|
+
return async function runPrune(flags) {
|
|
889
|
+
if (!flags.before) {
|
|
890
|
+
throw new Error("Pass --before <date> to prune delivered messages.");
|
|
891
|
+
}
|
|
892
|
+
const result = await runOutboxPrune({
|
|
893
|
+
modulePath: flags.module,
|
|
894
|
+
before: flags.before,
|
|
895
|
+
limit: flags.limit,
|
|
896
|
+
dryRun: Boolean(flags.dryRun),
|
|
897
|
+
});
|
|
898
|
+
writeOutput(this, flags.json
|
|
899
|
+
? JSON.stringify(result, null, 2)
|
|
900
|
+
: outboxDeleteNextSteps("Pruned delivered outbox messages", result));
|
|
901
|
+
};
|
|
902
|
+
},
|
|
903
|
+
});
|
|
612
904
|
const outboxRoutes = buildRouteMap({
|
|
613
905
|
docs: {
|
|
614
906
|
brief: "Run Beignet outbox operations.",
|
|
615
907
|
},
|
|
616
908
|
routes: {
|
|
617
909
|
drain: outboxDrainCommand,
|
|
910
|
+
list: outboxListCommand,
|
|
911
|
+
show: outboxShowCommand,
|
|
912
|
+
requeue: outboxRequeueCommand,
|
|
913
|
+
purge: outboxPurgeCommand,
|
|
914
|
+
prune: outboxPruneCommand,
|
|
618
915
|
},
|
|
619
916
|
});
|
|
620
917
|
const scheduleRunCommand = buildCommand({
|
|
@@ -1012,6 +1309,7 @@ Run npm create beignet@latest (or bun create beignet) to scaffold a new app.`,
|
|
|
1012
1309
|
make: makeRoutes,
|
|
1013
1310
|
mcp: mcpCommand,
|
|
1014
1311
|
outbox: outboxRoutes,
|
|
1312
|
+
preflight: preflightCommand,
|
|
1015
1313
|
providers: providerRoutes,
|
|
1016
1314
|
routes: routesCommand,
|
|
1017
1315
|
schedule: scheduleRoutes,
|
|
@@ -1070,6 +1368,57 @@ Result:
|
|
|
1070
1368
|
retried: ${result.result.retried}
|
|
1071
1369
|
deadLettered: ${result.result.deadLettered}`;
|
|
1072
1370
|
}
|
|
1371
|
+
function formatOutboxDate(value) {
|
|
1372
|
+
if (!value)
|
|
1373
|
+
return "none";
|
|
1374
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
1375
|
+
}
|
|
1376
|
+
function outboxMessageLine(message) {
|
|
1377
|
+
return `${message.id} ${message.status} ${message.kind} ${message.name} attempts ${message.attempts}/${message.maxAttempts} updated ${formatOutboxDate(message.updatedAt)}`;
|
|
1378
|
+
}
|
|
1379
|
+
function outboxListNextSteps(result) {
|
|
1380
|
+
const messages = result.messages.length === 0
|
|
1381
|
+
? " none"
|
|
1382
|
+
: result.messages
|
|
1383
|
+
.map((message) => ` ${outboxMessageLine(message)}`)
|
|
1384
|
+
.join("\n");
|
|
1385
|
+
return `Listed ${result.messages.length} of ${result.count} matching outbox messages in ${result.durationMs}ms
|
|
1386
|
+
|
|
1387
|
+
Messages:
|
|
1388
|
+
${messages}`;
|
|
1389
|
+
}
|
|
1390
|
+
function outboxShowNextSteps(result) {
|
|
1391
|
+
const message = result.message;
|
|
1392
|
+
const error = message.lastError
|
|
1393
|
+
? `${message.lastError.name ?? "Error"}: ${message.lastError.message}`
|
|
1394
|
+
: "none";
|
|
1395
|
+
return `Outbox message ${message.id} (${message.status}) in ${result.durationMs}ms
|
|
1396
|
+
|
|
1397
|
+
Message:
|
|
1398
|
+
kind: ${message.kind}
|
|
1399
|
+
name: ${message.name}
|
|
1400
|
+
attempts: ${message.attempts}/${message.maxAttempts}
|
|
1401
|
+
availableAt: ${formatOutboxDate(message.availableAt)}
|
|
1402
|
+
deliveredAt: ${formatOutboxDate(message.deliveredAt)}
|
|
1403
|
+
updatedAt: ${formatOutboxDate(message.updatedAt)}
|
|
1404
|
+
lastError: ${error}`;
|
|
1405
|
+
}
|
|
1406
|
+
function outboxRequeueNextSteps(result) {
|
|
1407
|
+
return `Requeued outbox message ${result.message.id} in ${result.durationMs}ms
|
|
1408
|
+
|
|
1409
|
+
Message:
|
|
1410
|
+
status: ${result.message.status}
|
|
1411
|
+
attempts: ${result.message.attempts}/${result.message.maxAttempts}
|
|
1412
|
+
availableAt: ${formatOutboxDate(result.message.availableAt)}`;
|
|
1413
|
+
}
|
|
1414
|
+
function outboxDeleteNextSteps(label, result) {
|
|
1415
|
+
const action = result.dryRun ? "Would delete" : "Deleted";
|
|
1416
|
+
return `${label} in ${result.durationMs}ms
|
|
1417
|
+
|
|
1418
|
+
Result:
|
|
1419
|
+
matched: ${result.matched}
|
|
1420
|
+
${action}: ${result.deleted}`;
|
|
1421
|
+
}
|
|
1073
1422
|
function scheduleRunNextSteps(result) {
|
|
1074
1423
|
return `Ran schedule ${result.name} in ${result.durationMs}ms
|
|
1075
1424
|
|
|
@@ -1083,18 +1432,28 @@ Run:
|
|
|
1083
1432
|
function nextSteps(result, options = {}) {
|
|
1084
1433
|
if (result.dryRun) {
|
|
1085
1434
|
const plannedFiles = result.files.map((file) => ` ${file}`).join("\n");
|
|
1435
|
+
const plannedPresets = options.presets && options.presets.length > 0
|
|
1436
|
+
? `\n\nProvider presets applied after scaffold (skipped in dry run):\n${options.presets
|
|
1437
|
+
.map((preset) => ` ${preset}`)
|
|
1438
|
+
.join("\n")}`
|
|
1439
|
+
: "";
|
|
1086
1440
|
return `Would create ${result.name} at ${result.targetDir}
|
|
1087
1441
|
|
|
1088
1442
|
Planned files:
|
|
1089
|
-
${plannedFiles}`;
|
|
1443
|
+
${plannedFiles}${plannedPresets}`;
|
|
1090
1444
|
}
|
|
1091
1445
|
const pm = result.packageManager;
|
|
1092
1446
|
const run = pm === "npm" ? "npm run" : `${pm} run`;
|
|
1093
1447
|
const cli = packageRunnerFromPackageManager(pm);
|
|
1094
1448
|
const envRequiredIntegrations = options.integrations?.filter(isEnvRequiredProviderIntegration) ?? [];
|
|
1095
|
-
const envStep = envRequiredIntegrations.length > 0
|
|
1449
|
+
const envStep = envRequiredIntegrations.length > 0 || options.presetsNeedEnv
|
|
1096
1450
|
? " Fill .env.local for selected provider integrations before starting the app.\n"
|
|
1097
1451
|
: "";
|
|
1452
|
+
const presetSection = options.presets && options.presets.length > 0
|
|
1453
|
+
? `\nProvider presets applied:\n${options.presets
|
|
1454
|
+
.map((preset) => ` ${preset}`)
|
|
1455
|
+
.join("\n")}\n See docs/integrations.md for setup notes and follow-up steps.\n`
|
|
1456
|
+
: "";
|
|
1098
1457
|
const openStep = options.api
|
|
1099
1458
|
? " open http://localhost:3000 for the API landing page"
|
|
1100
1459
|
: " open http://localhost:3000/sign-up";
|
|
@@ -1108,7 +1467,7 @@ ${plannedFiles}`;
|
|
|
1108
1467
|
`
|
|
1109
1468
|
: "";
|
|
1110
1469
|
return `Created ${result.name} at ${result.targetDir}
|
|
1111
|
-
|
|
1470
|
+
${presetSection}
|
|
1112
1471
|
Next steps:
|
|
1113
1472
|
cd ${result.targetDir}
|
|
1114
1473
|
${pm} install
|
|
@@ -1195,13 +1554,13 @@ function databaseCommandNextSteps(result) {
|
|
|
1195
1554
|
Command:
|
|
1196
1555
|
${command}`;
|
|
1197
1556
|
}
|
|
1198
|
-
function
|
|
1557
|
+
function databaseSchemaSyncNextSteps(result) {
|
|
1199
1558
|
const { changedFiles, skippedFiles } = changedFileLines({
|
|
1200
1559
|
createdFiles: result.createdFiles,
|
|
1201
1560
|
updatedFiles: result.updatedFiles,
|
|
1202
1561
|
skippedFiles: result.skippedFiles,
|
|
1203
1562
|
});
|
|
1204
|
-
const prefix = result.dryRun ? "Would
|
|
1563
|
+
const prefix = result.dryRun ? "Would sync" : "Synced";
|
|
1205
1564
|
const tables = result.tables.join(", ");
|
|
1206
1565
|
return `${prefix} Beignet ${result.dialect} database schema (${tables}) in ${result.cwd}
|
|
1207
1566
|
|
|
@@ -1303,7 +1662,7 @@ function makeOutboxNextSteps(result) {
|
|
|
1303
1662
|
function makeListenerNextSteps(result) {
|
|
1304
1663
|
return makeNextSteps(result, "listener", [
|
|
1305
1664
|
"Replace the starter handler with the event reaction.",
|
|
1306
|
-
"Register the listener collection from
|
|
1665
|
+
"Register the listener collection from server provider wiring.",
|
|
1307
1666
|
]);
|
|
1308
1667
|
}
|
|
1309
1668
|
function makeScheduleNextSteps(result, options = {}) {
|