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