@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/dist/index.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { buildApplication, buildCommand, buildRouteMap, run, text_en, } from "@stricli/core";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export { applyDoctorFixes, createProject, formatDoctor, formatLint, formatRoutes, inspectApp, lintApp, makeAdapter, makeContract, makeEvent, makeFactory, makeFeature, makeFeatureAddonChoices, makeJob, makeListener, makeNotification, makePolicy, makePort, makeResource, makeSchedule, makeSeed, makeTest, makeUpload, makeUseCase, runDatabaseCommand, };
|
|
12
|
-
const templateChoices = ["next"];
|
|
13
|
-
const packageManagerChoices = [
|
|
14
|
-
"bun",
|
|
15
|
-
"npm",
|
|
16
|
-
"pnpm",
|
|
17
|
-
"yarn",
|
|
4
|
+
import { buildApplication, buildCommand, buildRouteMap, proposeCompletions, run, text_en, } from "@stricli/core";
|
|
5
|
+
import { completionShellChoices, integrationChoices, makeFeatureAddonChoices, packageManagerChoices, templateChoices, } from "./choices.js";
|
|
6
|
+
import { getCliVersion } from "./version.js";
|
|
7
|
+
const outputFormatChoices = [
|
|
8
|
+
"human",
|
|
9
|
+
"json",
|
|
10
|
+
"github",
|
|
18
11
|
];
|
|
19
12
|
const parseString = (input) => input;
|
|
13
|
+
const parsePositiveInteger = (input) => {
|
|
14
|
+
const value = Number(input);
|
|
15
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
16
|
+
throw new Error("Expected a positive integer.");
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
};
|
|
20
20
|
const forceFlag = {
|
|
21
21
|
kind: "boolean",
|
|
22
22
|
optional: true,
|
|
@@ -41,18 +41,61 @@ const parsedStringFlag = (brief) => ({
|
|
|
41
41
|
optional: true,
|
|
42
42
|
brief,
|
|
43
43
|
});
|
|
44
|
+
const cwdFlag = parsedStringFlag("App directory to inspect. Defaults to the current working directory.");
|
|
45
|
+
const formatFlag = {
|
|
46
|
+
kind: "enum",
|
|
47
|
+
values: outputFormatChoices,
|
|
48
|
+
optional: true,
|
|
49
|
+
brief: "Output format. Defaults to human, or github when GITHUB_ACTIONS=true.",
|
|
50
|
+
};
|
|
44
51
|
const makeFlagParameters = {
|
|
45
52
|
force: forceFlag,
|
|
46
53
|
dryRun: dryRunFlag,
|
|
47
54
|
json: jsonFlag,
|
|
48
55
|
};
|
|
49
|
-
const
|
|
56
|
+
const routesFlagParameters = {
|
|
57
|
+
json: jsonFlag,
|
|
58
|
+
cwd: cwdFlag,
|
|
59
|
+
};
|
|
60
|
+
const lintFlagParameters = {
|
|
50
61
|
json: jsonFlag,
|
|
62
|
+
cwd: cwdFlag,
|
|
63
|
+
format: formatFlag,
|
|
51
64
|
};
|
|
52
65
|
const dbFlagParameters = {
|
|
53
66
|
json: jsonFlag,
|
|
54
67
|
dryRun: dryRunFlag,
|
|
55
68
|
};
|
|
69
|
+
const taskRunFlagParameters = {
|
|
70
|
+
json: jsonFlag,
|
|
71
|
+
input: parsedStringFlag("JSON input passed to the task schema."),
|
|
72
|
+
module: parsedStringFlag("Task registry module. Defaults to server/tasks.ts."),
|
|
73
|
+
};
|
|
74
|
+
const outboxDrainFlagParameters = {
|
|
75
|
+
json: jsonFlag,
|
|
76
|
+
module: parsedStringFlag("Outbox registry module. Defaults to server/outbox.ts."),
|
|
77
|
+
batchSize: {
|
|
78
|
+
kind: "parsed",
|
|
79
|
+
parse: parsePositiveInteger,
|
|
80
|
+
optional: true,
|
|
81
|
+
brief: "Maximum messages to claim in one drain pass.",
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
const scheduleRunFlagParameters = {
|
|
85
|
+
json: jsonFlag,
|
|
86
|
+
module: parsedStringFlag("Schedule registry module. Defaults to server/schedules.ts."),
|
|
87
|
+
payload: parsedStringFlag("JSON payload passed to the schedule schema."),
|
|
88
|
+
id: parsedStringFlag("Provider or app schedule run ID."),
|
|
89
|
+
attempt: {
|
|
90
|
+
kind: "parsed",
|
|
91
|
+
parse: parsePositiveInteger,
|
|
92
|
+
optional: true,
|
|
93
|
+
brief: "One-based provider attempt number.",
|
|
94
|
+
},
|
|
95
|
+
scheduledAt: parsedStringFlag("Provider scheduled timestamp."),
|
|
96
|
+
triggeredAt: parsedStringFlag("Schedule trigger timestamp."),
|
|
97
|
+
source: parsedStringFlag("Provider or app source label."),
|
|
98
|
+
};
|
|
56
99
|
const namePositional = {
|
|
57
100
|
kind: "tuple",
|
|
58
101
|
parameters: [
|
|
@@ -63,12 +106,32 @@ const namePositional = {
|
|
|
63
106
|
},
|
|
64
107
|
],
|
|
65
108
|
};
|
|
109
|
+
function resolveOutputFormat(flags) {
|
|
110
|
+
if (flags.json) {
|
|
111
|
+
if (flags.format && flags.format !== "json") {
|
|
112
|
+
throw new Error(`--json conflicts with --format ${flags.format}. Pass one of them.`);
|
|
113
|
+
}
|
|
114
|
+
return "json";
|
|
115
|
+
}
|
|
116
|
+
if (flags.format)
|
|
117
|
+
return flags.format;
|
|
118
|
+
return process.env.GITHUB_ACTIONS === "true" ? "github" : "human";
|
|
119
|
+
}
|
|
120
|
+
function useColor() {
|
|
121
|
+
return process.stdout.isTTY === true && !process.env.NO_COLOR;
|
|
122
|
+
}
|
|
66
123
|
const createCommand = buildCommand({
|
|
67
124
|
docs: {
|
|
68
125
|
brief: "Create a new Beignet app.",
|
|
69
|
-
fullDescription: `
|
|
126
|
+
fullDescription: `Scaffolds a full-stack Beignet starter: typed contracts, Better Auth,
|
|
127
|
+
Drizzle/libSQL persistence with checked-in migrations, pino logging,
|
|
128
|
+
devtools, and a shadcn UI shell. Pass --api for an API-only scaffold
|
|
129
|
+
without the UI shell.
|
|
130
|
+
|
|
131
|
+
Available integrations: ${integrationChoices.join(", ")}
|
|
70
132
|
|
|
71
|
-
|
|
133
|
+
Running create on an interactive terminal without selection flags opens a
|
|
134
|
+
prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
|
|
72
135
|
},
|
|
73
136
|
parameters: {
|
|
74
137
|
flags: {
|
|
@@ -78,11 +141,11 @@ Available integrations: ${integrationChoices.join(", ")}`,
|
|
|
78
141
|
default: "next",
|
|
79
142
|
brief: "Template to use.",
|
|
80
143
|
},
|
|
81
|
-
|
|
82
|
-
kind: "
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
brief: "
|
|
144
|
+
api: {
|
|
145
|
+
kind: "boolean",
|
|
146
|
+
optional: true,
|
|
147
|
+
withNegated: false,
|
|
148
|
+
brief: "Scaffold an API-only app without the UI shell.",
|
|
86
149
|
},
|
|
87
150
|
packageManager: {
|
|
88
151
|
kind: "enum",
|
|
@@ -90,35 +153,22 @@ Available integrations: ${integrationChoices.join(", ")}`,
|
|
|
90
153
|
optional: true,
|
|
91
154
|
brief: "Package manager to use in next-step commands.",
|
|
92
155
|
},
|
|
93
|
-
feature: {
|
|
94
|
-
kind: "enum",
|
|
95
|
-
values: featureChoices,
|
|
96
|
-
optional: true,
|
|
97
|
-
variadic: true,
|
|
98
|
-
brief: "Add a starter feature. Repeatable.",
|
|
99
|
-
},
|
|
100
|
-
features: {
|
|
101
|
-
kind: "enum",
|
|
102
|
-
values: featureChoices,
|
|
103
|
-
optional: true,
|
|
104
|
-
variadic: ",",
|
|
105
|
-
brief: "Add comma-separated starter features.",
|
|
106
|
-
},
|
|
107
|
-
integration: {
|
|
108
|
-
kind: "enum",
|
|
109
|
-
values: integrationChoices,
|
|
110
|
-
optional: true,
|
|
111
|
-
variadic: true,
|
|
112
|
-
brief: "Add a first-party integration. Repeatable.",
|
|
113
|
-
},
|
|
114
156
|
integrations: {
|
|
115
157
|
kind: "enum",
|
|
116
158
|
values: integrationChoices,
|
|
117
159
|
optional: true,
|
|
118
160
|
variadic: ",",
|
|
119
|
-
brief: "Add
|
|
161
|
+
brief: "Add first-party integrations as a comma-separated list.",
|
|
162
|
+
},
|
|
163
|
+
yes: {
|
|
164
|
+
kind: "boolean",
|
|
165
|
+
optional: true,
|
|
166
|
+
withNegated: false,
|
|
167
|
+
brief: "Skip interactive prompts and use the defaults.",
|
|
120
168
|
},
|
|
121
169
|
force: forceFlag,
|
|
170
|
+
dryRun: dryRunFlag,
|
|
171
|
+
json: jsonFlag,
|
|
122
172
|
},
|
|
123
173
|
positional: {
|
|
124
174
|
kind: "tuple",
|
|
@@ -127,28 +177,52 @@ Available integrations: ${integrationChoices.join(", ")}`,
|
|
|
127
177
|
parse: parseString,
|
|
128
178
|
placeholder: "directory",
|
|
129
179
|
brief: "Project directory to create.",
|
|
180
|
+
optional: true,
|
|
130
181
|
},
|
|
131
182
|
],
|
|
132
183
|
},
|
|
133
184
|
},
|
|
134
|
-
async
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
185
|
+
loader: async () => {
|
|
186
|
+
const { createProject } = await import("./create.js");
|
|
187
|
+
const { promptCreateSelections, shouldPromptInteractively } = await import("./create-prompts.js");
|
|
188
|
+
return async function runCreate(flags, directory) {
|
|
189
|
+
let selections = {
|
|
190
|
+
directory,
|
|
191
|
+
api: flags.api,
|
|
192
|
+
integrations: flags.integrations,
|
|
193
|
+
packageManager: flags.packageManager,
|
|
194
|
+
};
|
|
195
|
+
const interactive = shouldPromptInteractively({ ...selections, yes: flags.yes }, {
|
|
196
|
+
stdin: process.stdin.isTTY === true,
|
|
197
|
+
stdout: process.stdout.isTTY === true,
|
|
198
|
+
});
|
|
199
|
+
if (interactive) {
|
|
200
|
+
const answers = await promptCreateSelections(selections);
|
|
201
|
+
if (answers === undefined) {
|
|
202
|
+
this.process.stderr.write("Cancelled.\n");
|
|
203
|
+
this.process.exitCode = 2;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
selections = answers;
|
|
207
|
+
}
|
|
208
|
+
if (selections.directory === undefined) {
|
|
209
|
+
throw new Error("Project directory is required. Pass beignet create <directory>, or run beignet create in an interactive terminal.");
|
|
210
|
+
}
|
|
211
|
+
const api = selections.api ?? false;
|
|
212
|
+
const integrations = uniqueValues([...(selections.integrations ?? [])]);
|
|
213
|
+
const result = await createProject({
|
|
214
|
+
name: selections.directory,
|
|
215
|
+
template: flags.template,
|
|
216
|
+
api,
|
|
217
|
+
packageManager: selections.packageManager,
|
|
218
|
+
integrations,
|
|
219
|
+
force: Boolean(flags.force),
|
|
220
|
+
dryRun: Boolean(flags.dryRun),
|
|
221
|
+
});
|
|
222
|
+
writeOutput(this, flags.json
|
|
223
|
+
? JSON.stringify(result, null, 2)
|
|
224
|
+
: nextSteps(result, { api, integrations }));
|
|
225
|
+
};
|
|
152
226
|
},
|
|
153
227
|
});
|
|
154
228
|
const routesCommand = buildCommand({
|
|
@@ -156,11 +230,14 @@ const routesCommand = buildCommand({
|
|
|
156
230
|
brief: "Inspect registered Beignet routes.",
|
|
157
231
|
},
|
|
158
232
|
parameters: {
|
|
159
|
-
flags:
|
|
233
|
+
flags: routesFlagParameters,
|
|
160
234
|
},
|
|
161
|
-
async
|
|
162
|
-
const
|
|
163
|
-
|
|
235
|
+
loader: async () => {
|
|
236
|
+
const { formatRoutes, inspectApp } = await import("./inspect.js");
|
|
237
|
+
return async function runRoutes(flags) {
|
|
238
|
+
const result = await inspectApp({ cwd: flags.cwd });
|
|
239
|
+
writeOutput(this, flags.json ? JSON.stringify(result, null, 2) : formatRoutes(result));
|
|
240
|
+
};
|
|
164
241
|
},
|
|
165
242
|
});
|
|
166
243
|
const doctorCommand = buildCommand({
|
|
@@ -182,19 +259,35 @@ const doctorCommand = buildCommand({
|
|
|
182
259
|
withNegated: false,
|
|
183
260
|
brief: "Apply low-risk fixes before reporting.",
|
|
184
261
|
},
|
|
262
|
+
cwd: cwdFlag,
|
|
263
|
+
format: formatFlag,
|
|
185
264
|
},
|
|
186
265
|
},
|
|
187
|
-
async
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
266
|
+
loader: async () => {
|
|
267
|
+
const { applyDoctorFixes, formatDoctor, formatDoctorGithub, inspectApp } = await import("./inspect.js");
|
|
268
|
+
return async function runDoctor(flags) {
|
|
269
|
+
const format = resolveOutputFormat(flags);
|
|
270
|
+
const fixes = flags.fix
|
|
271
|
+
? await applyDoctorFixes({
|
|
272
|
+
cwd: flags.cwd,
|
|
273
|
+
strict: Boolean(flags.strict),
|
|
274
|
+
})
|
|
275
|
+
: [];
|
|
276
|
+
const result = await inspectApp({
|
|
277
|
+
cwd: flags.cwd,
|
|
278
|
+
strict: Boolean(flags.strict),
|
|
279
|
+
});
|
|
280
|
+
result.fixes = fixes;
|
|
281
|
+
writeOutput(this, format === "json"
|
|
282
|
+
? JSON.stringify(result, null, 2)
|
|
283
|
+
: format === "github"
|
|
284
|
+
? formatDoctorGithub(result)
|
|
285
|
+
: formatDoctor(result, { color: useColor() }));
|
|
286
|
+
if (result.diagnostics.some((diagnostic) => diagnostic.severity === "error" ||
|
|
287
|
+
(flags.strict && diagnostic.severity === "warning"))) {
|
|
288
|
+
this.process.exitCode = 1;
|
|
289
|
+
}
|
|
290
|
+
};
|
|
198
291
|
},
|
|
199
292
|
});
|
|
200
293
|
const lintCommand = buildCommand({
|
|
@@ -202,14 +295,22 @@ const lintCommand = buildCommand({
|
|
|
202
295
|
brief: "Check Beignet dependency direction conventions.",
|
|
203
296
|
},
|
|
204
297
|
parameters: {
|
|
205
|
-
flags:
|
|
206
|
-
},
|
|
207
|
-
async
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
298
|
+
flags: lintFlagParameters,
|
|
299
|
+
},
|
|
300
|
+
loader: async () => {
|
|
301
|
+
const { formatLint, formatLintGithub, lintApp } = await import("./lint.js");
|
|
302
|
+
return async function runLint(flags) {
|
|
303
|
+
const format = resolveOutputFormat(flags);
|
|
304
|
+
const result = await lintApp({ cwd: flags.cwd });
|
|
305
|
+
writeOutput(this, format === "json"
|
|
306
|
+
? JSON.stringify(result, null, 2)
|
|
307
|
+
: format === "github"
|
|
308
|
+
? formatLintGithub(result)
|
|
309
|
+
: formatLint(result, { color: useColor() }));
|
|
310
|
+
if (result.diagnostics.length > 0) {
|
|
311
|
+
this.process.exitCode = 1;
|
|
312
|
+
}
|
|
313
|
+
};
|
|
213
314
|
},
|
|
214
315
|
});
|
|
215
316
|
function databaseCommand(command) {
|
|
@@ -220,18 +321,21 @@ function databaseCommand(command) {
|
|
|
220
321
|
parameters: {
|
|
221
322
|
flags: dbFlagParameters,
|
|
222
323
|
},
|
|
223
|
-
async
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
324
|
+
loader: async () => {
|
|
325
|
+
const { runDatabaseCommand } = await import("./db.js");
|
|
326
|
+
return async function runDb(flags) {
|
|
327
|
+
const result = await runDatabaseCommand({
|
|
328
|
+
command,
|
|
329
|
+
captureOutput: Boolean(flags.json),
|
|
330
|
+
dryRun: Boolean(flags.dryRun),
|
|
331
|
+
});
|
|
332
|
+
writeOutput(this, flags.json
|
|
333
|
+
? JSON.stringify(result, null, 2)
|
|
334
|
+
: databaseCommandNextSteps(result));
|
|
335
|
+
if (result.exitCode !== 0) {
|
|
336
|
+
this.process.exitCode = result.exitCode;
|
|
337
|
+
}
|
|
338
|
+
};
|
|
235
339
|
},
|
|
236
340
|
});
|
|
237
341
|
}
|
|
@@ -246,330 +350,325 @@ const dbRoutes = buildRouteMap({
|
|
|
246
350
|
seed: databaseCommand("seed"),
|
|
247
351
|
},
|
|
248
352
|
});
|
|
249
|
-
const
|
|
250
|
-
docs: {
|
|
251
|
-
brief: "Generate a feature resource.",
|
|
252
|
-
},
|
|
253
|
-
parameters: {
|
|
254
|
-
flags: makeFlagParameters,
|
|
255
|
-
positional: namePositional,
|
|
256
|
-
},
|
|
257
|
-
async func(flags, name) {
|
|
258
|
-
const result = await makeResource({
|
|
259
|
-
name,
|
|
260
|
-
force: Boolean(flags.force),
|
|
261
|
-
dryRun: Boolean(flags.dryRun),
|
|
262
|
-
});
|
|
263
|
-
writeOutput(this, flags.json
|
|
264
|
-
? JSON.stringify(result, null, 2)
|
|
265
|
-
: makeResourceNextSteps(result));
|
|
266
|
-
},
|
|
267
|
-
});
|
|
268
|
-
const makeFeatureCommand = buildCommand({
|
|
353
|
+
const taskRunCommand = buildCommand({
|
|
269
354
|
docs: {
|
|
270
|
-
brief: "
|
|
355
|
+
brief: "Run an app-owned operational task.",
|
|
271
356
|
},
|
|
272
357
|
parameters: {
|
|
273
|
-
flags:
|
|
274
|
-
...makeFlagParameters,
|
|
275
|
-
with: {
|
|
276
|
-
kind: "enum",
|
|
277
|
-
values: makeFeatureAddonChoices,
|
|
278
|
-
optional: true,
|
|
279
|
-
variadic: ",",
|
|
280
|
-
brief: "Add feature-owned artifacts. Supports policy, event/events, job/jobs, and upload/uploads.",
|
|
281
|
-
},
|
|
282
|
-
},
|
|
358
|
+
flags: taskRunFlagParameters,
|
|
283
359
|
positional: namePositional,
|
|
284
360
|
},
|
|
285
|
-
async
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
361
|
+
loader: async () => {
|
|
362
|
+
const { runAppTask } = await import("./task.js");
|
|
363
|
+
return async function runTask(flags, name) {
|
|
364
|
+
const result = await runAppTask({
|
|
365
|
+
name,
|
|
366
|
+
input: flags.input,
|
|
367
|
+
modulePath: flags.module,
|
|
368
|
+
});
|
|
369
|
+
writeOutput(this, flags.json
|
|
370
|
+
? JSON.stringify(result, null, 2)
|
|
371
|
+
: appTaskRunNextSteps(result));
|
|
372
|
+
};
|
|
295
373
|
},
|
|
296
374
|
});
|
|
297
|
-
const
|
|
375
|
+
const taskRoutes = buildRouteMap({
|
|
298
376
|
docs: {
|
|
299
|
-
brief: "
|
|
300
|
-
},
|
|
301
|
-
parameters: {
|
|
302
|
-
flags: makeFlagParameters,
|
|
303
|
-
positional: namePositional,
|
|
377
|
+
brief: "Run Beignet app operational tasks.",
|
|
304
378
|
},
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
name,
|
|
308
|
-
force: Boolean(flags.force),
|
|
309
|
-
dryRun: Boolean(flags.dryRun),
|
|
310
|
-
});
|
|
311
|
-
writeOutput(this, flags.json
|
|
312
|
-
? JSON.stringify(result, null, 2)
|
|
313
|
-
: makeContractNextSteps(result));
|
|
379
|
+
routes: {
|
|
380
|
+
run: taskRunCommand,
|
|
314
381
|
},
|
|
315
382
|
});
|
|
316
|
-
const
|
|
383
|
+
const outboxDrainCommand = buildCommand({
|
|
317
384
|
docs: {
|
|
318
|
-
brief: "
|
|
385
|
+
brief: "Run one app-owned outbox drain pass.",
|
|
319
386
|
},
|
|
320
387
|
parameters: {
|
|
321
|
-
flags:
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
388
|
+
flags: outboxDrainFlagParameters,
|
|
389
|
+
},
|
|
390
|
+
loader: async () => {
|
|
391
|
+
const { runOutboxDrain } = await import("./outbox.js");
|
|
392
|
+
return async function runDrain(flags) {
|
|
393
|
+
const result = await runOutboxDrain({
|
|
394
|
+
modulePath: flags.module,
|
|
395
|
+
batchSize: flags.batchSize,
|
|
396
|
+
});
|
|
397
|
+
writeOutput(this, flags.json
|
|
398
|
+
? JSON.stringify(result, null, 2)
|
|
399
|
+
: outboxDrainNextSteps(result));
|
|
400
|
+
};
|
|
333
401
|
},
|
|
334
402
|
});
|
|
335
|
-
const
|
|
403
|
+
const outboxRoutes = buildRouteMap({
|
|
336
404
|
docs: {
|
|
337
|
-
brief: "
|
|
405
|
+
brief: "Run Beignet outbox operations.",
|
|
338
406
|
},
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
positional: namePositional,
|
|
342
|
-
},
|
|
343
|
-
async func(flags, name) {
|
|
344
|
-
const result = await makeTest({
|
|
345
|
-
name,
|
|
346
|
-
force: Boolean(flags.force),
|
|
347
|
-
dryRun: Boolean(flags.dryRun),
|
|
348
|
-
});
|
|
349
|
-
writeOutput(this, flags.json ? JSON.stringify(result, null, 2) : makeTestNextSteps(result));
|
|
407
|
+
routes: {
|
|
408
|
+
drain: outboxDrainCommand,
|
|
350
409
|
},
|
|
351
410
|
});
|
|
352
|
-
const
|
|
411
|
+
const scheduleRunCommand = buildCommand({
|
|
353
412
|
docs: {
|
|
354
|
-
brief: "
|
|
413
|
+
brief: "Run an app-owned schedule.",
|
|
355
414
|
},
|
|
356
415
|
parameters: {
|
|
357
|
-
flags:
|
|
416
|
+
flags: scheduleRunFlagParameters,
|
|
358
417
|
positional: namePositional,
|
|
359
418
|
},
|
|
360
|
-
async
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
419
|
+
loader: async () => {
|
|
420
|
+
const { runAppSchedule } = await import("./schedule.js");
|
|
421
|
+
return async function runSchedule(flags, name) {
|
|
422
|
+
const result = await runAppSchedule({
|
|
423
|
+
name,
|
|
424
|
+
modulePath: flags.module,
|
|
425
|
+
payload: flags.payload,
|
|
426
|
+
id: flags.id,
|
|
427
|
+
attempt: flags.attempt,
|
|
428
|
+
scheduledAt: flags.scheduledAt,
|
|
429
|
+
triggeredAt: flags.triggeredAt,
|
|
430
|
+
source: flags.source,
|
|
431
|
+
});
|
|
432
|
+
writeOutput(this, flags.json
|
|
433
|
+
? JSON.stringify(result, null, 2)
|
|
434
|
+
: scheduleRunNextSteps(result));
|
|
435
|
+
};
|
|
367
436
|
},
|
|
368
437
|
});
|
|
369
|
-
const
|
|
438
|
+
const scheduleRoutes = buildRouteMap({
|
|
370
439
|
docs: {
|
|
371
|
-
brief: "
|
|
372
|
-
},
|
|
373
|
-
parameters: {
|
|
374
|
-
flags: makeFlagParameters,
|
|
375
|
-
positional: namePositional,
|
|
440
|
+
brief: "Run Beignet schedule operations.",
|
|
376
441
|
},
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
name,
|
|
380
|
-
force: Boolean(flags.force),
|
|
381
|
-
dryRun: Boolean(flags.dryRun),
|
|
382
|
-
});
|
|
383
|
-
writeOutput(this, flags.json
|
|
384
|
-
? JSON.stringify(result, null, 2)
|
|
385
|
-
: makeAdapterNextSteps(result));
|
|
442
|
+
routes: {
|
|
443
|
+
run: scheduleRunCommand,
|
|
386
444
|
},
|
|
387
445
|
});
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
positional: namePositional,
|
|
395
|
-
},
|
|
396
|
-
async func(flags, name) {
|
|
397
|
-
const result = await makePolicy({
|
|
398
|
-
name,
|
|
399
|
-
force: Boolean(flags.force),
|
|
400
|
-
dryRun: Boolean(flags.dryRun),
|
|
401
|
-
});
|
|
402
|
-
writeOutput(this, flags.json
|
|
403
|
-
? JSON.stringify(result, null, 2)
|
|
404
|
-
: makePolicyNextSteps(result));
|
|
446
|
+
const completionShellFlagParameters = {
|
|
447
|
+
shell: {
|
|
448
|
+
kind: "enum",
|
|
449
|
+
values: completionShellChoices,
|
|
450
|
+
optional: true,
|
|
451
|
+
brief: "Shell to manage completions for. Defaults to $SHELL.",
|
|
405
452
|
},
|
|
406
|
-
|
|
407
|
-
|
|
453
|
+
json: jsonFlag,
|
|
454
|
+
};
|
|
455
|
+
const completionInstallCommand = buildCommand({
|
|
408
456
|
docs: {
|
|
409
|
-
brief: "
|
|
457
|
+
brief: "Install beignet shell completions for bash or zsh.",
|
|
410
458
|
},
|
|
411
459
|
parameters: {
|
|
412
|
-
flags:
|
|
413
|
-
positional: namePositional,
|
|
460
|
+
flags: completionShellFlagParameters,
|
|
414
461
|
},
|
|
415
|
-
async
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
462
|
+
loader: async () => {
|
|
463
|
+
const { installCompletions } = await import("./completion.js");
|
|
464
|
+
return async function runInstall(flags) {
|
|
465
|
+
const result = await installCompletions({ shell: flags.shell });
|
|
466
|
+
writeOutput(this, flags.json
|
|
467
|
+
? JSON.stringify(result, null, 2)
|
|
468
|
+
: `${result.status === "updated" ? "Updated" : "Installed"} ${result.shell} completions in ${result.file}
|
|
469
|
+
|
|
470
|
+
Restart your shell or source the file to activate them.`);
|
|
471
|
+
};
|
|
422
472
|
},
|
|
423
473
|
});
|
|
424
|
-
const
|
|
474
|
+
const completionUninstallCommand = buildCommand({
|
|
425
475
|
docs: {
|
|
426
|
-
brief: "
|
|
476
|
+
brief: "Remove beignet shell completions for bash or zsh.",
|
|
427
477
|
},
|
|
428
478
|
parameters: {
|
|
429
|
-
flags:
|
|
430
|
-
positional: namePositional,
|
|
479
|
+
flags: completionShellFlagParameters,
|
|
431
480
|
},
|
|
432
|
-
async
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
481
|
+
loader: async () => {
|
|
482
|
+
const { uninstallCompletions } = await import("./completion.js");
|
|
483
|
+
return async function runUninstall(flags) {
|
|
484
|
+
const result = await uninstallCompletions({ shell: flags.shell });
|
|
485
|
+
writeOutput(this, flags.json
|
|
486
|
+
? JSON.stringify(result, null, 2)
|
|
487
|
+
: result.status === "removed"
|
|
488
|
+
? `Removed ${result.shell} completions from ${result.file}`
|
|
489
|
+
: `No beignet completions found in ${result.file}`);
|
|
490
|
+
};
|
|
439
491
|
},
|
|
440
492
|
});
|
|
441
|
-
const
|
|
493
|
+
const completionRoutes = buildRouteMap({
|
|
442
494
|
docs: {
|
|
443
|
-
brief: "
|
|
444
|
-
},
|
|
445
|
-
parameters: {
|
|
446
|
-
flags: makeFlagParameters,
|
|
447
|
-
positional: namePositional,
|
|
495
|
+
brief: "Manage beignet shell completions.",
|
|
448
496
|
},
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
force: Boolean(flags.force),
|
|
453
|
-
dryRun: Boolean(flags.dryRun),
|
|
454
|
-
});
|
|
455
|
-
writeOutput(this, flags.json
|
|
456
|
-
? JSON.stringify(result, null, 2)
|
|
457
|
-
: makeFactoryNextSteps(result));
|
|
497
|
+
routes: {
|
|
498
|
+
install: completionInstallCommand,
|
|
499
|
+
uninstall: completionUninstallCommand,
|
|
458
500
|
},
|
|
459
501
|
});
|
|
460
|
-
const
|
|
502
|
+
const makeResourceCommand = buildCommand({
|
|
461
503
|
docs: {
|
|
462
|
-
brief: "Generate a
|
|
504
|
+
brief: "Generate a CRUD-shaped resource slice.",
|
|
463
505
|
},
|
|
464
506
|
parameters: {
|
|
465
|
-
flags:
|
|
507
|
+
flags: {
|
|
508
|
+
...makeFlagParameters,
|
|
509
|
+
auth: {
|
|
510
|
+
kind: "boolean",
|
|
511
|
+
optional: true,
|
|
512
|
+
withNegated: false,
|
|
513
|
+
brief: "Generate policy metadata and use-case authorization checks for mutating routes.",
|
|
514
|
+
},
|
|
515
|
+
tenant: {
|
|
516
|
+
kind: "boolean",
|
|
517
|
+
optional: true,
|
|
518
|
+
withNegated: false,
|
|
519
|
+
brief: "Generate tenant-scoped schemas, repository methods, and use-case checks.",
|
|
520
|
+
},
|
|
521
|
+
events: {
|
|
522
|
+
kind: "boolean",
|
|
523
|
+
optional: true,
|
|
524
|
+
withNegated: false,
|
|
525
|
+
brief: "Generate domain event definitions and publish resource events.",
|
|
526
|
+
},
|
|
527
|
+
softDelete: {
|
|
528
|
+
kind: "boolean",
|
|
529
|
+
optional: true,
|
|
530
|
+
withNegated: false,
|
|
531
|
+
brief: "Generate soft-delete persistence that archives rows with deletedAt.",
|
|
532
|
+
},
|
|
533
|
+
},
|
|
466
534
|
positional: namePositional,
|
|
467
535
|
},
|
|
468
|
-
async
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
536
|
+
loader: async () => {
|
|
537
|
+
const { makeResource } = await import("./make.js");
|
|
538
|
+
return async function runMakeResource(flags, name) {
|
|
539
|
+
const result = await makeResource({
|
|
540
|
+
name,
|
|
541
|
+
force: Boolean(flags.force),
|
|
542
|
+
dryRun: Boolean(flags.dryRun),
|
|
543
|
+
auth: Boolean(flags.auth),
|
|
544
|
+
tenant: Boolean(flags.tenant),
|
|
545
|
+
events: Boolean(flags.events),
|
|
546
|
+
softDelete: Boolean(flags.softDelete),
|
|
547
|
+
});
|
|
548
|
+
writeOutput(this, flags.json
|
|
549
|
+
? JSON.stringify(result, null, 2)
|
|
550
|
+
: makeResourceNextSteps(result));
|
|
551
|
+
};
|
|
475
552
|
},
|
|
476
553
|
});
|
|
477
|
-
const
|
|
554
|
+
const makeFeatureCommand = buildCommand({
|
|
478
555
|
docs: {
|
|
479
|
-
brief: "Generate a feature
|
|
556
|
+
brief: "Generate a contract-first feature slice.",
|
|
480
557
|
},
|
|
481
558
|
parameters: {
|
|
482
|
-
flags:
|
|
559
|
+
flags: {
|
|
560
|
+
...makeFlagParameters,
|
|
561
|
+
with: {
|
|
562
|
+
kind: "enum",
|
|
563
|
+
values: makeFeatureAddonChoices,
|
|
564
|
+
optional: true,
|
|
565
|
+
variadic: ",",
|
|
566
|
+
brief: "Add feature-owned artifacts. Supports policy, task/tasks, event/events, job/jobs, notification/notifications, ui, and upload/uploads.",
|
|
567
|
+
},
|
|
568
|
+
},
|
|
483
569
|
positional: namePositional,
|
|
484
570
|
},
|
|
485
|
-
async
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
571
|
+
loader: async () => {
|
|
572
|
+
const { makeFeature } = await import("./make.js");
|
|
573
|
+
return async function runMakeFeature(flags, name) {
|
|
574
|
+
const result = await makeFeature({
|
|
575
|
+
name,
|
|
576
|
+
with: flags.with,
|
|
577
|
+
force: Boolean(flags.force),
|
|
578
|
+
dryRun: Boolean(flags.dryRun),
|
|
579
|
+
});
|
|
580
|
+
writeOutput(this, flags.json
|
|
581
|
+
? JSON.stringify(result, null, 2)
|
|
582
|
+
: makeFeatureNextSteps(result));
|
|
583
|
+
};
|
|
494
584
|
},
|
|
495
585
|
});
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
586
|
+
function simpleMakeCommand(generator, brief, formatNextSteps) {
|
|
587
|
+
return buildCommand({
|
|
588
|
+
docs: {
|
|
589
|
+
brief,
|
|
590
|
+
},
|
|
591
|
+
parameters: {
|
|
592
|
+
flags: makeFlagParameters,
|
|
593
|
+
positional: namePositional,
|
|
594
|
+
},
|
|
595
|
+
loader: async () => {
|
|
596
|
+
const generators = await import("./make.js");
|
|
597
|
+
return async function runMake(flags, name) {
|
|
598
|
+
const result = await generators[generator]({
|
|
599
|
+
name,
|
|
600
|
+
force: Boolean(flags.force),
|
|
601
|
+
dryRun: Boolean(flags.dryRun),
|
|
602
|
+
});
|
|
603
|
+
writeOutput(this, flags.json
|
|
604
|
+
? JSON.stringify(result, null, 2)
|
|
605
|
+
: formatNextSteps(result));
|
|
606
|
+
};
|
|
607
|
+
},
|
|
608
|
+
});
|
|
609
|
+
}
|
|
500
610
|
const makeListenerCommand = buildCommand({
|
|
501
611
|
docs: {
|
|
502
612
|
brief: "Generate a feature event listener.",
|
|
503
613
|
},
|
|
504
614
|
parameters: {
|
|
505
|
-
flags:
|
|
615
|
+
flags: {
|
|
616
|
+
...makeFlagParameters,
|
|
617
|
+
event: parsedStringFlag("Event to listen to, for example posts/published."),
|
|
618
|
+
},
|
|
506
619
|
positional: namePositional,
|
|
507
620
|
},
|
|
508
|
-
async
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
621
|
+
loader: async () => {
|
|
622
|
+
const { makeListener } = await import("./make.js");
|
|
623
|
+
return async function runMakeListener(flags, name) {
|
|
624
|
+
if (!flags.event) {
|
|
625
|
+
throw new Error("beignet make listener requires --event feature/name, for example --event posts/published.");
|
|
626
|
+
}
|
|
627
|
+
const result = await makeListener({
|
|
628
|
+
name,
|
|
629
|
+
event: flags.event,
|
|
630
|
+
force: Boolean(flags.force),
|
|
631
|
+
dryRun: Boolean(flags.dryRun),
|
|
632
|
+
});
|
|
633
|
+
writeOutput(this, flags.json
|
|
634
|
+
? JSON.stringify(result, null, 2)
|
|
635
|
+
: makeListenerNextSteps(result));
|
|
636
|
+
};
|
|
521
637
|
},
|
|
522
638
|
});
|
|
523
|
-
const makeScheduleFlagParameters = {
|
|
524
|
-
...makeFlagParameters,
|
|
525
|
-
cron: parsedStringFlag("Cron expression. Defaults to 0 9 * * *."),
|
|
526
|
-
timezone: parsedStringFlag("IANA timezone, for example America/Chicago."),
|
|
527
|
-
route: {
|
|
528
|
-
kind: "boolean",
|
|
529
|
-
optional: true,
|
|
530
|
-
withNegated: false,
|
|
531
|
-
brief: "Generate a Next.js cron route for this schedule.",
|
|
532
|
-
},
|
|
533
|
-
};
|
|
534
639
|
const makeScheduleCommand = buildCommand({
|
|
535
640
|
docs: {
|
|
536
641
|
brief: "Generate a feature schedule.",
|
|
537
642
|
},
|
|
538
643
|
parameters: {
|
|
539
|
-
flags:
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
});
|
|
551
|
-
writeOutput(this, flags.json
|
|
552
|
-
? JSON.stringify(result, null, 2)
|
|
553
|
-
: makeScheduleNextSteps(result));
|
|
554
|
-
},
|
|
555
|
-
});
|
|
556
|
-
const makeUploadCommand = buildCommand({
|
|
557
|
-
docs: {
|
|
558
|
-
brief: "Generate a feature upload.",
|
|
559
|
-
},
|
|
560
|
-
parameters: {
|
|
561
|
-
flags: makeFlagParameters,
|
|
644
|
+
flags: {
|
|
645
|
+
...makeFlagParameters,
|
|
646
|
+
cron: parsedStringFlag("Cron expression. Defaults to 0 9 * * *."),
|
|
647
|
+
timezone: parsedStringFlag("IANA timezone, for example America/Chicago."),
|
|
648
|
+
route: {
|
|
649
|
+
kind: "boolean",
|
|
650
|
+
optional: true,
|
|
651
|
+
withNegated: false,
|
|
652
|
+
brief: "Generate a Next.js cron route for this schedule.",
|
|
653
|
+
},
|
|
654
|
+
},
|
|
562
655
|
positional: namePositional,
|
|
563
656
|
},
|
|
564
|
-
async
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
657
|
+
loader: async () => {
|
|
658
|
+
const { makeSchedule } = await import("./make.js");
|
|
659
|
+
return async function runMakeSchedule(flags, name) {
|
|
660
|
+
const result = await makeSchedule({
|
|
661
|
+
name,
|
|
662
|
+
cron: flags.cron,
|
|
663
|
+
timezone: flags.timezone,
|
|
664
|
+
route: Boolean(flags.route),
|
|
665
|
+
force: Boolean(flags.force),
|
|
666
|
+
dryRun: Boolean(flags.dryRun),
|
|
667
|
+
});
|
|
668
|
+
writeOutput(this, flags.json
|
|
669
|
+
? JSON.stringify(result, null, 2)
|
|
670
|
+
: makeScheduleNextSteps(result, { route: Boolean(flags.route) }));
|
|
671
|
+
};
|
|
573
672
|
},
|
|
574
673
|
});
|
|
575
674
|
const makeRoutes = buildRouteMap({
|
|
@@ -577,22 +676,23 @@ const makeRoutes = buildRouteMap({
|
|
|
577
676
|
brief: "Generate Beignet app files.",
|
|
578
677
|
},
|
|
579
678
|
routes: {
|
|
580
|
-
adapter:
|
|
581
|
-
contract:
|
|
582
|
-
event:
|
|
583
|
-
factory:
|
|
679
|
+
adapter: simpleMakeCommand("makeAdapter", "Generate a port adapter.", makeAdapterNextSteps),
|
|
680
|
+
contract: simpleMakeCommand("makeContract", "Generate a contract group.", makeContractNextSteps),
|
|
681
|
+
event: simpleMakeCommand("makeEvent", "Generate a feature event.", makeEventNextSteps),
|
|
682
|
+
factory: simpleMakeCommand("makeFactory", "Generate a feature test data factory.", makeFactoryNextSteps),
|
|
584
683
|
feature: makeFeatureCommand,
|
|
585
|
-
job:
|
|
586
|
-
notification:
|
|
684
|
+
job: simpleMakeCommand("makeJob", "Generate a feature job.", makeJobNextSteps),
|
|
685
|
+
notification: simpleMakeCommand("makeNotification", "Generate a feature notification.", makeNotificationNextSteps),
|
|
587
686
|
listener: makeListenerCommand,
|
|
588
|
-
policy:
|
|
589
|
-
port:
|
|
687
|
+
policy: simpleMakeCommand("makePolicy", "Generate an authorization policy.", makePolicyNextSteps),
|
|
688
|
+
port: simpleMakeCommand("makePort", "Generate an application port.", makePortNextSteps),
|
|
590
689
|
resource: makeResourceCommand,
|
|
591
690
|
schedule: makeScheduleCommand,
|
|
592
|
-
seed:
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
691
|
+
seed: simpleMakeCommand("makeSeed", "Generate a feature seed.", makeSeedNextSteps),
|
|
692
|
+
task: simpleMakeCommand("makeTask", "Generate a feature operational task.", makeTaskNextSteps),
|
|
693
|
+
test: simpleMakeCommand("makeTest", "Generate a use case test.", makeTestNextSteps),
|
|
694
|
+
upload: simpleMakeCommand("makeUpload", "Generate a feature upload.", makeUploadNextSteps),
|
|
695
|
+
useCase: simpleMakeCommand("makeUseCase", "Generate a use case.", makeUseCaseNextSteps),
|
|
596
696
|
},
|
|
597
697
|
});
|
|
598
698
|
const rootRoutes = buildRouteMap({
|
|
@@ -600,19 +700,26 @@ const rootRoutes = buildRouteMap({
|
|
|
600
700
|
brief: "Beignet CLI",
|
|
601
701
|
fullDescription: `Create apps, generate framework files, inspect routes, and check Beignet conventions.
|
|
602
702
|
|
|
603
|
-
create
|
|
703
|
+
Run npm create beignet@latest (or bun create beignet) to scaffold a new app.`,
|
|
604
704
|
},
|
|
605
705
|
routes: {
|
|
706
|
+
completion: completionRoutes,
|
|
606
707
|
create: createCommand,
|
|
607
708
|
db: dbRoutes,
|
|
608
709
|
doctor: doctorCommand,
|
|
609
710
|
lint: lintCommand,
|
|
610
711
|
make: makeRoutes,
|
|
712
|
+
outbox: outboxRoutes,
|
|
611
713
|
routes: routesCommand,
|
|
714
|
+
schedule: scheduleRoutes,
|
|
715
|
+
task: taskRoutes,
|
|
612
716
|
},
|
|
613
717
|
});
|
|
614
718
|
const cli = buildApplication(rootRoutes, {
|
|
615
719
|
name: "beignet",
|
|
720
|
+
versionInfo: {
|
|
721
|
+
currentVersion: getCliVersion(),
|
|
722
|
+
},
|
|
616
723
|
scanner: {
|
|
617
724
|
caseStyle: "allow-kebab-for-camel",
|
|
618
725
|
},
|
|
@@ -626,7 +733,7 @@ const cli = buildApplication(rootRoutes, {
|
|
|
626
733
|
commandErrorResult: (error) => error.message,
|
|
627
734
|
},
|
|
628
735
|
},
|
|
629
|
-
determineExitCode: () =>
|
|
736
|
+
determineExitCode: () => 2,
|
|
630
737
|
});
|
|
631
738
|
function uniqueValues(values) {
|
|
632
739
|
return [...new Set(values)];
|
|
@@ -637,45 +744,123 @@ function writeOutput(context, output) {
|
|
|
637
744
|
function formatCliException(error) {
|
|
638
745
|
return error instanceof Error ? error.message : String(error);
|
|
639
746
|
}
|
|
747
|
+
function appTaskRunNextSteps(result) {
|
|
748
|
+
const output = result.output === undefined
|
|
749
|
+
? "undefined"
|
|
750
|
+
: JSON.stringify(result.output, null, 2);
|
|
751
|
+
return `Ran task ${result.name} in ${result.durationMs}ms
|
|
752
|
+
|
|
753
|
+
Output:
|
|
754
|
+
${output}`;
|
|
755
|
+
}
|
|
756
|
+
function outboxDrainNextSteps(result) {
|
|
757
|
+
return `Drained outbox in ${result.durationMs}ms
|
|
758
|
+
|
|
759
|
+
Result:
|
|
760
|
+
claimed: ${result.result.claimed}
|
|
761
|
+
delivered: ${result.result.delivered}
|
|
762
|
+
retried: ${result.result.retried}
|
|
763
|
+
deadLettered: ${result.result.deadLettered}`;
|
|
764
|
+
}
|
|
765
|
+
function scheduleRunNextSteps(result) {
|
|
766
|
+
return `Ran schedule ${result.name} in ${result.durationMs}ms
|
|
767
|
+
|
|
768
|
+
Run:
|
|
769
|
+
source: ${result.run.source}
|
|
770
|
+
id: ${result.run.id ?? "none"}
|
|
771
|
+
attempt: ${result.run.attempt ?? "none"}
|
|
772
|
+
scheduledAt: ${result.run.scheduledAt ?? "none"}
|
|
773
|
+
triggeredAt: ${result.run.triggeredAt ?? "none"}`;
|
|
774
|
+
}
|
|
640
775
|
function nextSteps(result, options = {}) {
|
|
776
|
+
if (result.dryRun) {
|
|
777
|
+
const plannedFiles = result.files.map((file) => ` ${file}`).join("\n");
|
|
778
|
+
return `Would create ${result.name} at ${result.targetDir}
|
|
779
|
+
|
|
780
|
+
Planned files:
|
|
781
|
+
${plannedFiles}`;
|
|
782
|
+
}
|
|
641
783
|
const pm = result.packageManager;
|
|
642
784
|
const run = pm === "npm" ? "npm run" : `${pm} run`;
|
|
643
|
-
const
|
|
644
|
-
? " cp .env.example .env.local\n"
|
|
645
|
-
: "";
|
|
785
|
+
const cli = packageRunnerFromPackageManager(pm);
|
|
646
786
|
const envRequiredIntegrations = options.integrations?.filter(isEnvRequiredProviderIntegration) ?? [];
|
|
647
787
|
const envStep = envRequiredIntegrations.length > 0
|
|
648
788
|
? " Fill .env.local for selected provider integrations before starting the app.\n"
|
|
649
789
|
: "";
|
|
790
|
+
const openStep = options.api
|
|
791
|
+
? " open http://localhost:3000 for the API landing page"
|
|
792
|
+
: " open http://localhost:3000/sign-up";
|
|
650
793
|
return `Created ${result.name} at ${result.targetDir}
|
|
651
794
|
|
|
652
795
|
Next steps:
|
|
653
796
|
cd ${result.targetDir}
|
|
654
797
|
${pm} install
|
|
655
|
-
|
|
656
|
-
|
|
798
|
+
cp .env.example .env.local
|
|
799
|
+
${envStep}
|
|
800
|
+
Prepare the database:
|
|
801
|
+
${cli} db migrate
|
|
802
|
+
|
|
803
|
+
Start the app:
|
|
804
|
+
${run} dev
|
|
805
|
+
${openStep}
|
|
806
|
+
|
|
807
|
+
Inspect the app:
|
|
808
|
+
${cli} routes
|
|
809
|
+
${cli} lint
|
|
810
|
+
${cli} doctor
|
|
811
|
+
|
|
812
|
+
Generate a feature:
|
|
813
|
+
${cli} make feature projects
|
|
814
|
+
${cli} db generate
|
|
815
|
+
${cli} db migrate
|
|
816
|
+
${run} test
|
|
817
|
+
${run} typecheck
|
|
818
|
+
${cli} lint
|
|
819
|
+
${cli} doctor`;
|
|
820
|
+
}
|
|
821
|
+
function packageRunnerFromPackageManager(pm) {
|
|
822
|
+
switch (pm) {
|
|
823
|
+
case "npm":
|
|
824
|
+
return "npm run beignet --";
|
|
825
|
+
case "pnpm":
|
|
826
|
+
return "pnpm beignet";
|
|
827
|
+
case "yarn":
|
|
828
|
+
return "yarn beignet";
|
|
829
|
+
default:
|
|
830
|
+
return "bun beignet";
|
|
831
|
+
}
|
|
657
832
|
}
|
|
658
833
|
function isEnvRequiredProviderIntegration(integration) {
|
|
659
834
|
return (integration === "inngest" ||
|
|
660
835
|
integration === "resend" ||
|
|
661
836
|
integration === "upstash-rate-limit");
|
|
662
837
|
}
|
|
663
|
-
function
|
|
664
|
-
|
|
665
|
-
.
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
.map((file) => ` ${file}`)
|
|
669
|
-
|
|
670
|
-
|
|
838
|
+
function changedFileLines(result) {
|
|
839
|
+
return {
|
|
840
|
+
changedFiles: [...result.createdFiles, ...result.updatedFiles]
|
|
841
|
+
.map((file) => ` ${file}`)
|
|
842
|
+
.join("\n"),
|
|
843
|
+
skippedFiles: result.skippedFiles.map((file) => ` ${file}`).join("\n"),
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
function makeNextSteps(result, noun, steps) {
|
|
847
|
+
const { changedFiles, skippedFiles } = changedFileLines(result);
|
|
848
|
+
return `${result.dryRun ? "Would create" : "Created"} ${result.name} ${noun} in ${result.targetDir}
|
|
671
849
|
|
|
672
850
|
Changed files:
|
|
673
851
|
${changedFiles || " none"}
|
|
674
852
|
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
675
853
|
|
|
676
854
|
Next steps:
|
|
677
|
-
|
|
678
|
-
|
|
855
|
+
${steps.map((step) => ` ${step}`).join("\n")}`;
|
|
856
|
+
}
|
|
857
|
+
function makeResourceNextSteps(result) {
|
|
858
|
+
return makeNextSteps(result, "resource", [
|
|
859
|
+
"Use make resource when the feature maps cleanly to a REST resource with repository-backed persistence.",
|
|
860
|
+
"Review the generated schemas and repository fields.",
|
|
861
|
+
"If this app uses Drizzle, run beignet db generate and beignet db migrate so the new schema is ready.",
|
|
862
|
+
"Run your app's test and typecheck commands, then beignet lint and beignet doctor.",
|
|
863
|
+
]);
|
|
679
864
|
}
|
|
680
865
|
function databaseCommandNextSteps(result) {
|
|
681
866
|
const command = [result.runner, ...result.args].join(" ");
|
|
@@ -686,272 +871,149 @@ Command:
|
|
|
686
871
|
${command}`;
|
|
687
872
|
}
|
|
688
873
|
function makeFeatureNextSteps(result) {
|
|
689
|
-
|
|
690
|
-
.
|
|
691
|
-
.
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} feature slice in ${result.targetDir}
|
|
696
|
-
|
|
697
|
-
Changed files:
|
|
698
|
-
${changedFiles || " none"}
|
|
699
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
700
|
-
|
|
701
|
-
Next steps:
|
|
702
|
-
Treat the generated name field as a placeholder and shape the contracts, use cases, and repository around the feature's real workflow.
|
|
703
|
-
Run your app's typecheck, test, beignet lint, and beignet doctor commands.`;
|
|
874
|
+
return makeNextSteps(result, "feature slice", [
|
|
875
|
+
"Use make feature for product capabilities and workflows. Use make resource when the concept is mostly CRUD-shaped.",
|
|
876
|
+
"Treat the generated name field as a placeholder and shape the contracts, use cases, and repository around the feature's real workflow.",
|
|
877
|
+
"If this app uses Drizzle, run beignet db generate and beignet db migrate so the new schema is ready.",
|
|
878
|
+
"Run your app's test and typecheck commands, then beignet lint and beignet doctor.",
|
|
879
|
+
]);
|
|
704
880
|
}
|
|
705
881
|
function makeContractNextSteps(result) {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
.
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
return
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
717
|
-
|
|
718
|
-
Next steps:
|
|
719
|
-
Add route handlers for the generated contracts.
|
|
720
|
-
Register the exported contract list with OpenAPI if this app publishes docs.`;
|
|
882
|
+
return makeNextSteps(result, "contract", [
|
|
883
|
+
"Add route handlers for the generated contracts.",
|
|
884
|
+
"Register the exported contract list with OpenAPI if this app publishes docs.",
|
|
885
|
+
]);
|
|
886
|
+
}
|
|
887
|
+
function makeTaskNextSteps(result) {
|
|
888
|
+
return makeNextSteps(result, "task", [
|
|
889
|
+
"Replace the starter input schema and handler with the operational workflow.",
|
|
890
|
+
"Keep business rules in use cases and call them from the task handler.",
|
|
891
|
+
]);
|
|
721
892
|
}
|
|
722
893
|
function makeUseCaseNextSteps(result) {
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
.
|
|
726
|
-
|
|
727
|
-
.map((file) => ` ${file}`)
|
|
728
|
-
.join("\n");
|
|
729
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} use case in ${result.targetDir}
|
|
730
|
-
|
|
731
|
-
Changed files:
|
|
732
|
-
${changedFiles || " none"}
|
|
733
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
734
|
-
|
|
735
|
-
Next steps:
|
|
736
|
-
Replace the starter input and output schemas with domain-specific shapes.
|
|
737
|
-
Add a focused use case test before wiring it to an HTTP route.`;
|
|
894
|
+
return makeNextSteps(result, "use case", [
|
|
895
|
+
"Replace the starter input and output schemas with domain-specific shapes.",
|
|
896
|
+
"Add a focused use case test before wiring it to an HTTP route.",
|
|
897
|
+
]);
|
|
738
898
|
}
|
|
739
899
|
function makeTestNextSteps(result) {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
.
|
|
743
|
-
|
|
744
|
-
.map((file) => ` ${file}`)
|
|
745
|
-
.join("\n");
|
|
746
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} test in ${result.targetDir}
|
|
747
|
-
|
|
748
|
-
Changed files:
|
|
749
|
-
${changedFiles || " none"}
|
|
750
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
751
|
-
|
|
752
|
-
Next steps:
|
|
753
|
-
Replace the starter input, context, and assertion with behavior-specific coverage.
|
|
754
|
-
Run your app's test command.`;
|
|
900
|
+
return makeNextSteps(result, "test", [
|
|
901
|
+
"Replace the starter input, context, and assertion with behavior-specific coverage.",
|
|
902
|
+
"Run your app's test command.",
|
|
903
|
+
]);
|
|
755
904
|
}
|
|
756
905
|
function makePortNextSteps(result) {
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
.
|
|
760
|
-
|
|
761
|
-
.map((file) => ` ${file}`)
|
|
762
|
-
.join("\n");
|
|
763
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} port in ${result.targetDir}
|
|
764
|
-
|
|
765
|
-
Changed files:
|
|
766
|
-
${changedFiles || " none"}
|
|
767
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
768
|
-
|
|
769
|
-
Next steps:
|
|
770
|
-
Replace the starter execute method with domain-specific operations.
|
|
771
|
-
Replace the generated infrastructure stub with a real port adapter.`;
|
|
906
|
+
return makeNextSteps(result, "port", [
|
|
907
|
+
"Replace the starter execute method with domain-specific operations.",
|
|
908
|
+
"Replace the generated infrastructure stub with a real port adapter.",
|
|
909
|
+
]);
|
|
772
910
|
}
|
|
773
911
|
function makePolicyNextSteps(result) {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
.
|
|
777
|
-
|
|
778
|
-
.map((file) => ` ${file}`)
|
|
779
|
-
.join("\n");
|
|
780
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} policy in ${result.targetDir}
|
|
781
|
-
|
|
782
|
-
Changed files:
|
|
783
|
-
${changedFiles || " none"}
|
|
784
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
785
|
-
|
|
786
|
-
Next steps:
|
|
787
|
-
Replace the starter abilities with domain-specific authorization rules.
|
|
788
|
-
Register the policy with createGate(...) and bind it in request context.`;
|
|
912
|
+
return makeNextSteps(result, "policy", [
|
|
913
|
+
"Replace the starter abilities with domain-specific authorization rules.",
|
|
914
|
+
"Register the policy with createGate(...) and bind it in request context.",
|
|
915
|
+
]);
|
|
789
916
|
}
|
|
790
917
|
function makeAdapterNextSteps(result) {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
.
|
|
794
|
-
|
|
795
|
-
.map((file) => ` ${file}`)
|
|
796
|
-
.join("\n");
|
|
797
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} port adapter in ${result.targetDir}
|
|
798
|
-
|
|
799
|
-
Changed files:
|
|
800
|
-
${changedFiles || " none"}
|
|
801
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
802
|
-
|
|
803
|
-
Next steps:
|
|
804
|
-
Replace the generated throwing implementation with real infrastructure code.
|
|
805
|
-
Keep the adapter behind the port interface so use cases stay infrastructure-agnostic.`;
|
|
918
|
+
return makeNextSteps(result, "port adapter", [
|
|
919
|
+
"Replace the generated throwing implementation with real infrastructure code.",
|
|
920
|
+
"Keep the adapter behind the port interface so use cases stay infrastructure-agnostic.",
|
|
921
|
+
]);
|
|
806
922
|
}
|
|
807
923
|
function makeEventNextSteps(result) {
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
.
|
|
811
|
-
|
|
812
|
-
.map((file) => ` ${file}`)
|
|
813
|
-
.join("\n");
|
|
814
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} event in ${result.targetDir}
|
|
815
|
-
|
|
816
|
-
Changed files:
|
|
817
|
-
${changedFiles || " none"}
|
|
818
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
819
|
-
|
|
820
|
-
Next steps:
|
|
821
|
-
Replace the starter payload schema with the domain fact shape.
|
|
822
|
-
Emit the event from a use case with .emits(...) and events.record(...).`;
|
|
924
|
+
return makeNextSteps(result, "event", [
|
|
925
|
+
"Replace the starter payload schema with the domain fact shape.",
|
|
926
|
+
"Emit the event from a use case with .emits(...) and events.record(...).",
|
|
927
|
+
]);
|
|
823
928
|
}
|
|
824
929
|
function makeJobNextSteps(result) {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
.
|
|
828
|
-
|
|
829
|
-
.map((file) => ` ${file}`)
|
|
830
|
-
.join("\n");
|
|
831
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} job in ${result.targetDir}
|
|
832
|
-
|
|
833
|
-
Changed files:
|
|
834
|
-
${changedFiles || " none"}
|
|
835
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
836
|
-
|
|
837
|
-
Next steps:
|
|
838
|
-
Replace the starter payload and handler with the real background work.
|
|
839
|
-
Dispatch the job through ctx.ports.jobs from a use case, listener, or schedule.`;
|
|
930
|
+
return makeNextSteps(result, "job", [
|
|
931
|
+
"Replace the starter payload and handler with the real background work.",
|
|
932
|
+
"Dispatch the job through ctx.ports.jobs from a use case, listener, or schedule.",
|
|
933
|
+
]);
|
|
840
934
|
}
|
|
841
935
|
function makeFactoryNextSteps(result) {
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
.
|
|
845
|
-
|
|
846
|
-
.map((file) => ` ${file}`)
|
|
847
|
-
.join("\n");
|
|
848
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} factory in ${result.targetDir}
|
|
849
|
-
|
|
850
|
-
Changed files:
|
|
851
|
-
${changedFiles || " none"}
|
|
852
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
853
|
-
|
|
854
|
-
Next steps:
|
|
855
|
-
Replace the starter defaults with realistic test data for this feature.
|
|
856
|
-
Persist through app-owned repository ports so tests avoid raw database clients.`;
|
|
936
|
+
return makeNextSteps(result, "factory", [
|
|
937
|
+
"Replace the starter defaults with realistic test data for this feature.",
|
|
938
|
+
"Persist through app-owned repository ports so tests avoid raw database clients.",
|
|
939
|
+
]);
|
|
857
940
|
}
|
|
858
941
|
function makeSeedNextSteps(result) {
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
.
|
|
862
|
-
|
|
863
|
-
.map((file) => ` ${file}`)
|
|
864
|
-
.join("\n");
|
|
865
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} seed in ${result.targetDir}
|
|
866
|
-
|
|
867
|
-
Changed files:
|
|
868
|
-
${changedFiles || " none"}
|
|
869
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
870
|
-
|
|
871
|
-
Next steps:
|
|
872
|
-
Replace the starter record with idempotent local or demo data.
|
|
873
|
-
Import the feature seed from your app-owned database seed entrypoint and run it with runSeeds(...).`;
|
|
942
|
+
return makeNextSteps(result, "seed", [
|
|
943
|
+
"Replace the starter record with idempotent local or demo data.",
|
|
944
|
+
"Import the feature seed from your app-owned database seed entrypoint and run it with runSeeds(...).",
|
|
945
|
+
]);
|
|
874
946
|
}
|
|
875
947
|
function makeNotificationNextSteps(result) {
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
.
|
|
879
|
-
|
|
880
|
-
.map((file) => ` ${file}`)
|
|
881
|
-
.join("\n");
|
|
882
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} notification in ${result.targetDir}
|
|
883
|
-
|
|
884
|
-
Changed files:
|
|
885
|
-
${changedFiles || " none"}
|
|
886
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
887
|
-
|
|
888
|
-
Next steps:
|
|
889
|
-
Replace the starter payload and channels with the real user-facing message.
|
|
890
|
-
Send the notification through ctx.ports.notifications so delivery can run inline in tests and through jobs in production.`;
|
|
948
|
+
return makeNextSteps(result, "notification", [
|
|
949
|
+
"Replace the starter payload and channels with the real user-facing message.",
|
|
950
|
+
"Send the notification through ctx.ports.notifications so delivery can run inline in tests and through jobs in production.",
|
|
951
|
+
]);
|
|
891
952
|
}
|
|
892
953
|
function makeListenerNextSteps(result) {
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
.
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
return
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
Register the listener collection from infrastructure startup.`;
|
|
908
|
-
}
|
|
909
|
-
function makeScheduleNextSteps(result) {
|
|
910
|
-
const changedFiles = [...result.createdFiles, ...result.updatedFiles]
|
|
911
|
-
.map((file) => ` ${file}`)
|
|
912
|
-
.join("\n");
|
|
913
|
-
const skippedFiles = result.skippedFiles
|
|
914
|
-
.map((file) => ` ${file}`)
|
|
915
|
-
.join("\n");
|
|
916
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} schedule in ${result.targetDir}
|
|
917
|
-
|
|
918
|
-
Changed files:
|
|
919
|
-
${changedFiles || " none"}
|
|
920
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
921
|
-
|
|
922
|
-
Next steps:
|
|
923
|
-
Replace the starter payload and handler with the scheduled workflow.
|
|
924
|
-
Trigger it from a cron route, worker, or provider adapter.`;
|
|
954
|
+
return makeNextSteps(result, "listener", [
|
|
955
|
+
"Replace the starter handler with the event reaction.",
|
|
956
|
+
"Register the listener collection from infrastructure startup.",
|
|
957
|
+
]);
|
|
958
|
+
}
|
|
959
|
+
function makeScheduleNextSteps(result, options = {}) {
|
|
960
|
+
return makeNextSteps(result, "schedule", [
|
|
961
|
+
"Replace the starter payload and handler with the scheduled workflow.",
|
|
962
|
+
...(options.route
|
|
963
|
+
? [
|
|
964
|
+
"Set CRON_SECRET in your deployment and configure a scheduled request with Authorization: Bearer $CRON_SECRET.",
|
|
965
|
+
]
|
|
966
|
+
: ["Trigger it from a cron route, worker, or provider adapter."]),
|
|
967
|
+
]);
|
|
925
968
|
}
|
|
926
969
|
function makeUploadNextSteps(result) {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
.
|
|
930
|
-
|
|
931
|
-
.map((file) => ` ${file}`)
|
|
932
|
-
.join("\n");
|
|
933
|
-
return `${result.dryRun ? "Would create" : "Created"} ${result.name} upload in ${result.targetDir}
|
|
934
|
-
|
|
935
|
-
Changed files:
|
|
936
|
-
${changedFiles || " none"}
|
|
937
|
-
${skippedFiles ? `\nSkipped identical files:\n${skippedFiles}` : ""}
|
|
938
|
-
|
|
939
|
-
Next steps:
|
|
940
|
-
Replace the starter metadata, constraints, key, authorization, and onComplete behavior.
|
|
941
|
-
Register the upload in createUploadRouter(...) and expose it with createUploadRoute(...).`;
|
|
970
|
+
return makeNextSteps(result, "upload", [
|
|
971
|
+
"Replace the starter metadata, constraints, key, authorization, and onComplete behavior.",
|
|
972
|
+
"Register the upload in createUploadRouter(...) and expose it with createUploadRoute(...).",
|
|
973
|
+
]);
|
|
942
974
|
}
|
|
943
975
|
export async function main(inputs = process.argv.slice(2), context = { process }) {
|
|
944
976
|
if (inputs.length === 0) {
|
|
945
977
|
await run(cli, ["--help"], context);
|
|
946
|
-
context.process.exitCode =
|
|
978
|
+
context.process.exitCode = 2;
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
// Shell completion scripts call `beignet completion propose <words...>`.
|
|
982
|
+
// Handle it before stricli parses the inputs: the trailing words are a
|
|
983
|
+
// partial command line, not flags for this CLI.
|
|
984
|
+
if (inputs[0] === "completion" && inputs[1] === "propose") {
|
|
985
|
+
await writeCompletionProposals(inputs.slice(2), context);
|
|
947
986
|
return;
|
|
948
987
|
}
|
|
949
988
|
await run(cli, inputs, context);
|
|
950
989
|
normalizeExitCode(context.process);
|
|
951
990
|
}
|
|
991
|
+
async function writeCompletionProposals(inputs, context) {
|
|
992
|
+
try {
|
|
993
|
+
const proposals = await proposeCompletions(cli, inputs.length === 0 ? [""] : inputs, context);
|
|
994
|
+
for (const proposal of proposals) {
|
|
995
|
+
context.process.stdout.write(`${proposal.completion}\n`);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
catch {
|
|
999
|
+
// Completion failures must never break the shell; propose nothing.
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
// Stricli reports usage and internal failures as negative ExitCode values
|
|
1003
|
+
// (-5..-1). Node keeps the negative value on process.exitCode while Bun wraps
|
|
1004
|
+
// it to 251..255 on assignment, so both forms map to the usage exit code.
|
|
1005
|
+
const stricliErrorExitCodes = new Set([
|
|
1006
|
+
-5, -4, -3, -2, -1, 251, 252, 253, 254, 255,
|
|
1007
|
+
]);
|
|
952
1008
|
function normalizeExitCode(proc) {
|
|
953
|
-
if (typeof proc.exitCode
|
|
954
|
-
|
|
1009
|
+
if (typeof proc.exitCode !== "number") {
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
if (stricliErrorExitCodes.has(proc.exitCode)) {
|
|
1013
|
+
proc.exitCode = 2;
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
if (proc.exitCode > 127) {
|
|
955
1017
|
proc.exitCode = 1;
|
|
956
1018
|
}
|
|
957
1019
|
}
|
|
@@ -970,7 +1032,7 @@ function isCliEntrypoint() {
|
|
|
970
1032
|
if (isCliEntrypoint()) {
|
|
971
1033
|
main().catch((error) => {
|
|
972
1034
|
console.error(error instanceof Error ? error.message : String(error));
|
|
973
|
-
process.exitCode =
|
|
1035
|
+
process.exitCode = 2;
|
|
974
1036
|
});
|
|
975
1037
|
}
|
|
976
1038
|
//# sourceMappingURL=index.js.map
|