@beignet/cli 0.0.3 → 0.0.4
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 +205 -0
- package/README.md +379 -61
- package/dist/ansi.d.ts +10 -0
- package/dist/ansi.d.ts.map +1 -0
- package/dist/ansi.js +20 -0
- package/dist/ansi.js.map +1 -0
- package/dist/choices.d.ts +72 -0
- package/dist/choices.d.ts.map +1 -0
- package/dist/choices.js +88 -0
- package/dist/choices.js.map +1 -0
- package/dist/completion.d.ts +47 -0
- package/dist/completion.d.ts.map +1 -0
- package/dist/completion.js +123 -0
- package/dist/completion.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/create-prompts.d.ts +42 -0
- package/dist/create-prompts.d.ts.map +1 -0
- package/dist/create-prompts.js +136 -0
- package/dist/create-prompts.js.map +1 -0
- package/dist/create.d.ts +4 -1
- package/dist/create.d.ts.map +1 -1
- package/dist/create.js +16 -26
- package/dist/create.js.map +1 -1
- package/dist/db.d.ts +1 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +37 -2
- package/dist/db.js.map +1 -1
- package/dist/github-annotations.d.ts +18 -0
- package/dist/github-annotations.d.ts.map +1 -0
- package/dist/github-annotations.js +22 -0
- package/dist/github-annotations.js.map +1 -0
- package/dist/index.d.ts +1 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +657 -588
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +21 -2
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +1938 -131
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +20 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +17 -0
- package/dist/lib.js.map +1 -0
- package/dist/lint.d.ts +10 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +340 -33
- package/dist/lint.js.map +1 -1
- package/dist/make.d.ts +20 -3
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +1756 -394
- package/dist/make.js.map +1 -1
- package/dist/outbox.d.ts +24 -0
- package/dist/outbox.d.ts.map +1 -0
- package/dist/outbox.js +138 -0
- package/dist/outbox.js.map +1 -0
- package/dist/schedule.d.ts +36 -0
- package/dist/schedule.d.ts.map +1 -0
- package/dist/schedule.js +155 -0
- package/dist/schedule.js.map +1 -0
- package/dist/task.d.ts +26 -0
- package/dist/task.d.ts.map +1 -0
- package/dist/task.js +106 -0
- package/dist/task.js.map +1 -0
- package/dist/templates.d.ts +3 -32
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +1002 -527
- package/dist/templates.js.map +1 -1
- package/dist/version.d.ts +8 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +18 -0
- package/dist/version.js.map +1 -0
- package/package.json +9 -8
- package/src/ansi.ts +30 -0
- package/src/choices.ts +137 -0
- package/src/completion.ts +169 -0
- package/src/config.ts +16 -0
- package/src/create-prompts.ts +182 -0
- package/src/create.ts +24 -31
- package/src/db.ts +60 -4
- package/src/github-annotations.ts +37 -0
- package/src/index.ts +1067 -803
- package/src/inspect.ts +2859 -115
- package/src/lib.ts +45 -0
- package/src/lint.ts +493 -39
- package/src/make.ts +2181 -405
- package/src/outbox.ts +249 -0
- package/src/schedule.ts +272 -0
- package/src/task.ts +169 -0
- package/src/templates.ts +1073 -567
- package/src/version.ts +20 -0
- package/dist/create-bin.d.ts +0 -3
- package/dist/create-bin.d.ts.map +0 -1
- package/dist/create-bin.js +0 -9
- package/dist/create-bin.js.map +0 -1
- package/src/create-bin.ts +0 -11
package/src/index.ts
CHANGED
|
@@ -7,97 +7,59 @@ import {
|
|
|
7
7
|
buildRouteMap,
|
|
8
8
|
type CommandContext,
|
|
9
9
|
type FlagParametersForType,
|
|
10
|
+
proposeCompletions,
|
|
10
11
|
run,
|
|
11
12
|
type StricliDynamicCommandContext,
|
|
12
13
|
type StricliProcess,
|
|
13
14
|
text_en,
|
|
14
15
|
} from "@stricli/core";
|
|
15
|
-
import type { CreateOptions } from "./create.js";
|
|
16
|
-
import { createProject } from "./create.js";
|
|
17
|
-
import { type DatabaseCommand, runDatabaseCommand } from "./db.js";
|
|
18
|
-
import {
|
|
19
|
-
applyDoctorFixes,
|
|
20
|
-
formatDoctor,
|
|
21
|
-
formatRoutes,
|
|
22
|
-
inspectApp,
|
|
23
|
-
} from "./inspect.js";
|
|
24
|
-
import { formatLint, lintApp } from "./lint.js";
|
|
25
|
-
import {
|
|
26
|
-
makeAdapter,
|
|
27
|
-
makeContract,
|
|
28
|
-
makeEvent,
|
|
29
|
-
makeFactory,
|
|
30
|
-
makeFeature,
|
|
31
|
-
makeFeatureAddonChoices,
|
|
32
|
-
makeJob,
|
|
33
|
-
makeListener,
|
|
34
|
-
makeNotification,
|
|
35
|
-
makePolicy,
|
|
36
|
-
makePort,
|
|
37
|
-
makeResource,
|
|
38
|
-
makeSchedule,
|
|
39
|
-
makeSeed,
|
|
40
|
-
makeTest,
|
|
41
|
-
makeUpload,
|
|
42
|
-
makeUseCase,
|
|
43
|
-
} from "./make.js";
|
|
44
|
-
|
|
45
|
-
export type { MakeFeatureAddon } from "./make.js";
|
|
46
|
-
|
|
47
16
|
import {
|
|
17
|
+
type CompletionShell,
|
|
18
|
+
completionShellChoices,
|
|
48
19
|
type FeatureName,
|
|
49
20
|
featureChoices,
|
|
50
21
|
type IntegrationName,
|
|
51
22
|
integrationChoices,
|
|
23
|
+
type MakeFeatureAddon,
|
|
24
|
+
makeFeatureAddonChoices,
|
|
52
25
|
type PackageManager,
|
|
53
26
|
type PresetName,
|
|
27
|
+
packageManagerChoices,
|
|
54
28
|
presetChoices,
|
|
55
29
|
type TemplateName,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
createProject,
|
|
62
|
-
formatDoctor,
|
|
63
|
-
formatLint,
|
|
64
|
-
formatRoutes,
|
|
65
|
-
type IntegrationName,
|
|
66
|
-
inspectApp,
|
|
67
|
-
lintApp,
|
|
68
|
-
makeAdapter,
|
|
69
|
-
makeContract,
|
|
70
|
-
makeEvent,
|
|
71
|
-
makeFactory,
|
|
72
|
-
makeFeature,
|
|
73
|
-
makeFeatureAddonChoices,
|
|
74
|
-
makeJob,
|
|
75
|
-
makeListener,
|
|
76
|
-
makeNotification,
|
|
77
|
-
makePolicy,
|
|
78
|
-
makePort,
|
|
79
|
-
makeResource,
|
|
80
|
-
makeSchedule,
|
|
81
|
-
makeSeed,
|
|
82
|
-
makeTest,
|
|
83
|
-
makeUpload,
|
|
84
|
-
makeUseCase,
|
|
85
|
-
runDatabaseCommand,
|
|
86
|
-
};
|
|
30
|
+
templateChoices,
|
|
31
|
+
} from "./choices.js";
|
|
32
|
+
// Type-only: the runtime module stays behind the create command's lazy loader.
|
|
33
|
+
import type { CreateSelections } from "./create-prompts.js";
|
|
34
|
+
import { getCliVersion } from "./version.js";
|
|
87
35
|
|
|
88
36
|
type CliContext = CommandContext & {
|
|
89
37
|
readonly process: StricliProcess;
|
|
90
38
|
};
|
|
91
39
|
|
|
40
|
+
type OutputFormat = "human" | "json" | "github";
|
|
41
|
+
|
|
42
|
+
const outputFormatChoices = [
|
|
43
|
+
"human",
|
|
44
|
+
"json",
|
|
45
|
+
"github",
|
|
46
|
+
] as const satisfies readonly OutputFormat[];
|
|
47
|
+
|
|
92
48
|
type CreateFlags = {
|
|
93
49
|
template: TemplateName;
|
|
94
|
-
preset
|
|
50
|
+
preset?: PresetName;
|
|
95
51
|
packageManager?: PackageManager;
|
|
96
|
-
feature?: readonly FeatureName[];
|
|
97
52
|
features?: readonly FeatureName[];
|
|
98
|
-
integration?: readonly IntegrationName[];
|
|
99
53
|
integrations?: readonly IntegrationName[];
|
|
54
|
+
yes?: boolean;
|
|
100
55
|
force?: boolean;
|
|
56
|
+
dryRun?: boolean;
|
|
57
|
+
json?: boolean;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
type CompletionFlags = {
|
|
61
|
+
shell?: CompletionShell;
|
|
62
|
+
json?: boolean;
|
|
101
63
|
};
|
|
102
64
|
|
|
103
65
|
type MakeFlags = {
|
|
@@ -106,8 +68,15 @@ type MakeFlags = {
|
|
|
106
68
|
json?: boolean;
|
|
107
69
|
};
|
|
108
70
|
|
|
71
|
+
type MakeResourceFlags = MakeFlags & {
|
|
72
|
+
auth?: boolean;
|
|
73
|
+
tenant?: boolean;
|
|
74
|
+
events?: boolean;
|
|
75
|
+
softDelete?: boolean;
|
|
76
|
+
};
|
|
77
|
+
|
|
109
78
|
type MakeFeatureFlags = MakeFlags & {
|
|
110
|
-
with?: readonly
|
|
79
|
+
with?: readonly MakeFeatureAddon[];
|
|
111
80
|
};
|
|
112
81
|
|
|
113
82
|
type MakeListenerFlags = MakeFlags & {
|
|
@@ -120,8 +89,15 @@ type MakeScheduleFlags = MakeFlags & {
|
|
|
120
89
|
route?: boolean;
|
|
121
90
|
};
|
|
122
91
|
|
|
123
|
-
type
|
|
92
|
+
type RoutesFlags = {
|
|
93
|
+
json?: boolean;
|
|
94
|
+
cwd?: string;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
type LintFlags = {
|
|
124
98
|
json?: boolean;
|
|
99
|
+
cwd?: string;
|
|
100
|
+
format?: OutputFormat;
|
|
125
101
|
};
|
|
126
102
|
|
|
127
103
|
type DbFlags = {
|
|
@@ -129,21 +105,45 @@ type DbFlags = {
|
|
|
129
105
|
dryRun?: boolean;
|
|
130
106
|
};
|
|
131
107
|
|
|
108
|
+
type TaskRunFlags = {
|
|
109
|
+
json?: boolean;
|
|
110
|
+
input?: string;
|
|
111
|
+
module?: string;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
type OutboxDrainFlags = {
|
|
115
|
+
json?: boolean;
|
|
116
|
+
module?: string;
|
|
117
|
+
batchSize?: number;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
type ScheduleRunFlags = {
|
|
121
|
+
json?: boolean;
|
|
122
|
+
module?: string;
|
|
123
|
+
payload?: string;
|
|
124
|
+
id?: string;
|
|
125
|
+
attempt?: number;
|
|
126
|
+
scheduledAt?: string;
|
|
127
|
+
triggeredAt?: string;
|
|
128
|
+
source?: string;
|
|
129
|
+
};
|
|
130
|
+
|
|
132
131
|
type DoctorFlags = {
|
|
133
132
|
json?: boolean;
|
|
134
133
|
strict?: boolean;
|
|
135
134
|
fix?: boolean;
|
|
135
|
+
cwd?: string;
|
|
136
|
+
format?: OutputFormat;
|
|
136
137
|
};
|
|
137
138
|
|
|
138
|
-
const templateChoices = ["next"] as const satisfies readonly TemplateName[];
|
|
139
|
-
const packageManagerChoices = [
|
|
140
|
-
"bun",
|
|
141
|
-
"npm",
|
|
142
|
-
"pnpm",
|
|
143
|
-
"yarn",
|
|
144
|
-
] as const satisfies readonly PackageManager[];
|
|
145
|
-
|
|
146
139
|
const parseString = (input: string): string => input;
|
|
140
|
+
const parsePositiveInteger = (input: string): number => {
|
|
141
|
+
const value = Number(input);
|
|
142
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
143
|
+
throw new Error("Expected a positive integer.");
|
|
144
|
+
}
|
|
145
|
+
return value;
|
|
146
|
+
};
|
|
147
147
|
|
|
148
148
|
const forceFlag = {
|
|
149
149
|
kind: "boolean",
|
|
@@ -174,21 +174,79 @@ const parsedStringFlag = (brief: string) =>
|
|
|
174
174
|
brief,
|
|
175
175
|
}) as const;
|
|
176
176
|
|
|
177
|
+
const cwdFlag = parsedStringFlag(
|
|
178
|
+
"App directory to inspect. Defaults to the current working directory.",
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const formatFlag = {
|
|
182
|
+
kind: "enum",
|
|
183
|
+
values: outputFormatChoices,
|
|
184
|
+
optional: true,
|
|
185
|
+
brief:
|
|
186
|
+
"Output format. Defaults to human, or github when GITHUB_ACTIONS=true.",
|
|
187
|
+
} as const;
|
|
188
|
+
|
|
177
189
|
const makeFlagParameters = {
|
|
178
190
|
force: forceFlag,
|
|
179
191
|
dryRun: dryRunFlag,
|
|
180
192
|
json: jsonFlag,
|
|
181
193
|
} satisfies FlagParametersForType<MakeFlags, CliContext>;
|
|
182
194
|
|
|
183
|
-
const
|
|
195
|
+
const routesFlagParameters = {
|
|
196
|
+
json: jsonFlag,
|
|
197
|
+
cwd: cwdFlag,
|
|
198
|
+
} satisfies FlagParametersForType<RoutesFlags, CliContext>;
|
|
199
|
+
|
|
200
|
+
const lintFlagParameters = {
|
|
184
201
|
json: jsonFlag,
|
|
185
|
-
|
|
202
|
+
cwd: cwdFlag,
|
|
203
|
+
format: formatFlag,
|
|
204
|
+
} satisfies FlagParametersForType<LintFlags, CliContext>;
|
|
186
205
|
|
|
187
206
|
const dbFlagParameters = {
|
|
188
207
|
json: jsonFlag,
|
|
189
208
|
dryRun: dryRunFlag,
|
|
190
209
|
} satisfies FlagParametersForType<DbFlags, CliContext>;
|
|
191
210
|
|
|
211
|
+
const taskRunFlagParameters = {
|
|
212
|
+
json: jsonFlag,
|
|
213
|
+
input: parsedStringFlag("JSON input passed to the task schema."),
|
|
214
|
+
module: parsedStringFlag(
|
|
215
|
+
"Task registry module. Defaults to server/tasks.ts.",
|
|
216
|
+
),
|
|
217
|
+
} satisfies FlagParametersForType<TaskRunFlags, CliContext>;
|
|
218
|
+
|
|
219
|
+
const outboxDrainFlagParameters = {
|
|
220
|
+
json: jsonFlag,
|
|
221
|
+
module: parsedStringFlag(
|
|
222
|
+
"Outbox registry module. Defaults to server/outbox.ts.",
|
|
223
|
+
),
|
|
224
|
+
batchSize: {
|
|
225
|
+
kind: "parsed",
|
|
226
|
+
parse: parsePositiveInteger,
|
|
227
|
+
optional: true,
|
|
228
|
+
brief: "Maximum messages to claim in one drain pass.",
|
|
229
|
+
},
|
|
230
|
+
} satisfies FlagParametersForType<OutboxDrainFlags, CliContext>;
|
|
231
|
+
|
|
232
|
+
const scheduleRunFlagParameters = {
|
|
233
|
+
json: jsonFlag,
|
|
234
|
+
module: parsedStringFlag(
|
|
235
|
+
"Schedule registry module. Defaults to server/schedules.ts.",
|
|
236
|
+
),
|
|
237
|
+
payload: parsedStringFlag("JSON payload passed to the schedule schema."),
|
|
238
|
+
id: parsedStringFlag("Provider or app schedule run ID."),
|
|
239
|
+
attempt: {
|
|
240
|
+
kind: "parsed",
|
|
241
|
+
parse: parsePositiveInteger,
|
|
242
|
+
optional: true,
|
|
243
|
+
brief: "One-based provider attempt number.",
|
|
244
|
+
},
|
|
245
|
+
scheduledAt: parsedStringFlag("Provider scheduled timestamp."),
|
|
246
|
+
triggeredAt: parsedStringFlag("Schedule trigger timestamp."),
|
|
247
|
+
source: parsedStringFlag("Provider or app source label."),
|
|
248
|
+
} satisfies FlagParametersForType<ScheduleRunFlags, CliContext>;
|
|
249
|
+
|
|
192
250
|
const namePositional = {
|
|
193
251
|
kind: "tuple",
|
|
194
252
|
parameters: [
|
|
@@ -200,12 +258,40 @@ const namePositional = {
|
|
|
200
258
|
],
|
|
201
259
|
} as const;
|
|
202
260
|
|
|
203
|
-
|
|
261
|
+
function resolveOutputFormat(flags: {
|
|
262
|
+
json?: boolean;
|
|
263
|
+
format?: OutputFormat;
|
|
264
|
+
}): OutputFormat {
|
|
265
|
+
if (flags.json) {
|
|
266
|
+
if (flags.format && flags.format !== "json") {
|
|
267
|
+
throw new Error(
|
|
268
|
+
`--json conflicts with --format ${flags.format}. Pass one of them.`,
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
return "json";
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (flags.format) return flags.format;
|
|
275
|
+
return process.env.GITHUB_ACTIONS === "true" ? "github" : "human";
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function useColor(): boolean {
|
|
279
|
+
return process.stdout.isTTY === true && !process.env.NO_COLOR;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const createCommand = buildCommand<
|
|
283
|
+
CreateFlags,
|
|
284
|
+
[string | undefined],
|
|
285
|
+
CliContext
|
|
286
|
+
>({
|
|
204
287
|
docs: {
|
|
205
288
|
brief: "Create a new Beignet app.",
|
|
206
289
|
fullDescription: `Available features: ${featureChoices.join(", ")}
|
|
207
290
|
|
|
208
|
-
Available integrations: ${integrationChoices.join(", ")}
|
|
291
|
+
Available integrations: ${integrationChoices.join(", ")}
|
|
292
|
+
|
|
293
|
+
Running create on an interactive terminal without selection flags opens a
|
|
294
|
+
prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
|
|
209
295
|
},
|
|
210
296
|
parameters: {
|
|
211
297
|
flags: {
|
|
@@ -218,8 +304,8 @@ Available integrations: ${integrationChoices.join(", ")}`,
|
|
|
218
304
|
preset: {
|
|
219
305
|
kind: "enum",
|
|
220
306
|
values: presetChoices,
|
|
221
|
-
|
|
222
|
-
brief: "Starter preset to generate.",
|
|
307
|
+
optional: true,
|
|
308
|
+
brief: "Starter preset to generate. Defaults to standard.",
|
|
223
309
|
},
|
|
224
310
|
packageManager: {
|
|
225
311
|
kind: "enum",
|
|
@@ -227,35 +313,29 @@ Available integrations: ${integrationChoices.join(", ")}`,
|
|
|
227
313
|
optional: true,
|
|
228
314
|
brief: "Package manager to use in next-step commands.",
|
|
229
315
|
},
|
|
230
|
-
feature: {
|
|
231
|
-
kind: "enum",
|
|
232
|
-
values: featureChoices,
|
|
233
|
-
optional: true,
|
|
234
|
-
variadic: true,
|
|
235
|
-
brief: "Add a starter feature. Repeatable.",
|
|
236
|
-
},
|
|
237
316
|
features: {
|
|
238
317
|
kind: "enum",
|
|
239
318
|
values: featureChoices,
|
|
240
319
|
optional: true,
|
|
241
320
|
variadic: ",",
|
|
242
|
-
brief: "Add comma-separated
|
|
243
|
-
},
|
|
244
|
-
integration: {
|
|
245
|
-
kind: "enum",
|
|
246
|
-
values: integrationChoices,
|
|
247
|
-
optional: true,
|
|
248
|
-
variadic: true,
|
|
249
|
-
brief: "Add a first-party integration. Repeatable.",
|
|
321
|
+
brief: "Add starter features as a comma-separated list.",
|
|
250
322
|
},
|
|
251
323
|
integrations: {
|
|
252
324
|
kind: "enum",
|
|
253
325
|
values: integrationChoices,
|
|
254
326
|
optional: true,
|
|
255
327
|
variadic: ",",
|
|
256
|
-
brief: "Add
|
|
328
|
+
brief: "Add first-party integrations as a comma-separated list.",
|
|
329
|
+
},
|
|
330
|
+
yes: {
|
|
331
|
+
kind: "boolean",
|
|
332
|
+
optional: true,
|
|
333
|
+
withNegated: false,
|
|
334
|
+
brief: "Skip interactive prompts and use the defaults.",
|
|
257
335
|
},
|
|
258
336
|
force: forceFlag,
|
|
337
|
+
dryRun: dryRunFlag,
|
|
338
|
+
json: jsonFlag,
|
|
259
339
|
},
|
|
260
340
|
positional: {
|
|
261
341
|
kind: "tuple",
|
|
@@ -264,45 +344,92 @@ Available integrations: ${integrationChoices.join(", ")}`,
|
|
|
264
344
|
parse: parseString,
|
|
265
345
|
placeholder: "directory",
|
|
266
346
|
brief: "Project directory to create.",
|
|
347
|
+
optional: true,
|
|
267
348
|
},
|
|
268
349
|
],
|
|
269
350
|
},
|
|
270
351
|
},
|
|
271
|
-
async
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
352
|
+
loader: async () => {
|
|
353
|
+
const { createProject } = await import("./create.js");
|
|
354
|
+
const { promptCreateSelections, shouldPromptInteractively } = await import(
|
|
355
|
+
"./create-prompts.js"
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
return async function runCreate(
|
|
359
|
+
this: CliContext,
|
|
360
|
+
flags: CreateFlags,
|
|
361
|
+
directory: string | undefined,
|
|
362
|
+
) {
|
|
363
|
+
let selections: CreateSelections = {
|
|
364
|
+
directory,
|
|
365
|
+
preset: flags.preset,
|
|
366
|
+
features: flags.features,
|
|
367
|
+
integrations: flags.integrations,
|
|
368
|
+
packageManager: flags.packageManager,
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const interactive = shouldPromptInteractively(
|
|
372
|
+
{ ...selections, yes: flags.yes },
|
|
373
|
+
{
|
|
374
|
+
stdin: process.stdin.isTTY === true,
|
|
375
|
+
stdout: process.stdout.isTTY === true,
|
|
376
|
+
},
|
|
377
|
+
);
|
|
378
|
+
if (interactive) {
|
|
379
|
+
const answers = await promptCreateSelections(selections);
|
|
380
|
+
if (answers === undefined) {
|
|
381
|
+
this.process.stderr.write("Cancelled.\n");
|
|
382
|
+
this.process.exitCode = 2;
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
selections = answers;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (selections.directory === undefined) {
|
|
389
|
+
throw new Error(
|
|
390
|
+
"Project directory is required. Pass beignet create <directory>, or run beignet create in an interactive terminal.",
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const integrations = uniqueValues([...(selections.integrations ?? [])]);
|
|
395
|
+
const result = await createProject({
|
|
396
|
+
name: selections.directory,
|
|
397
|
+
template: flags.template,
|
|
398
|
+
preset: selections.preset ?? "standard",
|
|
399
|
+
features: uniqueValues([...(selections.features ?? [])]),
|
|
400
|
+
packageManager: selections.packageManager,
|
|
401
|
+
integrations,
|
|
402
|
+
force: Boolean(flags.force),
|
|
403
|
+
dryRun: Boolean(flags.dryRun),
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
writeOutput(
|
|
407
|
+
this,
|
|
408
|
+
flags.json
|
|
409
|
+
? JSON.stringify(result, null, 2)
|
|
410
|
+
: nextSteps(result, { integrations }),
|
|
411
|
+
);
|
|
412
|
+
};
|
|
290
413
|
},
|
|
291
414
|
});
|
|
292
415
|
|
|
293
|
-
const routesCommand = buildCommand<
|
|
416
|
+
const routesCommand = buildCommand<RoutesFlags, [], CliContext>({
|
|
294
417
|
docs: {
|
|
295
418
|
brief: "Inspect registered Beignet routes.",
|
|
296
419
|
},
|
|
297
420
|
parameters: {
|
|
298
|
-
flags:
|
|
421
|
+
flags: routesFlagParameters,
|
|
299
422
|
},
|
|
300
|
-
async
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
423
|
+
loader: async () => {
|
|
424
|
+
const { formatRoutes, inspectApp } = await import("./inspect.js");
|
|
425
|
+
|
|
426
|
+
return async function runRoutes(this: CliContext, flags: RoutesFlags) {
|
|
427
|
+
const result = await inspectApp({ cwd: flags.cwd });
|
|
428
|
+
writeOutput(
|
|
429
|
+
this,
|
|
430
|
+
flags.json ? JSON.stringify(result, null, 2) : formatRoutes(result),
|
|
431
|
+
);
|
|
432
|
+
};
|
|
306
433
|
},
|
|
307
434
|
});
|
|
308
435
|
|
|
@@ -325,50 +452,79 @@ const doctorCommand = buildCommand<DoctorFlags, [], CliContext>({
|
|
|
325
452
|
withNegated: false,
|
|
326
453
|
brief: "Apply low-risk fixes before reporting.",
|
|
327
454
|
},
|
|
455
|
+
cwd: cwdFlag,
|
|
456
|
+
format: formatFlag,
|
|
328
457
|
},
|
|
329
458
|
},
|
|
330
|
-
async
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
)
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
459
|
+
loader: async () => {
|
|
460
|
+
const { applyDoctorFixes, formatDoctor, formatDoctorGithub, inspectApp } =
|
|
461
|
+
await import("./inspect.js");
|
|
462
|
+
|
|
463
|
+
return async function runDoctor(this: CliContext, flags: DoctorFlags) {
|
|
464
|
+
const format = resolveOutputFormat(flags);
|
|
465
|
+
const fixes = flags.fix
|
|
466
|
+
? await applyDoctorFixes({
|
|
467
|
+
cwd: flags.cwd,
|
|
468
|
+
strict: Boolean(flags.strict),
|
|
469
|
+
})
|
|
470
|
+
: [];
|
|
471
|
+
const result = await inspectApp({
|
|
472
|
+
cwd: flags.cwd,
|
|
473
|
+
strict: Boolean(flags.strict),
|
|
474
|
+
});
|
|
475
|
+
result.fixes = fixes;
|
|
476
|
+
writeOutput(
|
|
477
|
+
this,
|
|
478
|
+
format === "json"
|
|
479
|
+
? JSON.stringify(result, null, 2)
|
|
480
|
+
: format === "github"
|
|
481
|
+
? formatDoctorGithub(result)
|
|
482
|
+
: formatDoctor(result, { color: useColor() }),
|
|
483
|
+
);
|
|
484
|
+
if (
|
|
485
|
+
result.diagnostics.some(
|
|
486
|
+
(diagnostic) =>
|
|
487
|
+
diagnostic.severity === "error" ||
|
|
488
|
+
(flags.strict && diagnostic.severity === "warning"),
|
|
489
|
+
)
|
|
490
|
+
) {
|
|
491
|
+
this.process.exitCode = 1;
|
|
492
|
+
}
|
|
493
|
+
};
|
|
349
494
|
},
|
|
350
495
|
});
|
|
351
496
|
|
|
352
|
-
const lintCommand = buildCommand<
|
|
497
|
+
const lintCommand = buildCommand<LintFlags, [], CliContext>({
|
|
353
498
|
docs: {
|
|
354
499
|
brief: "Check Beignet dependency direction conventions.",
|
|
355
500
|
},
|
|
356
501
|
parameters: {
|
|
357
|
-
flags:
|
|
502
|
+
flags: lintFlagParameters,
|
|
358
503
|
},
|
|
359
|
-
async
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
504
|
+
loader: async () => {
|
|
505
|
+
const { formatLint, formatLintGithub, lintApp } = await import("./lint.js");
|
|
506
|
+
|
|
507
|
+
return async function runLint(this: CliContext, flags: LintFlags) {
|
|
508
|
+
const format = resolveOutputFormat(flags);
|
|
509
|
+
const result = await lintApp({ cwd: flags.cwd });
|
|
510
|
+
writeOutput(
|
|
511
|
+
this,
|
|
512
|
+
format === "json"
|
|
513
|
+
? JSON.stringify(result, null, 2)
|
|
514
|
+
: format === "github"
|
|
515
|
+
? formatLintGithub(result)
|
|
516
|
+
: formatLint(result, { color: useColor() }),
|
|
517
|
+
);
|
|
518
|
+
if (result.diagnostics.length > 0) {
|
|
519
|
+
this.process.exitCode = 1;
|
|
520
|
+
}
|
|
521
|
+
};
|
|
368
522
|
},
|
|
369
523
|
});
|
|
370
524
|
|
|
371
|
-
|
|
525
|
+
type DatabaseCommandName = "generate" | "migrate" | "reset" | "seed";
|
|
526
|
+
|
|
527
|
+
function databaseCommand(command: DatabaseCommandName) {
|
|
372
528
|
return buildCommand<DbFlags, [], CliContext>({
|
|
373
529
|
docs: {
|
|
374
530
|
brief: `Run the app's db:${command} script.`,
|
|
@@ -376,22 +532,26 @@ function databaseCommand(command: DatabaseCommand) {
|
|
|
376
532
|
parameters: {
|
|
377
533
|
flags: dbFlagParameters,
|
|
378
534
|
},
|
|
379
|
-
async
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
535
|
+
loader: async () => {
|
|
536
|
+
const { runDatabaseCommand } = await import("./db.js");
|
|
537
|
+
|
|
538
|
+
return async function runDb(this: CliContext, flags: DbFlags) {
|
|
539
|
+
const result = await runDatabaseCommand({
|
|
540
|
+
command,
|
|
541
|
+
captureOutput: Boolean(flags.json),
|
|
542
|
+
dryRun: Boolean(flags.dryRun),
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
writeOutput(
|
|
546
|
+
this,
|
|
547
|
+
flags.json
|
|
548
|
+
? JSON.stringify(result, null, 2)
|
|
549
|
+
: databaseCommandNextSteps(result),
|
|
550
|
+
);
|
|
551
|
+
if (result.exitCode !== 0) {
|
|
552
|
+
this.process.exitCode = result.exitCode;
|
|
553
|
+
}
|
|
554
|
+
};
|
|
395
555
|
},
|
|
396
556
|
});
|
|
397
557
|
}
|
|
@@ -408,409 +568,461 @@ const dbRoutes = buildRouteMap({
|
|
|
408
568
|
},
|
|
409
569
|
});
|
|
410
570
|
|
|
411
|
-
const
|
|
571
|
+
const taskRunCommand = buildCommand<TaskRunFlags, [string], CliContext>({
|
|
412
572
|
docs: {
|
|
413
|
-
brief: "
|
|
573
|
+
brief: "Run an app-owned operational task.",
|
|
414
574
|
},
|
|
415
575
|
parameters: {
|
|
416
|
-
flags:
|
|
576
|
+
flags: taskRunFlagParameters,
|
|
417
577
|
positional: namePositional,
|
|
418
578
|
},
|
|
419
|
-
async
|
|
420
|
-
const
|
|
421
|
-
name,
|
|
422
|
-
force: Boolean(flags.force),
|
|
423
|
-
dryRun: Boolean(flags.dryRun),
|
|
424
|
-
});
|
|
425
|
-
writeOutput(
|
|
426
|
-
this,
|
|
427
|
-
flags.json
|
|
428
|
-
? JSON.stringify(result, null, 2)
|
|
429
|
-
: makeResourceNextSteps(result),
|
|
430
|
-
);
|
|
431
|
-
},
|
|
432
|
-
});
|
|
579
|
+
loader: async () => {
|
|
580
|
+
const { runAppTask } = await import("./task.js");
|
|
433
581
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
flags: {
|
|
441
|
-
...makeFlagParameters,
|
|
442
|
-
with: {
|
|
443
|
-
kind: "enum",
|
|
444
|
-
values: makeFeatureAddonChoices,
|
|
445
|
-
optional: true,
|
|
446
|
-
variadic: ",",
|
|
447
|
-
brief:
|
|
448
|
-
"Add feature-owned artifacts. Supports policy, event/events, job/jobs, and upload/uploads.",
|
|
449
|
-
},
|
|
450
|
-
} satisfies FlagParametersForType<MakeFeatureFlags, CliContext>,
|
|
451
|
-
positional: namePositional,
|
|
452
|
-
},
|
|
453
|
-
async func(flags, name) {
|
|
454
|
-
const result = await makeFeature({
|
|
582
|
+
return async function runTask(
|
|
583
|
+
this: CliContext,
|
|
584
|
+
flags: TaskRunFlags,
|
|
585
|
+
name: string,
|
|
586
|
+
) {
|
|
587
|
+
const result = await runAppTask({
|
|
455
588
|
name,
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
dryRun: Boolean(flags.dryRun),
|
|
589
|
+
input: flags.input,
|
|
590
|
+
modulePath: flags.module,
|
|
459
591
|
});
|
|
592
|
+
|
|
460
593
|
writeOutput(
|
|
461
594
|
this,
|
|
462
595
|
flags.json
|
|
463
596
|
? JSON.stringify(result, null, 2)
|
|
464
|
-
:
|
|
597
|
+
: appTaskRunNextSteps(result),
|
|
465
598
|
);
|
|
466
|
-
}
|
|
467
|
-
},
|
|
468
|
-
);
|
|
469
|
-
|
|
470
|
-
const makeContractCommand = buildCommand<MakeFlags, [string], CliContext>({
|
|
471
|
-
docs: {
|
|
472
|
-
brief: "Generate a contract group.",
|
|
473
|
-
},
|
|
474
|
-
parameters: {
|
|
475
|
-
flags: makeFlagParameters,
|
|
476
|
-
positional: namePositional,
|
|
477
|
-
},
|
|
478
|
-
async func(flags, name) {
|
|
479
|
-
const result = await makeContract({
|
|
480
|
-
name,
|
|
481
|
-
force: Boolean(flags.force),
|
|
482
|
-
dryRun: Boolean(flags.dryRun),
|
|
483
|
-
});
|
|
484
|
-
writeOutput(
|
|
485
|
-
this,
|
|
486
|
-
flags.json
|
|
487
|
-
? JSON.stringify(result, null, 2)
|
|
488
|
-
: makeContractNextSteps(result),
|
|
489
|
-
);
|
|
599
|
+
};
|
|
490
600
|
},
|
|
491
601
|
});
|
|
492
602
|
|
|
493
|
-
const
|
|
603
|
+
const taskRoutes = buildRouteMap({
|
|
494
604
|
docs: {
|
|
495
|
-
brief: "
|
|
496
|
-
},
|
|
497
|
-
parameters: {
|
|
498
|
-
flags: makeFlagParameters,
|
|
499
|
-
positional: namePositional,
|
|
605
|
+
brief: "Run Beignet app operational tasks.",
|
|
500
606
|
},
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
name,
|
|
504
|
-
force: Boolean(flags.force),
|
|
505
|
-
dryRun: Boolean(flags.dryRun),
|
|
506
|
-
});
|
|
507
|
-
writeOutput(
|
|
508
|
-
this,
|
|
509
|
-
flags.json
|
|
510
|
-
? JSON.stringify(result, null, 2)
|
|
511
|
-
: makeUseCaseNextSteps(result),
|
|
512
|
-
);
|
|
607
|
+
routes: {
|
|
608
|
+
run: taskRunCommand,
|
|
513
609
|
},
|
|
514
610
|
});
|
|
515
611
|
|
|
516
|
-
const
|
|
612
|
+
const outboxDrainCommand = buildCommand<OutboxDrainFlags, [], CliContext>({
|
|
517
613
|
docs: {
|
|
518
|
-
brief: "
|
|
614
|
+
brief: "Run one app-owned outbox drain pass.",
|
|
519
615
|
},
|
|
520
616
|
parameters: {
|
|
521
|
-
flags:
|
|
522
|
-
positional: namePositional,
|
|
523
|
-
},
|
|
524
|
-
async func(flags, name) {
|
|
525
|
-
const result = await makeTest({
|
|
526
|
-
name,
|
|
527
|
-
force: Boolean(flags.force),
|
|
528
|
-
dryRun: Boolean(flags.dryRun),
|
|
529
|
-
});
|
|
530
|
-
writeOutput(
|
|
531
|
-
this,
|
|
532
|
-
flags.json ? JSON.stringify(result, null, 2) : makeTestNextSteps(result),
|
|
533
|
-
);
|
|
617
|
+
flags: outboxDrainFlagParameters,
|
|
534
618
|
},
|
|
535
|
-
|
|
619
|
+
loader: async () => {
|
|
620
|
+
const { runOutboxDrain } = await import("./outbox.js");
|
|
536
621
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
});
|
|
551
|
-
writeOutput(
|
|
552
|
-
this,
|
|
553
|
-
flags.json ? JSON.stringify(result, null, 2) : makePortNextSteps(result),
|
|
554
|
-
);
|
|
622
|
+
return async function runDrain(this: CliContext, flags: OutboxDrainFlags) {
|
|
623
|
+
const result = await runOutboxDrain({
|
|
624
|
+
modulePath: flags.module,
|
|
625
|
+
batchSize: flags.batchSize,
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
writeOutput(
|
|
629
|
+
this,
|
|
630
|
+
flags.json
|
|
631
|
+
? JSON.stringify(result, null, 2)
|
|
632
|
+
: outboxDrainNextSteps(result),
|
|
633
|
+
);
|
|
634
|
+
};
|
|
555
635
|
},
|
|
556
636
|
});
|
|
557
637
|
|
|
558
|
-
const
|
|
638
|
+
const outboxRoutes = buildRouteMap({
|
|
559
639
|
docs: {
|
|
560
|
-
brief: "
|
|
561
|
-
},
|
|
562
|
-
parameters: {
|
|
563
|
-
flags: makeFlagParameters,
|
|
564
|
-
positional: namePositional,
|
|
640
|
+
brief: "Run Beignet outbox operations.",
|
|
565
641
|
},
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
name,
|
|
569
|
-
force: Boolean(flags.force),
|
|
570
|
-
dryRun: Boolean(flags.dryRun),
|
|
571
|
-
});
|
|
572
|
-
writeOutput(
|
|
573
|
-
this,
|
|
574
|
-
flags.json
|
|
575
|
-
? JSON.stringify(result, null, 2)
|
|
576
|
-
: makeAdapterNextSteps(result),
|
|
577
|
-
);
|
|
642
|
+
routes: {
|
|
643
|
+
drain: outboxDrainCommand,
|
|
578
644
|
},
|
|
579
645
|
});
|
|
580
646
|
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
647
|
+
const scheduleRunCommand = buildCommand<ScheduleRunFlags, [string], CliContext>(
|
|
648
|
+
{
|
|
649
|
+
docs: {
|
|
650
|
+
brief: "Run an app-owned schedule.",
|
|
651
|
+
},
|
|
652
|
+
parameters: {
|
|
653
|
+
flags: scheduleRunFlagParameters,
|
|
654
|
+
positional: namePositional,
|
|
655
|
+
},
|
|
656
|
+
loader: async () => {
|
|
657
|
+
const { runAppSchedule } = await import("./schedule.js");
|
|
658
|
+
|
|
659
|
+
return async function runSchedule(
|
|
660
|
+
this: CliContext,
|
|
661
|
+
flags: ScheduleRunFlags,
|
|
662
|
+
name: string,
|
|
663
|
+
) {
|
|
664
|
+
const result = await runAppSchedule({
|
|
665
|
+
name,
|
|
666
|
+
modulePath: flags.module,
|
|
667
|
+
payload: flags.payload,
|
|
668
|
+
id: flags.id,
|
|
669
|
+
attempt: flags.attempt,
|
|
670
|
+
scheduledAt: flags.scheduledAt,
|
|
671
|
+
triggeredAt: flags.triggeredAt,
|
|
672
|
+
source: flags.source,
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
writeOutput(
|
|
676
|
+
this,
|
|
677
|
+
flags.json
|
|
678
|
+
? JSON.stringify(result, null, 2)
|
|
679
|
+
: scheduleRunNextSteps(result),
|
|
680
|
+
);
|
|
681
|
+
};
|
|
682
|
+
},
|
|
601
683
|
},
|
|
602
|
-
|
|
684
|
+
);
|
|
603
685
|
|
|
604
|
-
const
|
|
686
|
+
const scheduleRoutes = buildRouteMap({
|
|
605
687
|
docs: {
|
|
606
|
-
brief: "
|
|
607
|
-
},
|
|
608
|
-
parameters: {
|
|
609
|
-
flags: makeFlagParameters,
|
|
610
|
-
positional: namePositional,
|
|
688
|
+
brief: "Run Beignet schedule operations.",
|
|
611
689
|
},
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
name,
|
|
615
|
-
force: Boolean(flags.force),
|
|
616
|
-
dryRun: Boolean(flags.dryRun),
|
|
617
|
-
});
|
|
618
|
-
writeOutput(
|
|
619
|
-
this,
|
|
620
|
-
flags.json ? JSON.stringify(result, null, 2) : makeEventNextSteps(result),
|
|
621
|
-
);
|
|
690
|
+
routes: {
|
|
691
|
+
run: scheduleRunCommand,
|
|
622
692
|
},
|
|
623
693
|
});
|
|
624
694
|
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
positional: namePositional,
|
|
632
|
-
},
|
|
633
|
-
async func(flags, name) {
|
|
634
|
-
const result = await makeJob({
|
|
635
|
-
name,
|
|
636
|
-
force: Boolean(flags.force),
|
|
637
|
-
dryRun: Boolean(flags.dryRun),
|
|
638
|
-
});
|
|
639
|
-
writeOutput(
|
|
640
|
-
this,
|
|
641
|
-
flags.json ? JSON.stringify(result, null, 2) : makeJobNextSteps(result),
|
|
642
|
-
);
|
|
695
|
+
const completionShellFlagParameters = {
|
|
696
|
+
shell: {
|
|
697
|
+
kind: "enum",
|
|
698
|
+
values: completionShellChoices,
|
|
699
|
+
optional: true,
|
|
700
|
+
brief: "Shell to manage completions for. Defaults to $SHELL.",
|
|
643
701
|
},
|
|
644
|
-
|
|
702
|
+
json: jsonFlag,
|
|
703
|
+
} satisfies FlagParametersForType<CompletionFlags, CliContext>;
|
|
645
704
|
|
|
646
|
-
const
|
|
705
|
+
const completionInstallCommand = buildCommand<CompletionFlags, [], CliContext>({
|
|
647
706
|
docs: {
|
|
648
|
-
brief: "
|
|
707
|
+
brief: "Install beignet shell completions for bash or zsh.",
|
|
649
708
|
},
|
|
650
709
|
parameters: {
|
|
651
|
-
flags:
|
|
652
|
-
positional: namePositional,
|
|
710
|
+
flags: completionShellFlagParameters,
|
|
653
711
|
},
|
|
654
|
-
async
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
712
|
+
loader: async () => {
|
|
713
|
+
const { installCompletions } = await import("./completion.js");
|
|
714
|
+
|
|
715
|
+
return async function runInstall(this: CliContext, flags: CompletionFlags) {
|
|
716
|
+
const result = await installCompletions({ shell: flags.shell });
|
|
717
|
+
writeOutput(
|
|
718
|
+
this,
|
|
719
|
+
flags.json
|
|
720
|
+
? JSON.stringify(result, null, 2)
|
|
721
|
+
: `${result.status === "updated" ? "Updated" : "Installed"} ${result.shell} completions in ${result.file}
|
|
722
|
+
|
|
723
|
+
Restart your shell or source the file to activate them.`,
|
|
724
|
+
);
|
|
725
|
+
};
|
|
666
726
|
},
|
|
667
727
|
});
|
|
668
728
|
|
|
669
|
-
const
|
|
729
|
+
const completionUninstallCommand = buildCommand<
|
|
730
|
+
CompletionFlags,
|
|
731
|
+
[],
|
|
732
|
+
CliContext
|
|
733
|
+
>({
|
|
670
734
|
docs: {
|
|
671
|
-
brief: "
|
|
735
|
+
brief: "Remove beignet shell completions for bash or zsh.",
|
|
672
736
|
},
|
|
673
737
|
parameters: {
|
|
674
|
-
flags:
|
|
675
|
-
positional: namePositional,
|
|
738
|
+
flags: completionShellFlagParameters,
|
|
676
739
|
},
|
|
677
|
-
async
|
|
678
|
-
const
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
740
|
+
loader: async () => {
|
|
741
|
+
const { uninstallCompletions } = await import("./completion.js");
|
|
742
|
+
|
|
743
|
+
return async function runUninstall(
|
|
744
|
+
this: CliContext,
|
|
745
|
+
flags: CompletionFlags,
|
|
746
|
+
) {
|
|
747
|
+
const result = await uninstallCompletions({ shell: flags.shell });
|
|
748
|
+
writeOutput(
|
|
749
|
+
this,
|
|
750
|
+
flags.json
|
|
751
|
+
? JSON.stringify(result, null, 2)
|
|
752
|
+
: result.status === "removed"
|
|
753
|
+
? `Removed ${result.shell} completions from ${result.file}`
|
|
754
|
+
: `No beignet completions found in ${result.file}`,
|
|
755
|
+
);
|
|
756
|
+
};
|
|
687
757
|
},
|
|
688
758
|
});
|
|
689
759
|
|
|
690
|
-
const
|
|
760
|
+
const completionRoutes = buildRouteMap({
|
|
691
761
|
docs: {
|
|
692
|
-
brief: "
|
|
693
|
-
},
|
|
694
|
-
parameters: {
|
|
695
|
-
flags: makeFlagParameters,
|
|
696
|
-
positional: namePositional,
|
|
762
|
+
brief: "Manage beignet shell completions.",
|
|
697
763
|
},
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
force: Boolean(flags.force),
|
|
702
|
-
dryRun: Boolean(flags.dryRun),
|
|
703
|
-
});
|
|
704
|
-
writeOutput(
|
|
705
|
-
this,
|
|
706
|
-
flags.json
|
|
707
|
-
? JSON.stringify(result, null, 2)
|
|
708
|
-
: makeNotificationNextSteps(result),
|
|
709
|
-
);
|
|
764
|
+
routes: {
|
|
765
|
+
install: completionInstallCommand,
|
|
766
|
+
uninstall: completionUninstallCommand,
|
|
710
767
|
},
|
|
711
768
|
});
|
|
712
769
|
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
event: parsedStringFlag("Event to listen to, for example posts/published."),
|
|
716
|
-
} satisfies FlagParametersForType<MakeListenerFlags, CliContext>;
|
|
717
|
-
|
|
718
|
-
const makeListenerCommand = buildCommand<
|
|
719
|
-
MakeListenerFlags,
|
|
770
|
+
const makeResourceCommand = buildCommand<
|
|
771
|
+
MakeResourceFlags,
|
|
720
772
|
[string],
|
|
721
773
|
CliContext
|
|
722
774
|
>({
|
|
723
775
|
docs: {
|
|
724
|
-
brief: "Generate a
|
|
776
|
+
brief: "Generate a CRUD-shaped resource slice.",
|
|
725
777
|
},
|
|
726
778
|
parameters: {
|
|
727
|
-
flags:
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
:
|
|
748
|
-
|
|
779
|
+
flags: {
|
|
780
|
+
...makeFlagParameters,
|
|
781
|
+
auth: {
|
|
782
|
+
kind: "boolean",
|
|
783
|
+
optional: true,
|
|
784
|
+
withNegated: false,
|
|
785
|
+
brief:
|
|
786
|
+
"Generate policy metadata and use-case authorization checks for mutating routes.",
|
|
787
|
+
},
|
|
788
|
+
tenant: {
|
|
789
|
+
kind: "boolean",
|
|
790
|
+
optional: true,
|
|
791
|
+
withNegated: false,
|
|
792
|
+
brief:
|
|
793
|
+
"Generate tenant-scoped schemas, repository methods, and use-case checks.",
|
|
794
|
+
},
|
|
795
|
+
events: {
|
|
796
|
+
kind: "boolean",
|
|
797
|
+
optional: true,
|
|
798
|
+
withNegated: false,
|
|
799
|
+
brief: "Generate domain event definitions and publish resource events.",
|
|
800
|
+
},
|
|
801
|
+
softDelete: {
|
|
802
|
+
kind: "boolean",
|
|
803
|
+
optional: true,
|
|
804
|
+
withNegated: false,
|
|
805
|
+
brief:
|
|
806
|
+
"Generate soft-delete persistence that archives rows with deletedAt.",
|
|
807
|
+
},
|
|
808
|
+
} satisfies FlagParametersForType<MakeResourceFlags, CliContext>,
|
|
809
|
+
positional: namePositional,
|
|
810
|
+
},
|
|
811
|
+
loader: async () => {
|
|
812
|
+
const { makeResource } = await import("./make.js");
|
|
813
|
+
|
|
814
|
+
return async function runMakeResource(
|
|
815
|
+
this: CliContext,
|
|
816
|
+
flags: MakeResourceFlags,
|
|
817
|
+
name: string,
|
|
818
|
+
) {
|
|
819
|
+
const result = await makeResource({
|
|
820
|
+
name,
|
|
821
|
+
force: Boolean(flags.force),
|
|
822
|
+
dryRun: Boolean(flags.dryRun),
|
|
823
|
+
auth: Boolean(flags.auth),
|
|
824
|
+
tenant: Boolean(flags.tenant),
|
|
825
|
+
events: Boolean(flags.events),
|
|
826
|
+
softDelete: Boolean(flags.softDelete),
|
|
827
|
+
});
|
|
828
|
+
writeOutput(
|
|
829
|
+
this,
|
|
830
|
+
flags.json
|
|
831
|
+
? JSON.stringify(result, null, 2)
|
|
832
|
+
: makeResourceNextSteps(result),
|
|
833
|
+
);
|
|
834
|
+
};
|
|
749
835
|
},
|
|
750
836
|
});
|
|
751
837
|
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
838
|
+
const makeFeatureCommand = buildCommand<MakeFeatureFlags, [string], CliContext>(
|
|
839
|
+
{
|
|
840
|
+
docs: {
|
|
841
|
+
brief: "Generate a contract-first feature slice.",
|
|
842
|
+
},
|
|
843
|
+
parameters: {
|
|
844
|
+
flags: {
|
|
845
|
+
...makeFlagParameters,
|
|
846
|
+
with: {
|
|
847
|
+
kind: "enum",
|
|
848
|
+
values: makeFeatureAddonChoices,
|
|
849
|
+
optional: true,
|
|
850
|
+
variadic: ",",
|
|
851
|
+
brief:
|
|
852
|
+
"Add feature-owned artifacts. Supports policy, task/tasks, event/events, job/jobs, notification/notifications, ui, and upload/uploads.",
|
|
853
|
+
},
|
|
854
|
+
} satisfies FlagParametersForType<MakeFeatureFlags, CliContext>,
|
|
855
|
+
positional: namePositional,
|
|
856
|
+
},
|
|
857
|
+
loader: async () => {
|
|
858
|
+
const { makeFeature } = await import("./make.js");
|
|
859
|
+
|
|
860
|
+
return async function runMakeFeature(
|
|
861
|
+
this: CliContext,
|
|
862
|
+
flags: MakeFeatureFlags,
|
|
863
|
+
name: string,
|
|
864
|
+
) {
|
|
865
|
+
const result = await makeFeature({
|
|
866
|
+
name,
|
|
867
|
+
with: flags.with,
|
|
868
|
+
force: Boolean(flags.force),
|
|
869
|
+
dryRun: Boolean(flags.dryRun),
|
|
870
|
+
});
|
|
871
|
+
writeOutput(
|
|
872
|
+
this,
|
|
873
|
+
flags.json
|
|
874
|
+
? JSON.stringify(result, null, 2)
|
|
875
|
+
: makeFeatureNextSteps(result),
|
|
876
|
+
);
|
|
877
|
+
};
|
|
878
|
+
},
|
|
761
879
|
},
|
|
762
|
-
|
|
880
|
+
);
|
|
763
881
|
|
|
764
|
-
|
|
765
|
-
|
|
882
|
+
type MakeGeneratorName =
|
|
883
|
+
| "makeAdapter"
|
|
884
|
+
| "makeContract"
|
|
885
|
+
| "makeEvent"
|
|
886
|
+
| "makeFactory"
|
|
887
|
+
| "makeJob"
|
|
888
|
+
| "makeNotification"
|
|
889
|
+
| "makePolicy"
|
|
890
|
+
| "makePort"
|
|
891
|
+
| "makeSeed"
|
|
892
|
+
| "makeTask"
|
|
893
|
+
| "makeTest"
|
|
894
|
+
| "makeUpload"
|
|
895
|
+
| "makeUseCase";
|
|
896
|
+
|
|
897
|
+
function simpleMakeCommand(
|
|
898
|
+
generator: MakeGeneratorName,
|
|
899
|
+
brief: string,
|
|
900
|
+
formatNextSteps: (result: MakeNextStepsResult) => string,
|
|
901
|
+
) {
|
|
902
|
+
return buildCommand<MakeFlags, [string], CliContext>({
|
|
903
|
+
docs: {
|
|
904
|
+
brief,
|
|
905
|
+
},
|
|
906
|
+
parameters: {
|
|
907
|
+
flags: makeFlagParameters,
|
|
908
|
+
positional: namePositional,
|
|
909
|
+
},
|
|
910
|
+
loader: async () => {
|
|
911
|
+
const generators = await import("./make.js");
|
|
912
|
+
|
|
913
|
+
return async function runMake(
|
|
914
|
+
this: CliContext,
|
|
915
|
+
flags: MakeFlags,
|
|
916
|
+
name: string,
|
|
917
|
+
) {
|
|
918
|
+
const result = await generators[generator]({
|
|
919
|
+
name,
|
|
920
|
+
force: Boolean(flags.force),
|
|
921
|
+
dryRun: Boolean(flags.dryRun),
|
|
922
|
+
});
|
|
923
|
+
writeOutput(
|
|
924
|
+
this,
|
|
925
|
+
flags.json
|
|
926
|
+
? JSON.stringify(result, null, 2)
|
|
927
|
+
: formatNextSteps(result),
|
|
928
|
+
);
|
|
929
|
+
};
|
|
930
|
+
},
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
const makeListenerCommand = buildCommand<
|
|
935
|
+
MakeListenerFlags,
|
|
766
936
|
[string],
|
|
767
937
|
CliContext
|
|
768
938
|
>({
|
|
769
939
|
docs: {
|
|
770
|
-
brief: "Generate a feature
|
|
940
|
+
brief: "Generate a feature event listener.",
|
|
771
941
|
},
|
|
772
942
|
parameters: {
|
|
773
|
-
flags:
|
|
943
|
+
flags: {
|
|
944
|
+
...makeFlagParameters,
|
|
945
|
+
event: parsedStringFlag(
|
|
946
|
+
"Event to listen to, for example posts/published.",
|
|
947
|
+
),
|
|
948
|
+
} satisfies FlagParametersForType<MakeListenerFlags, CliContext>,
|
|
774
949
|
positional: namePositional,
|
|
775
950
|
},
|
|
776
|
-
async
|
|
777
|
-
const
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
951
|
+
loader: async () => {
|
|
952
|
+
const { makeListener } = await import("./make.js");
|
|
953
|
+
|
|
954
|
+
return async function runMakeListener(
|
|
955
|
+
this: CliContext,
|
|
956
|
+
flags: MakeListenerFlags,
|
|
957
|
+
name: string,
|
|
958
|
+
) {
|
|
959
|
+
if (!flags.event) {
|
|
960
|
+
throw new Error(
|
|
961
|
+
"beignet make listener requires --event feature/name, for example --event posts/published.",
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
const result = await makeListener({
|
|
966
|
+
name,
|
|
967
|
+
event: flags.event,
|
|
968
|
+
force: Boolean(flags.force),
|
|
969
|
+
dryRun: Boolean(flags.dryRun),
|
|
970
|
+
});
|
|
971
|
+
writeOutput(
|
|
972
|
+
this,
|
|
973
|
+
flags.json
|
|
974
|
+
? JSON.stringify(result, null, 2)
|
|
975
|
+
: makeListenerNextSteps(result),
|
|
976
|
+
);
|
|
977
|
+
};
|
|
791
978
|
},
|
|
792
979
|
});
|
|
793
980
|
|
|
794
|
-
const
|
|
981
|
+
const makeScheduleCommand = buildCommand<
|
|
982
|
+
MakeScheduleFlags,
|
|
983
|
+
[string],
|
|
984
|
+
CliContext
|
|
985
|
+
>({
|
|
795
986
|
docs: {
|
|
796
|
-
brief: "Generate a feature
|
|
987
|
+
brief: "Generate a feature schedule.",
|
|
797
988
|
},
|
|
798
989
|
parameters: {
|
|
799
|
-
flags:
|
|
990
|
+
flags: {
|
|
991
|
+
...makeFlagParameters,
|
|
992
|
+
cron: parsedStringFlag("Cron expression. Defaults to 0 9 * * *."),
|
|
993
|
+
timezone: parsedStringFlag("IANA timezone, for example America/Chicago."),
|
|
994
|
+
route: {
|
|
995
|
+
kind: "boolean",
|
|
996
|
+
optional: true,
|
|
997
|
+
withNegated: false,
|
|
998
|
+
brief: "Generate a Next.js cron route for this schedule.",
|
|
999
|
+
},
|
|
1000
|
+
} satisfies FlagParametersForType<MakeScheduleFlags, CliContext>,
|
|
800
1001
|
positional: namePositional,
|
|
801
1002
|
},
|
|
802
|
-
async
|
|
803
|
-
const
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
:
|
|
813
|
-
|
|
1003
|
+
loader: async () => {
|
|
1004
|
+
const { makeSchedule } = await import("./make.js");
|
|
1005
|
+
|
|
1006
|
+
return async function runMakeSchedule(
|
|
1007
|
+
this: CliContext,
|
|
1008
|
+
flags: MakeScheduleFlags,
|
|
1009
|
+
name: string,
|
|
1010
|
+
) {
|
|
1011
|
+
const result = await makeSchedule({
|
|
1012
|
+
name,
|
|
1013
|
+
cron: flags.cron,
|
|
1014
|
+
timezone: flags.timezone,
|
|
1015
|
+
route: Boolean(flags.route),
|
|
1016
|
+
force: Boolean(flags.force),
|
|
1017
|
+
dryRun: Boolean(flags.dryRun),
|
|
1018
|
+
});
|
|
1019
|
+
writeOutput(
|
|
1020
|
+
this,
|
|
1021
|
+
flags.json
|
|
1022
|
+
? JSON.stringify(result, null, 2)
|
|
1023
|
+
: makeScheduleNextSteps(result),
|
|
1024
|
+
);
|
|
1025
|
+
};
|
|
814
1026
|
},
|
|
815
1027
|
});
|
|
816
1028
|
|
|
@@ -819,22 +1031,75 @@ const makeRoutes = buildRouteMap({
|
|
|
819
1031
|
brief: "Generate Beignet app files.",
|
|
820
1032
|
},
|
|
821
1033
|
routes: {
|
|
822
|
-
adapter:
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
1034
|
+
adapter: simpleMakeCommand(
|
|
1035
|
+
"makeAdapter",
|
|
1036
|
+
"Generate a port adapter.",
|
|
1037
|
+
makeAdapterNextSteps,
|
|
1038
|
+
),
|
|
1039
|
+
contract: simpleMakeCommand(
|
|
1040
|
+
"makeContract",
|
|
1041
|
+
"Generate a contract group.",
|
|
1042
|
+
makeContractNextSteps,
|
|
1043
|
+
),
|
|
1044
|
+
event: simpleMakeCommand(
|
|
1045
|
+
"makeEvent",
|
|
1046
|
+
"Generate a feature event.",
|
|
1047
|
+
makeEventNextSteps,
|
|
1048
|
+
),
|
|
1049
|
+
factory: simpleMakeCommand(
|
|
1050
|
+
"makeFactory",
|
|
1051
|
+
"Generate a feature test data factory.",
|
|
1052
|
+
makeFactoryNextSteps,
|
|
1053
|
+
),
|
|
826
1054
|
feature: makeFeatureCommand,
|
|
827
|
-
job:
|
|
828
|
-
|
|
1055
|
+
job: simpleMakeCommand(
|
|
1056
|
+
"makeJob",
|
|
1057
|
+
"Generate a feature job.",
|
|
1058
|
+
makeJobNextSteps,
|
|
1059
|
+
),
|
|
1060
|
+
notification: simpleMakeCommand(
|
|
1061
|
+
"makeNotification",
|
|
1062
|
+
"Generate a feature notification.",
|
|
1063
|
+
makeNotificationNextSteps,
|
|
1064
|
+
),
|
|
829
1065
|
listener: makeListenerCommand,
|
|
830
|
-
policy:
|
|
831
|
-
|
|
1066
|
+
policy: simpleMakeCommand(
|
|
1067
|
+
"makePolicy",
|
|
1068
|
+
"Generate an authorization policy.",
|
|
1069
|
+
makePolicyNextSteps,
|
|
1070
|
+
),
|
|
1071
|
+
port: simpleMakeCommand(
|
|
1072
|
+
"makePort",
|
|
1073
|
+
"Generate an application port.",
|
|
1074
|
+
makePortNextSteps,
|
|
1075
|
+
),
|
|
832
1076
|
resource: makeResourceCommand,
|
|
833
1077
|
schedule: makeScheduleCommand,
|
|
834
|
-
seed:
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
1078
|
+
seed: simpleMakeCommand(
|
|
1079
|
+
"makeSeed",
|
|
1080
|
+
"Generate a feature seed.",
|
|
1081
|
+
makeSeedNextSteps,
|
|
1082
|
+
),
|
|
1083
|
+
task: simpleMakeCommand(
|
|
1084
|
+
"makeTask",
|
|
1085
|
+
"Generate a feature operational task.",
|
|
1086
|
+
makeTaskNextSteps,
|
|
1087
|
+
),
|
|
1088
|
+
test: simpleMakeCommand(
|
|
1089
|
+
"makeTest",
|
|
1090
|
+
"Generate a use case test.",
|
|
1091
|
+
makeTestNextSteps,
|
|
1092
|
+
),
|
|
1093
|
+
upload: simpleMakeCommand(
|
|
1094
|
+
"makeUpload",
|
|
1095
|
+
"Generate a feature upload.",
|
|
1096
|
+
makeUploadNextSteps,
|
|
1097
|
+
),
|
|
1098
|
+
useCase: simpleMakeCommand(
|
|
1099
|
+
"makeUseCase",
|
|
1100
|
+
"Generate a use case.",
|
|
1101
|
+
makeUseCaseNextSteps,
|
|
1102
|
+
),
|
|
838
1103
|
},
|
|
839
1104
|
});
|
|
840
1105
|
|
|
@@ -843,20 +1108,27 @@ const rootRoutes = buildRouteMap({
|
|
|
843
1108
|
brief: "Beignet CLI",
|
|
844
1109
|
fullDescription: `Create apps, generate framework files, inspect routes, and check Beignet conventions.
|
|
845
1110
|
|
|
846
|
-
create
|
|
1111
|
+
Run npm create beignet@latest (or bun create beignet) to scaffold a new app.`,
|
|
847
1112
|
},
|
|
848
1113
|
routes: {
|
|
1114
|
+
completion: completionRoutes,
|
|
849
1115
|
create: createCommand,
|
|
850
1116
|
db: dbRoutes,
|
|
851
1117
|
doctor: doctorCommand,
|
|
852
1118
|
lint: lintCommand,
|
|
853
1119
|
make: makeRoutes,
|
|
1120
|
+
outbox: outboxRoutes,
|
|
854
1121
|
routes: routesCommand,
|
|
1122
|
+
schedule: scheduleRoutes,
|
|
1123
|
+
task: taskRoutes,
|
|
855
1124
|
},
|
|
856
1125
|
});
|
|
857
1126
|
|
|
858
1127
|
const cli = buildApplication(rootRoutes, {
|
|
859
1128
|
name: "beignet",
|
|
1129
|
+
versionInfo: {
|
|
1130
|
+
currentVersion: getCliVersion(),
|
|
1131
|
+
},
|
|
860
1132
|
scanner: {
|
|
861
1133
|
caseStyle: "allow-kebab-for-camel",
|
|
862
1134
|
},
|
|
@@ -870,7 +1142,7 @@ const cli = buildApplication(rootRoutes, {
|
|
|
870
1142
|
commandErrorResult: (error) => error.message,
|
|
871
1143
|
},
|
|
872
1144
|
},
|
|
873
|
-
determineExitCode: () =>
|
|
1145
|
+
determineExitCode: () => 2,
|
|
874
1146
|
});
|
|
875
1147
|
|
|
876
1148
|
function uniqueValues<T>(values: readonly T[]): T[] {
|
|
@@ -885,12 +1157,108 @@ function formatCliException(error: unknown): string {
|
|
|
885
1157
|
return error instanceof Error ? error.message : String(error);
|
|
886
1158
|
}
|
|
887
1159
|
|
|
1160
|
+
type CreateNextStepsResult = {
|
|
1161
|
+
name: string;
|
|
1162
|
+
targetDir: string;
|
|
1163
|
+
dryRun: boolean;
|
|
1164
|
+
files: string[];
|
|
1165
|
+
packageManager: PackageManager;
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
type MakeNextStepsResult = {
|
|
1169
|
+
name: string;
|
|
1170
|
+
targetDir: string;
|
|
1171
|
+
dryRun: boolean;
|
|
1172
|
+
createdFiles: string[];
|
|
1173
|
+
updatedFiles: string[];
|
|
1174
|
+
skippedFiles: string[];
|
|
1175
|
+
};
|
|
1176
|
+
|
|
1177
|
+
type DatabaseCommandNextStepsResult = {
|
|
1178
|
+
script: string;
|
|
1179
|
+
cwd: string;
|
|
1180
|
+
runner: string;
|
|
1181
|
+
args: string[];
|
|
1182
|
+
dryRun: boolean;
|
|
1183
|
+
};
|
|
1184
|
+
|
|
1185
|
+
type AppTaskRunNextStepsResult = {
|
|
1186
|
+
name: string;
|
|
1187
|
+
durationMs: number;
|
|
1188
|
+
output: unknown;
|
|
1189
|
+
};
|
|
1190
|
+
|
|
1191
|
+
type OutboxDrainNextStepsResult = {
|
|
1192
|
+
durationMs: number;
|
|
1193
|
+
result: {
|
|
1194
|
+
claimed: number;
|
|
1195
|
+
delivered: number;
|
|
1196
|
+
retried: number;
|
|
1197
|
+
deadLettered: number;
|
|
1198
|
+
};
|
|
1199
|
+
};
|
|
1200
|
+
|
|
1201
|
+
type ScheduleRunNextStepsResult = {
|
|
1202
|
+
name: string;
|
|
1203
|
+
durationMs: number;
|
|
1204
|
+
run: {
|
|
1205
|
+
source: string;
|
|
1206
|
+
id?: string;
|
|
1207
|
+
attempt?: number;
|
|
1208
|
+
scheduledAt?: string;
|
|
1209
|
+
triggeredAt?: string;
|
|
1210
|
+
};
|
|
1211
|
+
};
|
|
1212
|
+
|
|
1213
|
+
function appTaskRunNextSteps(result: AppTaskRunNextStepsResult): string {
|
|
1214
|
+
const output =
|
|
1215
|
+
result.output === undefined
|
|
1216
|
+
? "undefined"
|
|
1217
|
+
: JSON.stringify(result.output, null, 2);
|
|
1218
|
+
|
|
1219
|
+
return `Ran task ${result.name} in ${result.durationMs}ms
|
|
1220
|
+
|
|
1221
|
+
Output:
|
|
1222
|
+
${output}`;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
function outboxDrainNextSteps(result: OutboxDrainNextStepsResult): string {
|
|
1226
|
+
return `Drained outbox in ${result.durationMs}ms
|
|
1227
|
+
|
|
1228
|
+
Result:
|
|
1229
|
+
claimed: ${result.result.claimed}
|
|
1230
|
+
delivered: ${result.result.delivered}
|
|
1231
|
+
retried: ${result.result.retried}
|
|
1232
|
+
deadLettered: ${result.result.deadLettered}`;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
function scheduleRunNextSteps(result: ScheduleRunNextStepsResult): string {
|
|
1236
|
+
return `Ran schedule ${result.name} in ${result.durationMs}ms
|
|
1237
|
+
|
|
1238
|
+
Run:
|
|
1239
|
+
source: ${result.run.source}
|
|
1240
|
+
id: ${result.run.id ?? "none"}
|
|
1241
|
+
attempt: ${result.run.attempt ?? "none"}
|
|
1242
|
+
scheduledAt: ${result.run.scheduledAt ?? "none"}
|
|
1243
|
+
triggeredAt: ${result.run.triggeredAt ?? "none"}`;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
888
1246
|
function nextSteps(
|
|
889
|
-
result:
|
|
1247
|
+
result: CreateNextStepsResult,
|
|
890
1248
|
options: { integrations?: readonly IntegrationName[] } = {},
|
|
891
1249
|
): string {
|
|
1250
|
+
if (result.dryRun) {
|
|
1251
|
+
const plannedFiles = result.files.map((file) => ` ${file}`).join("\n");
|
|
1252
|
+
|
|
1253
|
+
return `Would create ${result.name} at ${result.targetDir}
|
|
1254
|
+
|
|
1255
|
+
Planned files:
|
|
1256
|
+
${plannedFiles}`;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
892
1259
|
const pm = result.packageManager;
|
|
893
1260
|
const run = pm === "npm" ? "npm run" : `${pm} run`;
|
|
1261
|
+
const cli = packageRunnerFromPackageManager(pm);
|
|
894
1262
|
const envFileStep = result.files.includes(".env.example")
|
|
895
1263
|
? " cp .env.example .env.local\n"
|
|
896
1264
|
: "";
|
|
@@ -900,14 +1268,53 @@ function nextSteps(
|
|
|
900
1268
|
envRequiredIntegrations.length > 0
|
|
901
1269
|
? " Fill .env.local for selected provider integrations before starting the app.\n"
|
|
902
1270
|
: "";
|
|
1271
|
+
const hasDatabase = result.files.some((file) => file.startsWith("infra/db/"));
|
|
1272
|
+
const databaseStep = hasDatabase
|
|
1273
|
+
? `
|
|
1274
|
+
Prepare the database:
|
|
1275
|
+
${cli} db generate
|
|
1276
|
+
${cli} db migrate
|
|
1277
|
+
`
|
|
1278
|
+
: "";
|
|
1279
|
+
const featureDatabaseSteps = hasDatabase
|
|
1280
|
+
? `
|
|
1281
|
+
${cli} db generate
|
|
1282
|
+
${cli} db migrate`
|
|
1283
|
+
: "";
|
|
903
1284
|
|
|
904
1285
|
return `Created ${result.name} at ${result.targetDir}
|
|
905
1286
|
|
|
906
1287
|
Next steps:
|
|
907
1288
|
cd ${result.targetDir}
|
|
908
1289
|
${pm} install
|
|
909
|
-
${envFileStep}${envStep}
|
|
910
|
-
|
|
1290
|
+
${envFileStep}${envStep}${databaseStep}
|
|
1291
|
+
Start the app:
|
|
1292
|
+
${run} dev
|
|
1293
|
+
|
|
1294
|
+
Inspect the app:
|
|
1295
|
+
${cli} routes
|
|
1296
|
+
${cli} lint
|
|
1297
|
+
${cli} doctor
|
|
1298
|
+
|
|
1299
|
+
Generate a feature:
|
|
1300
|
+
${cli} make feature projects${featureDatabaseSteps}
|
|
1301
|
+
${run} test
|
|
1302
|
+
${run} typecheck
|
|
1303
|
+
${cli} lint
|
|
1304
|
+
${cli} doctor`;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
function packageRunnerFromPackageManager(pm: PackageManager): string {
|
|
1308
|
+
switch (pm) {
|
|
1309
|
+
case "npm":
|
|
1310
|
+
return "npm run beignet --";
|
|
1311
|
+
case "pnpm":
|
|
1312
|
+
return "pnpm beignet";
|
|
1313
|
+
case "yarn":
|
|
1314
|
+
return "yarn beignet";
|
|
1315
|
+
default:
|
|
1316
|
+
return "bun beignet";
|
|
1317
|
+
}
|
|
911
1318
|
}
|
|
912
1319
|
|
|
913
1320
|
function isEnvRequiredProviderIntegration(
|
|
@@ -920,29 +1327,46 @@ function isEnvRequiredProviderIntegration(
|
|
|
920
1327
|
);
|
|
921
1328
|
}
|
|
922
1329
|
|
|
923
|
-
function
|
|
924
|
-
|
|
1330
|
+
function changedFileLines(result: MakeNextStepsResult): {
|
|
1331
|
+
changedFiles: string;
|
|
1332
|
+
skippedFiles: string;
|
|
1333
|
+
} {
|
|
1334
|
+
return {
|
|
1335
|
+
changedFiles: [...result.createdFiles, ...result.updatedFiles]
|
|
1336
|
+
.map((file) => ` ${file}`)
|
|
1337
|
+
.join("\n"),
|
|
1338
|
+
skippedFiles: result.skippedFiles.map((file) => ` ${file}`).join("\n"),
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
function makeNextSteps(
|
|
1343
|
+
result: MakeNextStepsResult,
|
|
1344
|
+
noun: string,
|
|
1345
|
+
steps: readonly string[],
|
|
925
1346
|
): string {
|
|
926
|
-
const changedFiles =
|
|
927
|
-
.map((file) => ` ${file}`)
|
|
928
|
-
.join("\n");
|
|
929
|
-
const skippedFiles = result.skippedFiles
|
|
930
|
-
.map((file) => ` ${file}`)
|
|
931
|
-
.join("\n");
|
|
1347
|
+
const { changedFiles, skippedFiles } = changedFileLines(result);
|
|
932
1348
|
|
|
933
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name}
|
|
1349
|
+
return `${result.dryRun ? "Would create" : "Created"} ${result.name} ${noun} in ${result.targetDir}
|
|
934
1350
|
|
|
935
1351
|
Changed files:
|
|
936
1352
|
${changedFiles || " none"}
|
|
937
1353
|
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
938
1354
|
|
|
939
1355
|
Next steps:
|
|
940
|
-
|
|
941
|
-
|
|
1356
|
+
${steps.map((step) => ` ${step}`).join("\n")}`;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
function makeResourceNextSteps(result: MakeNextStepsResult): string {
|
|
1360
|
+
return makeNextSteps(result, "resource", [
|
|
1361
|
+
"Use make resource when the feature maps cleanly to a REST resource with repository-backed persistence.",
|
|
1362
|
+
"Review the generated schemas and repository fields.",
|
|
1363
|
+
"If this app uses Drizzle, run beignet db generate and beignet db migrate so the new schema is ready.",
|
|
1364
|
+
"Run your app's test and typecheck commands, then beignet lint and beignet doctor.",
|
|
1365
|
+
]);
|
|
942
1366
|
}
|
|
943
1367
|
|
|
944
1368
|
function databaseCommandNextSteps(
|
|
945
|
-
result:
|
|
1369
|
+
result: DatabaseCommandNextStepsResult,
|
|
946
1370
|
): string {
|
|
947
1371
|
const command = [result.runner, ...result.args].join(" ");
|
|
948
1372
|
const prefix = result.dryRun ? "Would run" : "Ran";
|
|
@@ -953,317 +1377,118 @@ Command:
|
|
|
953
1377
|
${command}`;
|
|
954
1378
|
}
|
|
955
1379
|
|
|
956
|
-
function makeFeatureNextSteps(
|
|
957
|
-
result
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
.
|
|
962
|
-
|
|
963
|
-
.map((file) => ` ${file}`)
|
|
964
|
-
.join("\n");
|
|
965
|
-
|
|
966
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} feature slice in ${result.targetDir}
|
|
967
|
-
|
|
968
|
-
Changed files:
|
|
969
|
-
${changedFiles || " none"}
|
|
970
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
971
|
-
|
|
972
|
-
Next steps:
|
|
973
|
-
Treat the generated name field as a placeholder and shape the contracts, use cases, and repository around the feature's real workflow.
|
|
974
|
-
Run your app's typecheck, test, beignet lint, and beignet doctor commands.`;
|
|
1380
|
+
function makeFeatureNextSteps(result: MakeNextStepsResult): string {
|
|
1381
|
+
return makeNextSteps(result, "feature slice", [
|
|
1382
|
+
"Use make feature for product capabilities and workflows. Use make resource when the concept is mostly CRUD-shaped.",
|
|
1383
|
+
"Treat the generated name field as a placeholder and shape the contracts, use cases, and repository around the feature's real workflow.",
|
|
1384
|
+
"If this app uses Drizzle, run beignet db generate and beignet db migrate so the new schema is ready.",
|
|
1385
|
+
"Run your app's test and typecheck commands, then beignet lint and beignet doctor.",
|
|
1386
|
+
]);
|
|
975
1387
|
}
|
|
976
1388
|
|
|
977
|
-
function makeContractNextSteps(
|
|
978
|
-
result
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
.join("\n");
|
|
983
|
-
const skippedFiles = result.skippedFiles
|
|
984
|
-
.map((file) => ` ${file}`)
|
|
985
|
-
.join("\n");
|
|
986
|
-
|
|
987
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} contract in ${result.targetDir}
|
|
988
|
-
|
|
989
|
-
Changed files:
|
|
990
|
-
${changedFiles || " none"}
|
|
991
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
992
|
-
|
|
993
|
-
Next steps:
|
|
994
|
-
Add route handlers for the generated contracts.
|
|
995
|
-
Register the exported contract list with OpenAPI if this app publishes docs.`;
|
|
1389
|
+
function makeContractNextSteps(result: MakeNextStepsResult): string {
|
|
1390
|
+
return makeNextSteps(result, "contract", [
|
|
1391
|
+
"Add route handlers for the generated contracts.",
|
|
1392
|
+
"Register the exported contract list with OpenAPI if this app publishes docs.",
|
|
1393
|
+
]);
|
|
996
1394
|
}
|
|
997
1395
|
|
|
998
|
-
function
|
|
999
|
-
result
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
.join("\n");
|
|
1004
|
-
const skippedFiles = result.skippedFiles
|
|
1005
|
-
.map((file) => ` ${file}`)
|
|
1006
|
-
.join("\n");
|
|
1007
|
-
|
|
1008
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} use case in ${result.targetDir}
|
|
1009
|
-
|
|
1010
|
-
Changed files:
|
|
1011
|
-
${changedFiles || " none"}
|
|
1012
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
1013
|
-
|
|
1014
|
-
Next steps:
|
|
1015
|
-
Replace the starter input and output schemas with domain-specific shapes.
|
|
1016
|
-
Add a focused use case test before wiring it to an HTTP route.`;
|
|
1396
|
+
function makeTaskNextSteps(result: MakeNextStepsResult): string {
|
|
1397
|
+
return makeNextSteps(result, "task", [
|
|
1398
|
+
"Replace the starter input schema and handler with the operational workflow.",
|
|
1399
|
+
"Keep business rules in use cases and call them from the task handler.",
|
|
1400
|
+
]);
|
|
1017
1401
|
}
|
|
1018
1402
|
|
|
1019
|
-
function
|
|
1020
|
-
result
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
.join("\n");
|
|
1025
|
-
const skippedFiles = result.skippedFiles
|
|
1026
|
-
.map((file) => ` ${file}`)
|
|
1027
|
-
.join("\n");
|
|
1028
|
-
|
|
1029
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} test in ${result.targetDir}
|
|
1030
|
-
|
|
1031
|
-
Changed files:
|
|
1032
|
-
${changedFiles || " none"}
|
|
1033
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
1034
|
-
|
|
1035
|
-
Next steps:
|
|
1036
|
-
Replace the starter input, context, and assertion with behavior-specific coverage.
|
|
1037
|
-
Run your app's test command.`;
|
|
1403
|
+
function makeUseCaseNextSteps(result: MakeNextStepsResult): string {
|
|
1404
|
+
return makeNextSteps(result, "use case", [
|
|
1405
|
+
"Replace the starter input and output schemas with domain-specific shapes.",
|
|
1406
|
+
"Add a focused use case test before wiring it to an HTTP route.",
|
|
1407
|
+
]);
|
|
1038
1408
|
}
|
|
1039
1409
|
|
|
1040
|
-
function
|
|
1041
|
-
result
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
.join("\n");
|
|
1046
|
-
const skippedFiles = result.skippedFiles
|
|
1047
|
-
.map((file) => ` ${file}`)
|
|
1048
|
-
.join("\n");
|
|
1049
|
-
|
|
1050
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} port in ${result.targetDir}
|
|
1051
|
-
|
|
1052
|
-
Changed files:
|
|
1053
|
-
${changedFiles || " none"}
|
|
1054
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
1055
|
-
|
|
1056
|
-
Next steps:
|
|
1057
|
-
Replace the starter execute method with domain-specific operations.
|
|
1058
|
-
Replace the generated infrastructure stub with a real port adapter.`;
|
|
1410
|
+
function makeTestNextSteps(result: MakeNextStepsResult): string {
|
|
1411
|
+
return makeNextSteps(result, "test", [
|
|
1412
|
+
"Replace the starter input, context, and assertion with behavior-specific coverage.",
|
|
1413
|
+
"Run your app's test command.",
|
|
1414
|
+
]);
|
|
1059
1415
|
}
|
|
1060
1416
|
|
|
1061
|
-
function
|
|
1062
|
-
result
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
.join("\n");
|
|
1067
|
-
const skippedFiles = result.skippedFiles
|
|
1068
|
-
.map((file) => ` ${file}`)
|
|
1069
|
-
.join("\n");
|
|
1070
|
-
|
|
1071
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} policy in ${result.targetDir}
|
|
1072
|
-
|
|
1073
|
-
Changed files:
|
|
1074
|
-
${changedFiles || " none"}
|
|
1075
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
1076
|
-
|
|
1077
|
-
Next steps:
|
|
1078
|
-
Replace the starter abilities with domain-specific authorization rules.
|
|
1079
|
-
Register the policy with createGate(...) and bind it in request context.`;
|
|
1417
|
+
function makePortNextSteps(result: MakeNextStepsResult): string {
|
|
1418
|
+
return makeNextSteps(result, "port", [
|
|
1419
|
+
"Replace the starter execute method with domain-specific operations.",
|
|
1420
|
+
"Replace the generated infrastructure stub with a real port adapter.",
|
|
1421
|
+
]);
|
|
1080
1422
|
}
|
|
1081
1423
|
|
|
1082
|
-
function
|
|
1083
|
-
result
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
.join("\n");
|
|
1088
|
-
const skippedFiles = result.skippedFiles
|
|
1089
|
-
.map((file) => ` ${file}`)
|
|
1090
|
-
.join("\n");
|
|
1091
|
-
|
|
1092
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} port adapter in ${result.targetDir}
|
|
1093
|
-
|
|
1094
|
-
Changed files:
|
|
1095
|
-
${changedFiles || " none"}
|
|
1096
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
1097
|
-
|
|
1098
|
-
Next steps:
|
|
1099
|
-
Replace the generated throwing implementation with real infrastructure code.
|
|
1100
|
-
Keep the adapter behind the port interface so use cases stay infrastructure-agnostic.`;
|
|
1424
|
+
function makePolicyNextSteps(result: MakeNextStepsResult): string {
|
|
1425
|
+
return makeNextSteps(result, "policy", [
|
|
1426
|
+
"Replace the starter abilities with domain-specific authorization rules.",
|
|
1427
|
+
"Register the policy with createGate(...) and bind it in request context.",
|
|
1428
|
+
]);
|
|
1101
1429
|
}
|
|
1102
1430
|
|
|
1103
|
-
function
|
|
1104
|
-
result
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
.join("\n");
|
|
1109
|
-
const skippedFiles = result.skippedFiles
|
|
1110
|
-
.map((file) => ` ${file}`)
|
|
1111
|
-
.join("\n");
|
|
1112
|
-
|
|
1113
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} event in ${result.targetDir}
|
|
1114
|
-
|
|
1115
|
-
Changed files:
|
|
1116
|
-
${changedFiles || " none"}
|
|
1117
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
1118
|
-
|
|
1119
|
-
Next steps:
|
|
1120
|
-
Replace the starter payload schema with the domain fact shape.
|
|
1121
|
-
Emit the event from a use case with .emits(...) and events.record(...).`;
|
|
1431
|
+
function makeAdapterNextSteps(result: MakeNextStepsResult): string {
|
|
1432
|
+
return makeNextSteps(result, "port adapter", [
|
|
1433
|
+
"Replace the generated throwing implementation with real infrastructure code.",
|
|
1434
|
+
"Keep the adapter behind the port interface so use cases stay infrastructure-agnostic.",
|
|
1435
|
+
]);
|
|
1122
1436
|
}
|
|
1123
1437
|
|
|
1124
|
-
function
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
.
|
|
1128
|
-
|
|
1129
|
-
.map((file) => ` ${file}`)
|
|
1130
|
-
.join("\n");
|
|
1131
|
-
|
|
1132
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} job in ${result.targetDir}
|
|
1133
|
-
|
|
1134
|
-
Changed files:
|
|
1135
|
-
${changedFiles || " none"}
|
|
1136
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
1137
|
-
|
|
1138
|
-
Next steps:
|
|
1139
|
-
Replace the starter payload and handler with the real background work.
|
|
1140
|
-
Dispatch the job through ctx.ports.jobs from a use case, listener, or schedule.`;
|
|
1438
|
+
function makeEventNextSteps(result: MakeNextStepsResult): string {
|
|
1439
|
+
return makeNextSteps(result, "event", [
|
|
1440
|
+
"Replace the starter payload schema with the domain fact shape.",
|
|
1441
|
+
"Emit the event from a use case with .emits(...) and events.record(...).",
|
|
1442
|
+
]);
|
|
1141
1443
|
}
|
|
1142
1444
|
|
|
1143
|
-
function
|
|
1144
|
-
result
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
.join("\n");
|
|
1149
|
-
const skippedFiles = result.skippedFiles
|
|
1150
|
-
.map((file) => ` ${file}`)
|
|
1151
|
-
.join("\n");
|
|
1152
|
-
|
|
1153
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} factory in ${result.targetDir}
|
|
1154
|
-
|
|
1155
|
-
Changed files:
|
|
1156
|
-
${changedFiles || " none"}
|
|
1157
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
1158
|
-
|
|
1159
|
-
Next steps:
|
|
1160
|
-
Replace the starter defaults with realistic test data for this feature.
|
|
1161
|
-
Persist through app-owned repository ports so tests avoid raw database clients.`;
|
|
1445
|
+
function makeJobNextSteps(result: MakeNextStepsResult): string {
|
|
1446
|
+
return makeNextSteps(result, "job", [
|
|
1447
|
+
"Replace the starter payload and handler with the real background work.",
|
|
1448
|
+
"Dispatch the job through ctx.ports.jobs from a use case, listener, or schedule.",
|
|
1449
|
+
]);
|
|
1162
1450
|
}
|
|
1163
1451
|
|
|
1164
|
-
function
|
|
1165
|
-
result
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
.join("\n");
|
|
1170
|
-
const skippedFiles = result.skippedFiles
|
|
1171
|
-
.map((file) => ` ${file}`)
|
|
1172
|
-
.join("\n");
|
|
1173
|
-
|
|
1174
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} seed in ${result.targetDir}
|
|
1175
|
-
|
|
1176
|
-
Changed files:
|
|
1177
|
-
${changedFiles || " none"}
|
|
1178
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
1179
|
-
|
|
1180
|
-
Next steps:
|
|
1181
|
-
Replace the starter record with idempotent local or demo data.
|
|
1182
|
-
Import the feature seed from your app-owned database seed entrypoint and run it with runSeeds(...).`;
|
|
1452
|
+
function makeFactoryNextSteps(result: MakeNextStepsResult): string {
|
|
1453
|
+
return makeNextSteps(result, "factory", [
|
|
1454
|
+
"Replace the starter defaults with realistic test data for this feature.",
|
|
1455
|
+
"Persist through app-owned repository ports so tests avoid raw database clients.",
|
|
1456
|
+
]);
|
|
1183
1457
|
}
|
|
1184
1458
|
|
|
1185
|
-
function
|
|
1186
|
-
result
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
.join("\n");
|
|
1191
|
-
const skippedFiles = result.skippedFiles
|
|
1192
|
-
.map((file) => ` ${file}`)
|
|
1193
|
-
.join("\n");
|
|
1194
|
-
|
|
1195
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} notification in ${result.targetDir}
|
|
1196
|
-
|
|
1197
|
-
Changed files:
|
|
1198
|
-
${changedFiles || " none"}
|
|
1199
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
1200
|
-
|
|
1201
|
-
Next steps:
|
|
1202
|
-
Replace the starter payload and channels with the real user-facing message.
|
|
1203
|
-
Send the notification through ctx.ports.notifications so delivery can run inline in tests and through jobs in production.`;
|
|
1459
|
+
function makeSeedNextSteps(result: MakeNextStepsResult): string {
|
|
1460
|
+
return makeNextSteps(result, "seed", [
|
|
1461
|
+
"Replace the starter record with idempotent local or demo data.",
|
|
1462
|
+
"Import the feature seed from your app-owned database seed entrypoint and run it with runSeeds(...).",
|
|
1463
|
+
]);
|
|
1204
1464
|
}
|
|
1205
1465
|
|
|
1206
|
-
function
|
|
1207
|
-
result
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
.join("\n");
|
|
1212
|
-
const skippedFiles = result.skippedFiles
|
|
1213
|
-
.map((file) => ` ${file}`)
|
|
1214
|
-
.join("\n");
|
|
1215
|
-
|
|
1216
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} listener in ${result.targetDir}
|
|
1217
|
-
|
|
1218
|
-
Changed files:
|
|
1219
|
-
${changedFiles || " none"}
|
|
1220
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
1221
|
-
|
|
1222
|
-
Next steps:
|
|
1223
|
-
Replace the starter handler with the event reaction.
|
|
1224
|
-
Register the listener collection from infrastructure startup.`;
|
|
1466
|
+
function makeNotificationNextSteps(result: MakeNextStepsResult): string {
|
|
1467
|
+
return makeNextSteps(result, "notification", [
|
|
1468
|
+
"Replace the starter payload and channels with the real user-facing message.",
|
|
1469
|
+
"Send the notification through ctx.ports.notifications so delivery can run inline in tests and through jobs in production.",
|
|
1470
|
+
]);
|
|
1225
1471
|
}
|
|
1226
1472
|
|
|
1227
|
-
function
|
|
1228
|
-
result
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
.join("\n");
|
|
1233
|
-
const skippedFiles = result.skippedFiles
|
|
1234
|
-
.map((file) => ` ${file}`)
|
|
1235
|
-
.join("\n");
|
|
1236
|
-
|
|
1237
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} schedule in ${result.targetDir}
|
|
1238
|
-
|
|
1239
|
-
Changed files:
|
|
1240
|
-
${changedFiles || " none"}
|
|
1241
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
1242
|
-
|
|
1243
|
-
Next steps:
|
|
1244
|
-
Replace the starter payload and handler with the scheduled workflow.
|
|
1245
|
-
Trigger it from a cron route, worker, or provider adapter.`;
|
|
1473
|
+
function makeListenerNextSteps(result: MakeNextStepsResult): string {
|
|
1474
|
+
return makeNextSteps(result, "listener", [
|
|
1475
|
+
"Replace the starter handler with the event reaction.",
|
|
1476
|
+
"Register the listener collection from infrastructure startup.",
|
|
1477
|
+
]);
|
|
1246
1478
|
}
|
|
1247
1479
|
|
|
1248
|
-
function
|
|
1249
|
-
result
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
const skippedFiles = result.skippedFiles
|
|
1255
|
-
.map((file) => ` ${file}`)
|
|
1256
|
-
.join("\n");
|
|
1257
|
-
|
|
1258
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} upload in ${result.targetDir}
|
|
1259
|
-
|
|
1260
|
-
Changed files:
|
|
1261
|
-
${changedFiles || " none"}
|
|
1262
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
1480
|
+
function makeScheduleNextSteps(result: MakeNextStepsResult): string {
|
|
1481
|
+
return makeNextSteps(result, "schedule", [
|
|
1482
|
+
"Replace the starter payload and handler with the scheduled workflow.",
|
|
1483
|
+
"Trigger it from a cron route, worker, or provider adapter.",
|
|
1484
|
+
]);
|
|
1485
|
+
}
|
|
1263
1486
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1487
|
+
function makeUploadNextSteps(result: MakeNextStepsResult): string {
|
|
1488
|
+
return makeNextSteps(result, "upload", [
|
|
1489
|
+
"Replace the starter metadata, constraints, key, authorization, and onComplete behavior.",
|
|
1490
|
+
"Register the upload in createUploadRouter(...) and expose it with createUploadRoute(...).",
|
|
1491
|
+
]);
|
|
1267
1492
|
}
|
|
1268
1493
|
|
|
1269
1494
|
export async function main(
|
|
@@ -1272,7 +1497,15 @@ export async function main(
|
|
|
1272
1497
|
): Promise<void> {
|
|
1273
1498
|
if (inputs.length === 0) {
|
|
1274
1499
|
await run(cli, ["--help"], context);
|
|
1275
|
-
context.process.exitCode =
|
|
1500
|
+
context.process.exitCode = 2;
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
// Shell completion scripts call `beignet completion propose <words...>`.
|
|
1505
|
+
// Handle it before stricli parses the inputs: the trailing words are a
|
|
1506
|
+
// partial command line, not flags for this CLI.
|
|
1507
|
+
if (inputs[0] === "completion" && inputs[1] === "propose") {
|
|
1508
|
+
await writeCompletionProposals(inputs.slice(2), context);
|
|
1276
1509
|
return;
|
|
1277
1510
|
}
|
|
1278
1511
|
|
|
@@ -1280,11 +1513,42 @@ export async function main(
|
|
|
1280
1513
|
normalizeExitCode(context.process);
|
|
1281
1514
|
}
|
|
1282
1515
|
|
|
1516
|
+
async function writeCompletionProposals(
|
|
1517
|
+
inputs: readonly string[],
|
|
1518
|
+
context: StricliDynamicCommandContext<CliContext>,
|
|
1519
|
+
): Promise<void> {
|
|
1520
|
+
try {
|
|
1521
|
+
const proposals = await proposeCompletions(
|
|
1522
|
+
cli,
|
|
1523
|
+
inputs.length === 0 ? [""] : inputs,
|
|
1524
|
+
context,
|
|
1525
|
+
);
|
|
1526
|
+
for (const proposal of proposals) {
|
|
1527
|
+
context.process.stdout.write(`${proposal.completion}\n`);
|
|
1528
|
+
}
|
|
1529
|
+
} catch {
|
|
1530
|
+
// Completion failures must never break the shell; propose nothing.
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
// Stricli reports usage and internal failures as negative ExitCode values
|
|
1535
|
+
// (-5..-1). Node keeps the negative value on process.exitCode while Bun wraps
|
|
1536
|
+
// it to 251..255 on assignment, so both forms map to the usage exit code.
|
|
1537
|
+
const stricliErrorExitCodes = new Set([
|
|
1538
|
+
-5, -4, -3, -2, -1, 251, 252, 253, 254, 255,
|
|
1539
|
+
]);
|
|
1540
|
+
|
|
1283
1541
|
function normalizeExitCode(proc: StricliProcess): void {
|
|
1284
|
-
if (
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1542
|
+
if (typeof proc.exitCode !== "number") {
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
if (stricliErrorExitCodes.has(proc.exitCode)) {
|
|
1547
|
+
proc.exitCode = 2;
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
if (proc.exitCode > 127) {
|
|
1288
1552
|
proc.exitCode = 1;
|
|
1289
1553
|
}
|
|
1290
1554
|
}
|
|
@@ -1307,6 +1571,6 @@ function isCliEntrypoint(): boolean {
|
|
|
1307
1571
|
if (isCliEntrypoint()) {
|
|
1308
1572
|
main().catch((error: unknown) => {
|
|
1309
1573
|
console.error(error instanceof Error ? error.message : String(error));
|
|
1310
|
-
process.exitCode =
|
|
1574
|
+
process.exitCode = 2;
|
|
1311
1575
|
});
|
|
1312
1576
|
}
|