@asterflow/cli 0.1.0

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.
@@ -0,0 +1,644 @@
1
+ #!/usr/bin/env node
2
+ // packages/cli/src/index.ts
3
+ import Ve from "yargs";
4
+ import { hideBin as ze } from "yargs/helpers";
5
+ // packages/cli/src/commands/add.ts
6
+ import { exec as me } from "child_process";
7
+ import { existsSync as ue } from "fs";
8
+ import { readFile as ge, writeFile as fe } from "fs/promises";
9
+ import { join as ye, resolve as be } from "path";
10
+ import { promisify as he } from "util";
11
+ // packages/cli/src/registry/plugins.ts
12
+ var y = {
13
+ fs: {
14
+ id: "fs",
15
+ packageName: "@asterflow/fs",
16
+ version: "^1.0.7",
17
+ description: "File system-based routing - routes are discovered from a routes/ directory.",
18
+ importName: "fsRoutingPlugin",
19
+ useSnippet: ".use(fsRoutingPlugin, { routes })",
20
+ usageNote: "Run `asterflow generate` (or `asterflow generate --watch`) after adding/removing files under src/routes/ - it writes src/routes.gen.ts, which the app entry imports as `routes`."
21
+ },
22
+ multipart: {
23
+ id: "multipart",
24
+ packageName: "@asterflow/multipart",
25
+ version: "^1.0.0",
26
+ description: "multipart/form-data parsing (file uploads), with per-route type-safe field criteria.",
27
+ importName: "multipartPlugin",
28
+ useSnippet: ".use(multipartPlugin, { limits: { fileSize: 10 * 1024 * 1024 } })",
29
+ usageNote: "Build upload routes with Method.create(Method.POST).multipart({...}).handler(...) - see src/routes/upload.ts for a working example."
30
+ }
31
+ }, qe = Object.keys(y);
32
+ // packages/cli/src/utils/log.ts
33
+ import P from "chalk";
34
+ var d = {
35
+ info: (e) => console.log(P.cyan(e)),
36
+ success: (e) => console.log(P.green(e)),
37
+ warn: (e) => console.log(P.yellow(e)),
38
+ error: (e) => console.error(P.red(e)),
39
+ step: (e) => console.log(P.gray(` ${e}`))
40
+ };
41
+ // packages/cli/src/utils/packageManager.ts
42
+ import { existsSync as pe } from "fs";
43
+ import { join as ce } from "path";
44
+ var de = {
45
+ "bun.lock": "bun",
46
+ "bun.lockb": "bun",
47
+ "pnpm-lock.yaml": "pnpm",
48
+ "yarn.lock": "yarn",
49
+ "package-lock.json": "npm"
50
+ };
51
+ function C(e) {
52
+ for (let [t, r] of Object.entries(de))
53
+ if (pe(ce(e, t))) return r;
54
+ return "bun";
55
+ }
56
+ function U(e, t) {
57
+ return e ?? C(t);
58
+ }
59
+ function B(e) {
60
+ return e === "yarn" ? "yarn" : `${e} install`;
61
+ }
62
+ function W(e, t) {
63
+ let r = t.join(" ");
64
+ switch (e) {
65
+ case "bun":
66
+ return `bun add ${r}`;
67
+ case "pnpm":
68
+ return `pnpm add ${r}`;
69
+ case "yarn":
70
+ return `yarn add ${r}`;
71
+ case "npm":
72
+ return `npm install ${r}`;
73
+ }
74
+ }
75
+ // packages/cli/src/commands/add.ts
76
+ var we = he(me);
77
+ async function Y({ names: e, directory: t }) {
78
+ let r = be(t ?? "."), s = ye(r, "package.json"), l = e.split(",").map((a) => a.trim()).filter(Boolean);
79
+ if (l.length === 0) {
80
+ d.error("No plugin names given. Usage: asterflow add fs,multipart"), process.exitCode = 1;
81
+ return;
82
+ }
83
+ let b = l.filter((a) => !y[a]);
84
+ if (b.length > 0) {
85
+ d.error(`Unknown plugin${b.length > 1 ? "s" : ""}: ${b.join(", ")}`), d.step(`Available plugins: ${Object.keys(y).join(", ")}`), process.exitCode = 1;
86
+ return;
87
+ }
88
+ if (!ue(s)) {
89
+ d.error(`No package.json found at ${s}. Run this from an AsterFlow project (or \`asterflow init\` first).`), process.exitCode = 1;
90
+ return;
91
+ }
92
+ let m = l.map((a) => y[a]), p = await ge(s, "utf-8"), c = JSON.parse(p);
93
+ c.dependencies ??= {};
94
+ let u = m.filter((a) => c.dependencies[a.packageName]), f = m.filter((a) => !c.dependencies[a.packageName]);
95
+ for (let a of f) c.dependencies[a.packageName] = a.version;
96
+ if (c.dependencies = Object.fromEntries(Object.entries(c.dependencies).sort(([a], [h]) => a.localeCompare(h))), f.length > 0) {
97
+ await fe(s, `${JSON.stringify(c, null, 2)}
98
+ `), d.success(`Added to package.json: ${f.map((w) => w.packageName).join(", ")}`);
99
+ let a = C(r), h = W(a, f.map((w) => w.packageName));
100
+ d.info(`Installing (${h})...`);
101
+ try {
102
+ await we(h, { cwd: r }), d.success("Dependencies installed.");
103
+ } catch (w) {
104
+ d.error(`Install failed - run manually: ${h}`), d.step(w instanceof Error ? w.message : String(w));
105
+ }
106
+ }
107
+ u.length > 0 && d.warn(`Already present, skipped: ${u.map((a) => a.packageName).join(", ")}`), d.info("Wire it up:");
108
+ for (let a of m)
109
+ d.step(`import { ${a.importName} } from '${a.packageName}'`), d.step(`app${a.useSnippet}`), d.step(a.usageNote);
110
+ }
111
+ // packages/cli/src/commands/generate.ts
112
+ import { generateRouteManifest as ke } from "@asterflow/fs";
113
+ import { watch as xe } from "fs";
114
+ import { join as ve, resolve as V } from "path";
115
+ var Pe = "src/routes", z = "src/routes.gen.ts";
116
+ async function J({ directory: e, out: t, watch: r }) {
117
+ let s = V(e ?? Pe), l = V(t ?? z), b = async () => {
118
+ try {
119
+ await ke({ routesDir: s, outFile: l }), d.success(`Generated ${ve(t ?? z)}`);
120
+ } catch (p) {
121
+ d.error(p instanceof Error ? p.message : String(p)), r || (process.exitCode = 1);
122
+ }
123
+ };
124
+ if (await b(), !r) return;
125
+ d.info(`Watching ${s} for changes...`);
126
+ let m;
127
+ xe(s, { recursive: !0 }, () => {
128
+ clearTimeout(m), m = setTimeout(b, 100);
129
+ });
130
+ }
131
+ // packages/cli/src/commands/init.ts
132
+ import * as i from "@clack/prompts";
133
+ import ie from "ci-info";
134
+ import { exec as _e } from "child_process";
135
+ import { appendFile as Ge, mkdir as A, writeFile as M } from "fs/promises";
136
+ import { basename as _, dirname as oe, join as x, resolve as Le } from "path";
137
+ import { promisify as Ue } from "util";
138
+ // packages/cli/src/generators/appEntry.ts
139
+ function q({ adapter: e, plugins: t, stub: r }) {
140
+ let s = t.some((p) => p.id === "fs"), l = [
141
+ "import { AsterFlow } from 'asterflow'",
142
+ "import { adapters } from '@asterflow/adapter'",
143
+ ...e.imports,
144
+ ...t.map((p) => `import { ${p.importName} } from '${p.packageName}'`),
145
+ ...s ? ["import routes from './routes.gen'"] : []
146
+ ], b = [
147
+ ...t.map((p) => p.useSnippet.replace(/^\./, "")),
148
+ ...!s && r ? [`method('get', {
149
+ path: '/',
150
+ handler({ response }) {
151
+ return response.success({ message: 'Hello from AsterFlow!' })
152
+ }
153
+ })`] : [],
154
+ `listen${e.listenCall}`
155
+ ], m = e.preamble ? `${e.preamble}
156
+ ` : "";
157
+ return `${l.join(`
158
+ `)}
159
+ ${m}export default new AsterFlow({ driver: ${e.driverExpression} })
160
+ .${b.join(`
161
+ .`)}
162
+ `;
163
+ }
164
+ // packages/cli/src/generators/packageJson.ts
165
+ function K({ name: e, adapter: t, plugins: r }) {
166
+ let s = r.some((u) => u.id === "fs"), l = {
167
+ asterflow: "^0.0.5",
168
+ "@asterflow/adapter": "^1.0.0",
169
+ zod: "^3.25.67"
170
+ };
171
+ t.dependency && (l[t.dependency] = "latest");
172
+ for (let u of r) l[u.packageName] = u.version;
173
+ let b = Object.fromEntries(Object.entries(l).sort(([u], [f]) => u.localeCompare(f))), m = s ? {
174
+ predev: "asterflow generate",
175
+ dev: "bun run src/index.ts",
176
+ prebuild: "asterflow generate"
177
+ } : { dev: "bun run src/index.ts" }, p = {
178
+ typescript: "^5.8.3",
179
+ "@types/bun": "latest"
180
+ };
181
+ return s && (p["@asterflow/cli"] = "^0.1.0"), `${JSON.stringify({
182
+ name: e,
183
+ version: "0.1.0",
184
+ type: "module",
185
+ private: !0,
186
+ scripts: m,
187
+ dependencies: b,
188
+ devDependencies: p
189
+ }, null, 2)}
190
+ `;
191
+ }
192
+ // packages/cli/src/helpers/copyTemplate.ts
193
+ import { readFile as $e, writeFile as je } from "fs/promises";
194
+ import { join as H } from "path";
195
+ var Ie = H(__dirname, "..", "..", "templates");
196
+ async function D(e) {
197
+ return $e(H(Ie, e), "utf-8");
198
+ }
199
+ async function O(e, t) {
200
+ let r = await D(e);
201
+ await je(t, r);
202
+ }
203
+ // packages/cli/src/registry/templates.ts
204
+ var Q = {
205
+ hello: {
206
+ id: "hello",
207
+ path: "/",
208
+ source: "routes/index.ts",
209
+ summary: "Minimal GET route at the root path."
210
+ },
211
+ upload: {
212
+ id: "upload",
213
+ path: "/upload",
214
+ source: "routes/upload.ts",
215
+ plugin: "multipart",
216
+ summary: "File upload handled by @asterflow/multipart."
217
+ }
218
+ }, $ = Object.keys(Q);
219
+ function S(e) {
220
+ let t = new Set(e);
221
+ return $.map((r) => Q[r]).filter((r) => !r.plugin || t.has(r.plugin));
222
+ }
223
+ function X(e) {
224
+ let t = e.path.replace(/^\/+|\/+$/g, "");
225
+ return t.length === 0 ? "index.ts" : `${t}/index.ts`;
226
+ }
227
+ // packages/cli/src/generators/routes.ts
228
+ async function Z({ pluginIds: e, templateIds: t }) {
229
+ if (!e.includes("fs")) return [];
230
+ let r = S(e).filter((s) => t.includes(s.id));
231
+ return Promise.all(r.map(async (s) => ({
232
+ fileName: X(s),
233
+ content: await D(s.source)
234
+ })));
235
+ }
236
+ // packages/cli/src/helpers/git.ts
237
+ import { execSync as v } from "child_process";
238
+ import { rmSync as Ee } from "fs";
239
+ import { join as Se } from "path";
240
+ function Ae(e) {
241
+ try {
242
+ return v("git rev-parse --is-inside-work-tree", { cwd: e, stdio: "ignore" }), !0;
243
+ } catch {
244
+ return !1;
245
+ }
246
+ }
247
+ function Re(e) {
248
+ try {
249
+ return v("git config init.defaultBranch", { cwd: e, stdio: "ignore" }), !0;
250
+ } catch {
251
+ return !1;
252
+ }
253
+ }
254
+ function ee(e) {
255
+ let t = !1;
256
+ try {
257
+ return v("git --version", { stdio: "ignore" }), Ae(e) ? !1 : (v("git init", { cwd: e, stdio: "ignore" }), t = !0, Re(e) || v("git checkout -b main", { cwd: e, stdio: "ignore" }), v("git add -A", { cwd: e, stdio: "ignore" }), v('git commit -m "Initial commit from create-asterflow-app"', { cwd: e, stdio: "ignore" }), !0);
258
+ } catch {
259
+ if (t)
260
+ try {
261
+ Ee(Se(e, ".git"), { recursive: !0, force: !0 });
262
+ } catch {
263
+ }
264
+ return !1;
265
+ }
266
+ }
267
+ // packages/cli/src/helpers/isFolderEmpty.ts
268
+ import { lstatSync as Ne, readdirSync as Te } from "fs";
269
+ import { join as Ce } from "path";
270
+ import te from "chalk";
271
+ var De = new Set([
272
+ ".DS_Store",
273
+ ".git",
274
+ ".gitattributes",
275
+ ".gitignore",
276
+ ".gitlab-ci.yml",
277
+ ".hg",
278
+ ".hgcheck",
279
+ ".hgignore",
280
+ ".idea",
281
+ ".npmignore",
282
+ ".vscode",
283
+ "LICENSE",
284
+ "Thumbs.db"
285
+ ]);
286
+ function re(e, t) {
287
+ let r = Te(e).filter((s) => !De.has(s) && !s.endsWith(".iml"));
288
+ if (r.length === 0) return !0;
289
+ console.log(`The directory ${te.green(t)} contains files that could conflict:`), console.log();
290
+ for (let s of r) {
291
+ let l = Ne(Ce(e, s)).isDirectory();
292
+ console.log(` ${te.cyan(s)}${l ? "/" : ""}`);
293
+ }
294
+ return console.log(), console.log("Either try using a new directory name, or remove the files listed above."), console.log(), !1;
295
+ }
296
+ // packages/cli/src/helpers/isWriteable.ts
297
+ import { constants as Oe } from "fs";
298
+ import { access as Fe } from "fs/promises";
299
+ async function ne(e) {
300
+ try {
301
+ return await Fe(e, Oe.W_OK), !0;
302
+ } catch {
303
+ return !1;
304
+ }
305
+ }
306
+ // packages/cli/src/helpers/validateProjectName.ts
307
+ import Me from "validate-npm-package-name";
308
+ function F(e) {
309
+ let t = Me(e);
310
+ return t.validForNewPackages ? { valid: !0 } : {
311
+ valid: !1,
312
+ problems: [...t.errors ?? [], ...t.warnings ?? []]
313
+ };
314
+ }
315
+ // packages/cli/src/registry/adapters.ts
316
+ var j = {
317
+ bun: {
318
+ id: "bun",
319
+ label: "Bun (native Bun.serve)",
320
+ dependency: null,
321
+ imports: [],
322
+ driverExpression: "adapters.bun",
323
+ preamble: null,
324
+ listenCall: `({ port: 3333 }, (err) => {
325
+ if (err) {
326
+ console.error(err)
327
+ process.exit(1)
328
+ }
329
+ console.log('Server listening on http:
330
+ })`
331
+ },
332
+ node: {
333
+ id: "node",
334
+ label: "Node.js (native http)",
335
+ dependency: null,
336
+ imports: [],
337
+ driverExpression: "adapters.node",
338
+ preamble: null,
339
+ listenCall: `({ port: 3333 }, (err) => {
340
+ if (err) {
341
+ console.error(err)
342
+ process.exit(1)
343
+ }
344
+ console.log('Server listening on http:
345
+ })`
346
+ },
347
+ express: {
348
+ id: "express",
349
+ label: "Express",
350
+ dependency: "express",
351
+ imports: ["import express from 'express'"],
352
+ driverExpression: "adapters.express",
353
+ preamble: "const server = express()",
354
+ listenCall: `(server, 3333, () => {
355
+ console.log('Server listening on http:
356
+ })`
357
+ },
358
+ fastify: {
359
+ id: "fastify",
360
+ label: "Fastify",
361
+ dependency: "fastify",
362
+ imports: ["import fastify from 'fastify'"],
363
+ driverExpression: "adapters.fastify",
364
+ preamble: "const server = fastify()",
365
+ listenCall: `(server, { port: 3333 }, (err) => {
366
+ if (err) {
367
+ console.error(err)
368
+ process.exit(1)
369
+ }
370
+ console.log('Server listening on http:
371
+ })`
372
+ }
373
+ }, I = Object.keys(j);
374
+ // packages/cli/src/commands/init.ts
375
+ var Be = Ue(_e), k = {
376
+ adapter: "bun",
377
+ plugins: ["fs", "multipart"],
378
+ install: !0,
379
+ git: !0
380
+ };
381
+ async function se(e) {
382
+ i.intro("Create a new AsterFlow project");
383
+ let t = Le(e.directory ?? "."), r = _(t) === "." ? "my-asterflow-app" : _(t);
384
+ if (!await ne(oe(t))) {
385
+ i.cancel(`The path ${t} is not writable. Check folder permissions and try again.`), process.exitCode = 1;
386
+ return;
387
+ }
388
+ if (await A(t, { recursive: !0 }), !re(t, _(t))) {
389
+ process.exitCode = 1;
390
+ return;
391
+ }
392
+ let s = process.argv.some((n) => n.startsWith("--")), l = ie.isCI || !!e.yes || s, b = s && !e.yes && !ie.isCI;
393
+ if (e.name) {
394
+ let n = F(e.name);
395
+ if (!n.valid) {
396
+ i.cancel(`Invalid project name "${e.name}":
397
+ ${n.problems.map((o) => ` - ${o}`).join(`
398
+ `)}`), process.exitCode = 1;
399
+ return;
400
+ }
401
+ }
402
+ let m = e.name;
403
+ if (m === void 0)
404
+ if (l)
405
+ m = r;
406
+ else {
407
+ let n = await i.text({
408
+ message: "Project name",
409
+ placeholder: r,
410
+ defaultValue: r,
411
+ validate: (o) => {
412
+ let g = F(o || r);
413
+ return g.valid ? void 0 : `Invalid project name: ${g.problems[0]}`;
414
+ }
415
+ });
416
+ if (i.isCancel(n)) return i.cancel("Cancelled.");
417
+ m = n;
418
+ }
419
+ if (e.adapter && !j[e.adapter]) {
420
+ i.cancel(`Unknown adapter "${e.adapter}". Available: ${I.join(", ")}`), process.exitCode = 1;
421
+ return;
422
+ }
423
+ let p = e.adapter;
424
+ if (!p)
425
+ if (l)
426
+ p = k.adapter;
427
+ else {
428
+ let n = await i.select({
429
+ message: "Which adapter do you want to use?",
430
+ options: Object.values(j).map((o) => ({ value: o.id, label: o.label })),
431
+ initialValue: k.adapter
432
+ });
433
+ if (i.isCancel(n)) return i.cancel("Cancelled.");
434
+ p = n;
435
+ }
436
+ let c;
437
+ if (e.plugins !== void 0) {
438
+ c = e.plugins.split(",").map((o) => o.trim()).filter(Boolean);
439
+ let n = c.filter((o) => !y[o]);
440
+ if (n.length > 0) {
441
+ i.cancel(`Unknown plugin${n.length > 1 ? "s" : ""}: ${n.join(", ")}. Available: ${Object.keys(y).join(", ")}`), process.exitCode = 1;
442
+ return;
443
+ }
444
+ } else if (l)
445
+ c = k.plugins;
446
+ else {
447
+ let n = await i.multiselect({
448
+ message: "Which plugins do you want to install? (space to select, enter to confirm)",
449
+ options: Object.values(y).map((o) => ({ value: o.id, label: o.packageName, hint: o.description })),
450
+ initialValues: k.plugins,
451
+ required: !1
452
+ });
453
+ if (i.isCancel(n)) return i.cancel("Cancelled.");
454
+ c = n;
455
+ }
456
+ c.length === 0 && (c = k.plugins);
457
+ let u = S(c), f;
458
+ if (e.routes !== void 0) {
459
+ let n = e.routes.split(",").map((g) => g.trim()).filter(Boolean);
460
+ f = n.includes("none") ? [] : n;
461
+ let o = f.filter((g) => !u.some((le) => le.id === g));
462
+ if (o.length > 0) {
463
+ i.cancel(`Unknown route template${o.length > 1 ? "s" : ""}: ${o.join(", ")}. Available: ${u.map((g) => g.id).join(", ")}`), process.exitCode = 1;
464
+ return;
465
+ }
466
+ } else if (l)
467
+ f = u.map((n) => n.id);
468
+ else {
469
+ let n = await i.multiselect({
470
+ message: "Which example routes do you want to scaffold? (space to select, enter to confirm)",
471
+ options: u.map((o) => ({ value: o.id, label: o.path, hint: o.summary })),
472
+ initialValues: u.map((o) => o.id),
473
+ required: !1
474
+ });
475
+ if (i.isCancel(n)) return i.cancel("Cancelled.");
476
+ f = n;
477
+ }
478
+ let a = e.install;
479
+ if (a === void 0)
480
+ if (l)
481
+ a = k.install;
482
+ else {
483
+ let n = await i.confirm({ message: "Install dependencies now?", initialValue: k.install });
484
+ if (i.isCancel(n)) return i.cancel("Cancelled.");
485
+ a = n;
486
+ }
487
+ let h = e.git;
488
+ if (h === void 0)
489
+ if (l)
490
+ h = k.git;
491
+ else {
492
+ let n = await i.confirm({ message: "Initialize a git repository?", initialValue: k.git });
493
+ if (i.isCancel(n)) return i.cancel("Cancelled.");
494
+ h = n;
495
+ }
496
+ b && We({
497
+ adapter: e.adapter === void 0,
498
+ plugins: e.plugins === void 0,
499
+ routes: e.routes === void 0,
500
+ install: e.install === void 0,
501
+ git: e.git === void 0
502
+ }, { adapterId: p, pluginIds: c, templateIds: f, install: a, git: h });
503
+ let w = j[p], E = c.map((n) => y[n]), G = i.spinner();
504
+ G.start("Scaffolding project");
505
+ let N = x(t, "src");
506
+ await A(N, { recursive: !0 }), await M(x(t, "package.json"), K({ name: m, adapter: w, plugins: E })), await O("tsconfig.json", x(t, "tsconfig.json")), await M(x(N, "index.ts"), q({ adapter: w, plugins: E, stub: f.length > 0 })), await O("gitignore", x(t, ".gitignore")), E.some((n) => n.id === "fs") && await Ge(x(t, ".gitignore"), `src/routes.gen.ts
507
+ `);
508
+ let L = await Z({ pluginIds: c, templateIds: f });
509
+ if (L.length > 0) {
510
+ let n = x(N, "routes");
511
+ await A(n, { recursive: !0 });
512
+ for (let o of L) {
513
+ let g = x(n, o.fileName);
514
+ await A(oe(g), { recursive: !0 }), await M(g, o.content);
515
+ }
516
+ }
517
+ G.stop("Project scaffolded");
518
+ let T = U(e.packageManager, t);
519
+ if (a) {
520
+ let n = B(T), o = i.spinner();
521
+ o.start(`Installing dependencies (${n})`);
522
+ try {
523
+ await Be(n, { cwd: t }), o.stop("Dependencies installed");
524
+ } catch (g) {
525
+ o.stop("Failed to install dependencies"), i.log.error(g instanceof Error ? g.message : String(g));
526
+ }
527
+ }
528
+ h && ee(t) && i.log.success("Initialized a git repository.");
529
+ for (let n of E)
530
+ i.log.info(`${n.packageName}: ${n.usageNote}`);
531
+ Ye({ name: m, targetDir: t, packageManager: T, useYarn: T === "yarn" });
532
+ }
533
+ function We(e, t) {
534
+ let r = [];
535
+ e.adapter && r.push(` --adapter <id> ${t.adapterId} (available: ${I.join(", ")})`), e.plugins && r.push(` --plugins <ids> ${t.pluginIds.length > 0 ? t.pluginIds.join(",") : "none"} (available: ${Object.keys(y).join(", ")})`), e.routes && r.push(` --routes <ids> ${t.templateIds.length > 0 ? t.templateIds.join(",") : "none"} (available: ${$.join(", ")})`), e.install && r.push(` --no-install skip installing dependencies (default: ${t.install ? "installs" : "skips"})`), e.git && r.push(` --no-git skip git init (default: ${t.git ? "inits" : "skips"})`), r.length !== 0 && i.note(r.join(`
536
+ `), "Using defaults for unprovided options");
537
+ }
538
+ function Ye({ name: e, targetDir: t, packageManager: r, useYarn: s }) {
539
+ let l = `${r} ${s ? "" : "run "}dev`;
540
+ i.note(`cd ${t}
541
+ ${l}`, "Next steps"), i.outro(`Success! Created ${e} at ${t}`);
542
+ }
543
+ // packages/cli/src/commands/list.ts
544
+ import R from "chalk";
545
+ function ae() {
546
+ console.log(R.bold(`
547
+ Available AsterFlow plugins:
548
+ `));
549
+ for (let e of Object.values(y))
550
+ console.log(` ${R.cyan(e.id.padEnd(12))} ${R.gray(e.packageName)}`), console.log(` ${" ".repeat(12)} ${e.description}
551
+ `);
552
+ console.log(R.gray(`Install with: asterflow add <name>[,<name>...]
553
+ `));
554
+ }
555
+ // packages/cli/src/index.ts
556
+ (async () => {
557
+ await Ve(ze(process.argv)).scriptName("asterflow").command(
558
+ "init [directory]",
559
+ "Scaffold a new AsterFlow project",
560
+ (e) => e.positional("directory", {
561
+ type: "string",
562
+ describe: "Directory to create the project in (defaults to the current directory)"
563
+ }).option("name", {
564
+ type: "string",
565
+ describe: "Project name (defaults to the directory name)"
566
+ }).option("adapter", {
567
+ type: "string",
568
+ describe: `Adapter to use (${I.join(", ")})`
569
+ }).option("plugins", {
570
+ type: "string",
571
+ describe: "Comma-separated plugin ids, e.g. fs,multipart"
572
+ }).option("routes", {
573
+ type: "string",
574
+ describe: `Comma-separated route template ids to scaffold, or "none" (${$.join(", ")})`
575
+ }).option("install", {
576
+ type: "boolean",
577
+ describe: "Install dependencies (default: yes)"
578
+ }).option("skip-install", {
579
+ type: "boolean",
580
+ describe: "Alias for --no-install"
581
+ }).option("git", {
582
+ type: "boolean",
583
+ describe: "Initialize a git repository (default: yes)"
584
+ }).option("use-npm", { type: "boolean", describe: "Install dependencies with npm" }).option("use-pnpm", { type: "boolean", describe: "Install dependencies with pnpm" }).option("use-yarn", { type: "boolean", describe: "Install dependencies with yarn" }).option("use-bun", { type: "boolean", describe: "Install dependencies with bun" }).option("yes", {
585
+ type: "boolean",
586
+ describe: "Accept defaults for anything not explicitly set, without prompting"
587
+ }),
588
+ async (e) => {
589
+ let t = e.useNpm ? "npm" : e.usePnpm ? "pnpm" : e.useYarn ? "yarn" : e.useBun ? "bun" : void 0;
590
+ await se({
591
+ directory: e.directory,
592
+ name: e.name,
593
+ adapter: e.adapter,
594
+ plugins: e.plugins,
595
+ routes: e.routes,
596
+ install: e.skipInstall ? !1 : e.install,
597
+ git: e.git,
598
+ packageManager: t,
599
+ yes: e.yes
600
+ });
601
+ }
602
+ ).command(
603
+ "add <names>",
604
+ "Add one or more plugins to the current project",
605
+ (e) => e.positional("names", {
606
+ type: "string",
607
+ describe: "Comma-separated plugin names, e.g. fs,multipart",
608
+ demandOption: !0
609
+ }).option("directory", {
610
+ alias: "C",
611
+ type: "string",
612
+ describe: "Project directory (defaults to the current directory)"
613
+ }),
614
+ async (e) => {
615
+ await Y({ names: e.names, directory: e.directory });
616
+ }
617
+ ).command(
618
+ "list",
619
+ "List available plugins",
620
+ () => {
621
+ },
622
+ () => ae()
623
+ ).command(
624
+ "generate [directory]",
625
+ "Generate the static route manifest consumed by @asterflow/fs (fs-routing)",
626
+ (e) => e.positional("directory", {
627
+ type: "string",
628
+ describe: "Routes directory to scan (defaults to src/routes)"
629
+ }).option("out", {
630
+ alias: "o",
631
+ type: "string",
632
+ describe: "Output file for the generated manifest (defaults to src/routes.gen.ts)"
633
+ }).option("watch", {
634
+ alias: "w",
635
+ type: "boolean",
636
+ describe: "Regenerate whenever a file under the routes directory changes"
637
+ }),
638
+ async (e) => {
639
+ await J({ directory: e.directory, out: e.out, watch: e.watch });
640
+ }
641
+ ).demandCommand(1, "Specify a command: init, add, list or generate").strict().help().alias("help", "h").parse();
642
+ })().catch((e) => {
643
+ console.error(e instanceof Error ? e.message : e), process.exitCode = 1;
644
+ });
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,5 @@
1
+ export interface AddOptions {
2
+ names: string;
3
+ directory?: string;
4
+ }
5
+ export declare function runAdd({ names, directory }: AddOptions): Promise<void>;
@@ -0,0 +1,6 @@
1
+ export interface GenerateOptions {
2
+ directory?: string;
3
+ out?: string;
4
+ watch?: boolean;
5
+ }
6
+ export declare function runGenerate({ directory, out, watch: watchMode }: GenerateOptions): Promise<void>;
@@ -0,0 +1,16 @@
1
+ import type { PackageManager } from '../utils/packageManager';
2
+ export interface InitOptions {
3
+ directory?: string;
4
+ name?: string;
5
+ adapter?: string;
6
+ /** Comma-separated plugin ids, e.g. "fs,multipart" - same style as `asterflow add`. */
7
+ plugins?: string;
8
+ /** Comma-separated route template ids, e.g. "hello,upload", or "none" - same style as `plugins`. */
9
+ routes?: string;
10
+ install?: boolean;
11
+ git?: boolean;
12
+ packageManager?: PackageManager;
13
+ /** Accept defaults for anything not explicitly set, without prompting - even with zero other flags. */
14
+ yes?: boolean;
15
+ }
16
+ export declare function runInit(options: InitOptions): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function runList(): void;
@@ -0,0 +1,8 @@
1
+ import type { AdapterDefinition } from '../registry/adapters';
2
+ import type { PluginDefinition } from '../registry/plugins';
3
+ export interface AppEntryOptions {
4
+ adapter: AdapterDefinition;
5
+ plugins: PluginDefinition[];
6
+ stub: boolean;
7
+ }
8
+ export declare function generateEntry({ adapter, plugins, stub }: AppEntryOptions): string;