@stacksjs/buddy 0.70.112 → 0.70.114
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/dist/cli.js +23 -2
- package/dist/commands/add.js +1 -1
- package/dist/commands/build.js +58 -11
- package/dist/commands/clean.js +9 -2
- package/dist/commands/cloud.js +27 -11
- package/dist/commands/commit.js +1 -1
- package/dist/commands/create.js +20 -10
- package/dist/commands/deploy.js +31 -6
- package/dist/commands/dev.js +38 -31
- package/dist/commands/doctor.d.ts +9 -0
- package/dist/commands/doctor.js +162 -19
- package/dist/commands/extension.js +38 -2
- package/dist/commands/fresh.js +9 -2
- package/dist/commands/make.js +48 -31
- package/dist/commands/migrate.js +2 -1
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +56 -10
- package/dist/commands/types.js +1 -1
- package/dist/lazy-commands.js +15 -5
- package/package.json +45 -44
package/dist/commands/doctor.js
CHANGED
|
@@ -2,22 +2,28 @@ import process from "node:process";
|
|
|
2
2
|
import { bold, dim, green, intro, log, onUnknownSubcommand, red, yellow } from "@stacksjs/cli";
|
|
3
3
|
import { feature } from "@stacksjs/config";
|
|
4
4
|
import { storage } from "@stacksjs/storage";
|
|
5
|
+
import { isSupportedBunVersion, isVersionGreaterThanOrEqual, minimumBunVersion } from "@stacksjs/utils";
|
|
5
6
|
import { FEATURE_NAMES, featurePathsPresent } from "./features";
|
|
7
|
+
const minimumSqliteVersion = "3.47.2";
|
|
8
|
+
|
|
9
|
+
class ProbeWarning extends Error {
|
|
10
|
+
}
|
|
6
11
|
async function probe(checks, name, fn, timeoutMs = 2000) {
|
|
7
|
-
const start = Date.now();
|
|
12
|
+
const start = Date.now(), ac = new AbortController, timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
8
13
|
try {
|
|
9
|
-
const
|
|
14
|
+
const message = await Promise.race([
|
|
10
15
|
fn(),
|
|
11
16
|
new Promise((_, rej) => ac.signal.addEventListener("abort", () => rej(Error(`timed out (>${Math.round(timeoutMs / 1000)}s)`))))
|
|
12
17
|
]);
|
|
13
|
-
clearTimeout(timer);
|
|
14
18
|
checks.push({ name, status: "pass", message: `${message} (${Date.now() - start}ms)` });
|
|
15
19
|
} catch (err) {
|
|
16
20
|
checks.push({
|
|
17
21
|
name,
|
|
18
|
-
status: "fail",
|
|
22
|
+
status: err instanceof ProbeWarning ? "warn" : "fail",
|
|
19
23
|
message: err instanceof Error ? err.message : String(err)
|
|
20
24
|
});
|
|
25
|
+
} finally {
|
|
26
|
+
clearTimeout(timer);
|
|
21
27
|
}
|
|
22
28
|
}
|
|
23
29
|
export function doctor(buddy) {
|
|
@@ -25,19 +31,18 @@ export function doctor(buddy) {
|
|
|
25
31
|
log.debug("Running `buddy doctor` ...");
|
|
26
32
|
await intro("buddy doctor");
|
|
27
33
|
const checks = [], bunVersion = process.versions.bun;
|
|
28
|
-
if (bunVersion)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
});
|
|
34
|
+
if (bunVersion && isSupportedBunVersion(bunVersion))
|
|
35
|
+
checks.push({
|
|
36
|
+
name: "Bun Runtime",
|
|
37
|
+
status: "pass",
|
|
38
|
+
message: `v${bunVersion}`
|
|
39
|
+
});
|
|
40
|
+
else if (bunVersion)
|
|
41
|
+
checks.push({
|
|
42
|
+
name: "Bun Runtime",
|
|
43
|
+
status: "fail",
|
|
44
|
+
message: `v${bunVersion} (requires v${minimumBunVersion} or later, run: bun upgrade)`
|
|
45
|
+
});
|
|
41
46
|
else
|
|
42
47
|
checks.push({
|
|
43
48
|
name: "Bun Runtime",
|
|
@@ -100,9 +105,34 @@ export function doctor(buddy) {
|
|
|
100
105
|
status: "fail",
|
|
101
106
|
message: "Not set \u2014 features that depend on it (encrypted columns, signed URLs, env decryption) will refuse to run. Run `buddy key:generate`."
|
|
102
107
|
});
|
|
108
|
+
await probe(checks, "SQLite Engine", async () => {
|
|
109
|
+
const { Database } = await import("bun:sqlite"), memory = new Database(":memory:");
|
|
110
|
+
let version;
|
|
111
|
+
try {
|
|
112
|
+
const row = memory.query("SELECT sqlite_version() AS version").get();
|
|
113
|
+
if (typeof row?.version === "string")
|
|
114
|
+
version = row.version;
|
|
115
|
+
} finally {
|
|
116
|
+
memory.close();
|
|
117
|
+
}
|
|
118
|
+
if (!version)
|
|
119
|
+
throw new ProbeWarning("could not read sqlite_version() (skipped)");
|
|
120
|
+
if (!isVersionGreaterThanOrEqual(version, minimumSqliteVersion))
|
|
121
|
+
throw Error(`v${version} is below the required v${minimumSqliteVersion} (system.sqlite in package.json); upgrade Bun for a newer bundled SQLite`);
|
|
122
|
+
return `v${version}`;
|
|
123
|
+
});
|
|
103
124
|
await probe(checks, "Database", async () => {
|
|
104
|
-
const { db } = await import("@stacksjs/database");
|
|
105
|
-
|
|
125
|
+
const { db } = await import("@stacksjs/database"), unsafe = db.unsafe;
|
|
126
|
+
if (typeof unsafe !== "function")
|
|
127
|
+
throw new ProbeWarning("driver exposes no raw query method (skipped)");
|
|
128
|
+
const statement = unsafe("SELECT 1");
|
|
129
|
+
if (!statement || typeof statement.then !== "function" && typeof statement.execute !== "function")
|
|
130
|
+
throw new ProbeWarning("driver returned a non-executable statement (skipped)");
|
|
131
|
+
const result = typeof statement.execute === "function" ? await statement.execute() : await statement;
|
|
132
|
+
if (result === void 0 || result === null)
|
|
133
|
+
throw Error("probe query returned no result");
|
|
134
|
+
if (Array.isArray(result) && result.length === 0)
|
|
135
|
+
throw Error("probe query returned no rows");
|
|
106
136
|
return "Reachable";
|
|
107
137
|
});
|
|
108
138
|
await probe(checks, "Database FKs", async () => {
|
|
@@ -218,6 +248,119 @@ export function doctor(buddy) {
|
|
|
218
248
|
message: `Could not audit storage config: ${err instanceof Error ? err.message : String(err)}`
|
|
219
249
|
});
|
|
220
250
|
}
|
|
251
|
+
try {
|
|
252
|
+
if (process.platform !== "darwin")
|
|
253
|
+
checks.push({ name: "Dev domains (rpx)", status: "pass", message: "Not macOS (skipped)" });
|
|
254
|
+
else {
|
|
255
|
+
const fs = await import("node:fs"), os = await import("node:os"), path = await import("node:path"), isAlive = (pid) => {
|
|
256
|
+
try {
|
|
257
|
+
process.kill(pid, 0);
|
|
258
|
+
return !0;
|
|
259
|
+
} catch (err) {
|
|
260
|
+
return err.code === "EPERM";
|
|
261
|
+
}
|
|
262
|
+
}, registryDir = path.join(os.homedir(), ".stacks", "rpx", "registry.d"), registered = new Set, deadRegistryFiles = [];
|
|
263
|
+
if (fs.existsSync(registryDir))
|
|
264
|
+
for (const file of fs.readdirSync(registryDir)) {
|
|
265
|
+
if (!file.endsWith(".json"))
|
|
266
|
+
continue;
|
|
267
|
+
try {
|
|
268
|
+
const entry = JSON.parse(fs.readFileSync(path.join(registryDir, file), "utf8"));
|
|
269
|
+
if (entry.to && (entry.pid === void 0 || isAlive(entry.pid)))
|
|
270
|
+
registered.add(entry.to.toLowerCase());
|
|
271
|
+
if (typeof entry.pid === "number" && !isAlive(entry.pid))
|
|
272
|
+
deadRegistryFiles.push(file);
|
|
273
|
+
} catch {}
|
|
274
|
+
}
|
|
275
|
+
const staleHosts = new Set, staleResolvers = [], hostsPath = "/etc/hosts", hostsLines = fs.existsSync(hostsPath) ? fs.readFileSync(hostsPath, "utf8").split(`
|
|
276
|
+
`) : [];
|
|
277
|
+
for (let i = 0;i < hostsLines.length; i++) {
|
|
278
|
+
const line = hostsLines[i];
|
|
279
|
+
if (line.trim() === "# Added by rpx") {
|
|
280
|
+
for (let j = i + 1;j < hostsLines.length; j++) {
|
|
281
|
+
const blockLine = hostsLines[j].trim();
|
|
282
|
+
if (blockLine === "" || blockLine.startsWith("#"))
|
|
283
|
+
break;
|
|
284
|
+
const names = blockLine.split("#")[0]?.trim().split(/\s+/).slice(1) ?? [];
|
|
285
|
+
for (const name of names)
|
|
286
|
+
if (!registered.has(name.toLowerCase()))
|
|
287
|
+
staleHosts.add(name);
|
|
288
|
+
}
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
const hash = line.indexOf("#");
|
|
292
|
+
if (hash === -1)
|
|
293
|
+
continue;
|
|
294
|
+
const marker = /^rpx(?::pid=(\d+))?$/.exec(line.slice(hash + 1).trim());
|
|
295
|
+
if (!marker)
|
|
296
|
+
continue;
|
|
297
|
+
const names = line.slice(0, hash).trim().split(/\s+/).slice(1), pid = marker[1] ? Number.parseInt(marker[1], 10) : null;
|
|
298
|
+
if (pid !== null ? !isAlive(pid) : names.every((n) => !registered.has(n.toLowerCase())))
|
|
299
|
+
for (const name of names)
|
|
300
|
+
staleHosts.add(name);
|
|
301
|
+
}
|
|
302
|
+
const resolverDir = "/etc/resolver";
|
|
303
|
+
if (fs.existsSync(resolverDir))
|
|
304
|
+
for (const file of fs.readdirSync(resolverDir))
|
|
305
|
+
try {
|
|
306
|
+
const content = fs.readFileSync(path.join(resolverDir, file), "utf8");
|
|
307
|
+
if (!content.includes("127.0.0.1") || !content.includes("15353"))
|
|
308
|
+
continue;
|
|
309
|
+
const domain = file.toLowerCase();
|
|
310
|
+
if (![...registered].some((host) => host === domain || host.endsWith(`.${domain}`)))
|
|
311
|
+
staleResolvers.push(file);
|
|
312
|
+
} catch {}
|
|
313
|
+
if (staleHosts.size > 0 || staleResolvers.length > 0 || deadRegistryFiles.length > 0) {
|
|
314
|
+
const parts = [];
|
|
315
|
+
if (staleHosts.size > 0)
|
|
316
|
+
parts.push(`hosts(${[...staleHosts].join(", ")})`);
|
|
317
|
+
if (staleResolvers.length > 0)
|
|
318
|
+
parts.push(`resolver(${staleResolvers.join(", ")})`);
|
|
319
|
+
if (deadRegistryFiles.length > 0)
|
|
320
|
+
parts.push(`registry(${deadRegistryFiles.join(", ")})`);
|
|
321
|
+
checks.push({
|
|
322
|
+
name: "Dev domains (rpx)",
|
|
323
|
+
status: "warn",
|
|
324
|
+
message: `Stale loopback overrides from dead dev sessions: ${parts.join(" ")}. These keep pointing the domain at 127.0.0.1. Remove with: sudo nano /etc/hosts; sudo rm /etc/resolver/<name>; rm ~/.stacks/rpx/registry.d/<file>. Updating @stacksjs/rpx lets the daemon sweep pid-stamped entries automatically.`
|
|
325
|
+
});
|
|
326
|
+
} else
|
|
327
|
+
checks.push({ name: "Dev domains (rpx)", status: "pass", message: "No stale dev-domain overrides" });
|
|
328
|
+
}
|
|
329
|
+
} catch (err) {
|
|
330
|
+
checks.push({
|
|
331
|
+
name: "Dev domains (rpx)",
|
|
332
|
+
status: "warn",
|
|
333
|
+
message: `Could not audit dev-domain overrides: ${err instanceof Error ? err.message : String(err)}`
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
await probe(checks, "Dev ports", async () => {
|
|
337
|
+
const net = await import("node:net"), { config } = await import("@stacksjs/config"), configured = config.ports ?? {}, targets = [
|
|
338
|
+
{ name: "frontend", key: "frontend", envVar: "PORT", fallback: 3000 },
|
|
339
|
+
{ name: "api", key: "api", envVar: "PORT_API", fallback: 3008 },
|
|
340
|
+
{ name: "docs", key: "docs", envVar: "PORT_DOCS", fallback: 3006 },
|
|
341
|
+
{ name: "dashboard", key: "admin", envVar: "PORT_ADMIN", fallback: 3002 }
|
|
342
|
+
].map((t) => ({ ...t, port: Number(configured[t.key]) || t.fallback })), canConnect = (port, host) => new Promise((resolve) => {
|
|
343
|
+
const socket = net.createConnection({ port, host });
|
|
344
|
+
socket.setTimeout(400);
|
|
345
|
+
const done = (occupied) => {
|
|
346
|
+
socket.destroy();
|
|
347
|
+
resolve(occupied);
|
|
348
|
+
};
|
|
349
|
+
socket.once("connect", () => done(!0));
|
|
350
|
+
socket.once("timeout", () => done(!1));
|
|
351
|
+
socket.once("error", () => done(!1));
|
|
352
|
+
}), occupied = new Set;
|
|
353
|
+
await Promise.all([...new Set(targets.map((t) => t.port))].map(async (port) => {
|
|
354
|
+
if (await canConnect(port, "127.0.0.1") || await canConnect(port, "::1"))
|
|
355
|
+
occupied.add(port);
|
|
356
|
+
}));
|
|
357
|
+
const busy = targets.filter((t) => occupied.has(t.port));
|
|
358
|
+
if (busy.length > 0) {
|
|
359
|
+
const list = busy.map((t) => `${t.name} :${t.port} (${t.envVar})`).join(", ");
|
|
360
|
+
throw new ProbeWarning(`in use: ${list}. buddy dev will fail to bind; stop the process holding the port or set the override env var`);
|
|
361
|
+
}
|
|
362
|
+
return `All free: ${targets.map((t) => `${t.name} :${t.port}`).join(", ")}`;
|
|
363
|
+
});
|
|
221
364
|
try {
|
|
222
365
|
const orphans = [];
|
|
223
366
|
for (const name of FEATURE_NAMES) {
|
|
@@ -10,7 +10,7 @@ export function extension(buddy) {
|
|
|
10
10
|
const pkg = await Bun.file(`${process.cwd()}/package.json`).json().catch(() => ({}));
|
|
11
11
|
return { config, version: pkg.version ?? "0.0.0" };
|
|
12
12
|
};
|
|
13
|
-
buddy.command("extension:build", "Build the browser extension (Chrome + Firefox) from config/extension.ts").option("--target <target>", "Build a single target (chrome | firefox); omit to build all").option("--version <version>", "Override the extension version (defaults to package.json)").action(async (options) => {
|
|
13
|
+
buddy.command("extension:build", "Build the browser extension (Chrome + Firefox + Safari) from config/extension.ts").option("--target <target>", "Build a single target (chrome | firefox | safari); omit to build all").option("--version <version>", "Override the extension version (defaults to package.json)").action(async (options) => {
|
|
14
14
|
const { buildExtension, buildAllTargets } = await import("@stacksjs/browser-extension"), { config, version } = await load(), v = options.version ?? version;
|
|
15
15
|
if (options.target) {
|
|
16
16
|
const { outdir } = await buildExtension(config, { target: options.target, version: v });
|
|
@@ -68,11 +68,47 @@ console.log('[${name}] popup')
|
|
|
68
68
|
`);
|
|
69
69
|
log.info("Next: add icons under public/icons, then `buddy extension:build`.");
|
|
70
70
|
});
|
|
71
|
-
buddy.command("extension:package", "Build + zip the browser extension into store-ready archives").option("--target <target>", "Package a single target (chrome | firefox); omit to package all").option("--version <version>", "Override the extension version (defaults to package.json)").action(async (options) => {
|
|
71
|
+
buddy.command("extension:package", "Build + zip the browser extension into store-ready archives").option("--target <target>", "Package a single target (chrome | firefox | safari); omit to package all").option("--version <version>", "Override the extension version (defaults to package.json)").action(async (options) => {
|
|
72
72
|
const { packageExtension } = await import("@stacksjs/browser-extension"), { config, version } = await load(), v = options.version ?? version, targets = options.target ? [options.target] : config.targets ?? ["chrome", "firefox"];
|
|
73
73
|
for (const target of targets) {
|
|
74
74
|
const out = await packageExtension(config, { target, version: v });
|
|
75
75
|
log.success(`Packaged ${config.name} (${target}) \u2192 ${out}`);
|
|
76
76
|
}
|
|
77
77
|
});
|
|
78
|
+
buddy.command("extension:safari:init", "Scaffold the Safari container app (Xcode project) from the template").option("--bundle-id <id>", "Base bundle identifier (defaults to config safariBundleId)").option("--dir <dir>", "Output directory for the Xcode project (default safari)").option("--force", "Overwrite existing scaffold files").option("--team-id <id>", "Apple Developer team used for signing").action(async (options) => {
|
|
79
|
+
const { scaffoldSafariApp } = await import("@stacksjs/browser-extension"), { config } = await load(), { dir, written, skipped } = await scaffoldSafariApp(config, {
|
|
80
|
+
bundleId: options.bundleId,
|
|
81
|
+
dir: options.dir,
|
|
82
|
+
force: Boolean(options.force),
|
|
83
|
+
teamId: options.teamId
|
|
84
|
+
});
|
|
85
|
+
log.success(`Scaffolded the Safari container app \u2192 ${dir} (${written.length} files)`);
|
|
86
|
+
if (skipped.length)
|
|
87
|
+
log.info(`kept ${skipped.length} existing files (use --force to overwrite)`);
|
|
88
|
+
});
|
|
89
|
+
buddy.command("extension:safari:app", "Build the extension, sync it into the appex, and xcodebuild the container app").option("--release", "Build the Release configuration (default Debug)").option("--signed", "Allow code signing (needs an Apple Development identity)").option("--skip-xcodebuild", "Only build + sync the extension payload").option("--version <version>", "Override the extension version (defaults to package.json)").action(async (options) => {
|
|
90
|
+
const { buildSafariApp } = await import("@stacksjs/browser-extension"), { config, version } = await load(), { appPath, resources } = await buildSafariApp(config, {
|
|
91
|
+
version: options.version ?? version,
|
|
92
|
+
release: Boolean(options.release),
|
|
93
|
+
signed: Boolean(options.signed),
|
|
94
|
+
skipXcodebuild: Boolean(options.skipXcodebuild)
|
|
95
|
+
});
|
|
96
|
+
if (appPath) {
|
|
97
|
+
log.success(`Built ${appPath}`);
|
|
98
|
+
log.info("Open the app once, then enable the extension in Safari > Settings > Extensions.");
|
|
99
|
+
} else
|
|
100
|
+
log.success(`Extension payload synced \u2192 ${resources}`);
|
|
101
|
+
});
|
|
102
|
+
buddy.command("extension:safari:publish", "Archive and validate or upload the Safari app to App Store Connect").option("--version <version>", "Override the marketing version (defaults to package.json)").option("--build-number <number>", "CFBundleVersion (defaults to GITHUB_RUN_NUMBER or Unix time)").option("--team-id <id>", "Apple Developer team (defaults to config safariTeamId)").option("--api-key-id <id>", "App Store Connect API key ID").option("--api-issuer-id <id>", "App Store Connect API issuer ID").option("--api-key-path <path>", "Path to the App Store Connect AuthKey_*.p8 file").option("--validate-only", "Create and validate the archive without uploading it").action(async (options) => {
|
|
103
|
+
const { publishSafariApp } = await import("@stacksjs/browser-extension"), { config, version } = await load(), result = await publishSafariApp(config, {
|
|
104
|
+
version: options.version ?? version,
|
|
105
|
+
buildNumber: options.buildNumber,
|
|
106
|
+
teamId: options.teamId,
|
|
107
|
+
keyId: options.apiKeyId,
|
|
108
|
+
issuerId: options.apiIssuerId,
|
|
109
|
+
keyPath: options.apiKeyPath,
|
|
110
|
+
validateOnly: Boolean(options.validateOnly)
|
|
111
|
+
});
|
|
112
|
+
log.success(options.validateOnly ? `Validated Safari archive ${result.archivePath} (build ${result.buildNumber})` : `Uploaded Safari build ${result.buildNumber} to App Store Connect`);
|
|
113
|
+
});
|
|
78
114
|
}
|
package/dist/commands/fresh.js
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
import { runAction } from "@stacksjs/actions";
|
|
3
3
|
import { intro, log, onUnknownSubcommand, outro } from "@stacksjs/cli";
|
|
4
|
+
import { hasTTY, isCI } from "@stacksjs/env";
|
|
4
5
|
import { Action } from "@stacksjs/enums";
|
|
5
6
|
import { ExitCode } from "@stacksjs/types";
|
|
6
7
|
export function fresh(buddy) {
|
|
7
8
|
const descriptions = {
|
|
8
9
|
fresh: "Re-installs your npm dependencies",
|
|
9
10
|
project: "Target a specific project",
|
|
11
|
+
force: "Skip the confirmation prompt (required in CI/non-interactive shells)",
|
|
10
12
|
verbose: "Enable verbose output"
|
|
11
13
|
};
|
|
12
|
-
buddy.command("fresh", descriptions.fresh).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
|
|
14
|
+
buddy.command("fresh", descriptions.fresh).option("-p, --project [project]", descriptions.project, { default: !1 }).option("-f, --force", descriptions.force, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
|
|
13
15
|
log.debug("Running `buddy fresh` ...", options);
|
|
14
|
-
|
|
16
|
+
const skipConfirm = options.force === !0 || Boolean(buddy.isForce) || Boolean(buddy.isNoInteraction);
|
|
17
|
+
if (!skipConfirm && (isCI || !hasTTY || !process.stdin.isTTY)) {
|
|
18
|
+
log.syncError("Refusing to run `buddy fresh` from a non-interactive shell without confirmation.");
|
|
19
|
+
log.fatal(" \u27A1\uFE0F Re-run with `--force` to proceed: `buddy fresh --force`");
|
|
20
|
+
}
|
|
21
|
+
if (!skipConfirm) {
|
|
15
22
|
const { confirm } = await import("@stacksjs/cli");
|
|
16
23
|
if (!await confirm({
|
|
17
24
|
message: "This will remove and reinstall all dependencies. Continue?",
|
package/dist/commands/make.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
import {
|
|
3
|
+
createFactory,
|
|
3
4
|
createMiddleware,
|
|
4
5
|
createMigration,
|
|
5
6
|
createModel,
|
|
@@ -45,16 +46,17 @@ export function make(buddy) {
|
|
|
45
46
|
resource: "Create a new API resource",
|
|
46
47
|
name: "The name of the action",
|
|
47
48
|
queue: "Make queue migration",
|
|
49
|
+
queueTable: "Create the queue jobs table migration",
|
|
48
50
|
stack: "Create a new stack",
|
|
49
51
|
certificate: "Create a new SSL Certificate",
|
|
50
52
|
select: "What are you trying to make?",
|
|
51
53
|
project: "Target a specific project",
|
|
52
54
|
verbose: "Enable verbose output"
|
|
53
55
|
};
|
|
54
|
-
buddy.command("make [make]", "The make command").option("-a, --action [action]", descriptions.action, { default: !1 }).option("-c, --component [component]", descriptions.component, { default: !1 }).option("-d, --database [database]", descriptions.database, { default: !1 }).option("-f, --factory [factory]", descriptions.factory, { default: !1 }).option("-
|
|
56
|
+
buddy.command("make [make]", "The make command").option("-a, --action [action]", descriptions.action, { default: !1 }).option("-c, --component [component]", descriptions.component, { default: !1 }).option("-d, --database [database]", descriptions.database, { default: !1 }).option("-f, --factory [factory]", descriptions.factory, { default: !1 }).option("-fn, --function [function]", descriptions.function, { default: !1 }).option("-l, --language [language]", descriptions.language, { default: !1 }).option("-m, --model [model]", descriptions.model, { default: !1 }).option("-mw, --middleware [middleware]", descriptions.middleware, { default: !1 }).option("-p, --page [page]", descriptions.page, { default: !1 }).option("-mg, --migration [migration]", descriptions.migration, { default: !1 }).option("-n, --notification [notification]", descriptions.notification, { default: !1 }).option("-qt, --queue-table", descriptions.queue, { default: !1 }).option("-s, --stack [stack]", descriptions.stack, { default: !1 }).option("--dry-run", "Preview the files that would be generated without writing", { default: !1 }).option("--with-validation", "Include a validation rules block in the generated stub", { default: !1 }).option("--with-auth", "Include auth-aware boilerplate in the generated stub", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (make, options) => {
|
|
55
57
|
log.debug("Running `buddy make` ...", options);
|
|
56
58
|
if (!buddy.args[0]) {
|
|
57
|
-
|
|
59
|
+
console.error("You need to specify a name. Read more about the documentation here.");
|
|
58
60
|
process.exit(ExitCode.FatalError);
|
|
59
61
|
}
|
|
60
62
|
setDryRun(Boolean(options.dryRun || options["dry-run"]));
|
|
@@ -115,7 +117,14 @@ export function make(buddy) {
|
|
|
115
117
|
case "stack":
|
|
116
118
|
await makeStack(options);
|
|
117
119
|
break;
|
|
118
|
-
|
|
120
|
+
case "factory":
|
|
121
|
+
await createFactory(options);
|
|
122
|
+
break;
|
|
123
|
+
default: {
|
|
124
|
+
console.error(`Unknown make subcommand: ${make}`);
|
|
125
|
+
console.error("Valid subcommands: action, certificate, command, component, database, factory, function, job, language, mail, middleware, migration, model, notification, page, policy, queue-table, resource, stack");
|
|
126
|
+
process.exit(ExitCode.InvalidArgument);
|
|
127
|
+
}
|
|
119
128
|
}
|
|
120
129
|
}
|
|
121
130
|
await invoke(options);
|
|
@@ -127,7 +136,7 @@ export function make(buddy) {
|
|
|
127
136
|
name = name ?? options.name;
|
|
128
137
|
options.name = name;
|
|
129
138
|
if (!name) {
|
|
130
|
-
|
|
139
|
+
console.error("You need to specify a name. Read more about the documentation here.");
|
|
131
140
|
process.exit(ExitCode.FatalError);
|
|
132
141
|
}
|
|
133
142
|
setDryRun(Boolean(options.dryRun || options["dry-run"]));
|
|
@@ -140,7 +149,7 @@ export function make(buddy) {
|
|
|
140
149
|
buddy.command("scaffold:crud [name]", "Generate model, migration, and CRUD actions").alias("make:crud").alias("make:scaffold").option("-n, --name [name]", "Resource name (PascalCase)", { default: !1 }).option("-f, --fields [fields]", "Comma-separated field list, e.g. title:string,body:text,published:boolean", { default: "" }).option("--dry-run", "Preview the generated files without writing", { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).example("buddy scaffold:crud Post --fields=title:string,body:text,published:boolean").action(async (name, options) => {
|
|
141
150
|
name = name ?? options.name;
|
|
142
151
|
if (!name) {
|
|
143
|
-
|
|
152
|
+
console.error("scaffold:crud requires a resource name. Example: buddy scaffold:crud Post --fields=title:string,body:text");
|
|
144
153
|
process.exit(ExitCode.FatalError);
|
|
145
154
|
}
|
|
146
155
|
const { scaffoldCrud } = await import("@stacksjs/actions");
|
|
@@ -148,7 +157,7 @@ export function make(buddy) {
|
|
|
148
157
|
try {
|
|
149
158
|
await scaffoldCrud(name, options);
|
|
150
159
|
} catch (err) {
|
|
151
|
-
|
|
160
|
+
console.error("scaffold:crud failed:", err);
|
|
152
161
|
process.exit(ExitCode.FatalError);
|
|
153
162
|
}
|
|
154
163
|
});
|
|
@@ -158,8 +167,8 @@ export function make(buddy) {
|
|
|
158
167
|
name = name ?? options.name;
|
|
159
168
|
options.name = name;
|
|
160
169
|
if (!name) {
|
|
161
|
-
|
|
162
|
-
|
|
170
|
+
console.error("You need to specify a command name.");
|
|
171
|
+
console.error("Example: buddy make:command SendEmails");
|
|
163
172
|
process.exit(ExitCode.FatalError);
|
|
164
173
|
}
|
|
165
174
|
if (!await makeCommand(options)) {
|
|
@@ -180,7 +189,7 @@ export function make(buddy) {
|
|
|
180
189
|
name = name ?? options.name;
|
|
181
190
|
options.name = name;
|
|
182
191
|
if (!name) {
|
|
183
|
-
|
|
192
|
+
console.error("You need to specify a name. Read more about the documentation here.");
|
|
184
193
|
process.exit(ExitCode.FatalError);
|
|
185
194
|
}
|
|
186
195
|
await makeComponent(options);
|
|
@@ -190,25 +199,32 @@ export function make(buddy) {
|
|
|
190
199
|
name = name ?? options.name;
|
|
191
200
|
options.name = name;
|
|
192
201
|
if (!name) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
202
|
+
console.error("You need to specify a database name via the `--name` option, or as the command\u2019s argument.");
|
|
203
|
+
console.error("Example: `buddy make:database my-cool-database`");
|
|
204
|
+
console.error("Or: `buddy make:database --name=my-cool-database`");
|
|
205
|
+
console.error("Read more about the documentation here: https://stacksjs.com/docs/make/database");
|
|
197
206
|
process.exit(ExitCode.FatalError);
|
|
198
207
|
}
|
|
199
208
|
makeDatabase(options);
|
|
200
209
|
});
|
|
201
|
-
buddy.command("make:factory [name]", descriptions.factory).option("-n, --name [name]", descriptions.name, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action((name, options) => {
|
|
210
|
+
buddy.command("make:factory [name]", descriptions.factory).option("-n, --name [name]", descriptions.name, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (name, options) => {
|
|
202
211
|
log.debug("Running `buddy make:factory` ...", options);
|
|
203
212
|
name = name ?? options.name;
|
|
204
213
|
options.name = name;
|
|
205
214
|
if (!name) {
|
|
206
|
-
|
|
215
|
+
console.error("You need to specify a name. Read more about the documentation here.");
|
|
207
216
|
process.exit(ExitCode.FatalError);
|
|
208
217
|
}
|
|
218
|
+
await createFactory(options);
|
|
209
219
|
});
|
|
210
|
-
buddy.command("make:function [name]", descriptions.function).option("-n, --name [name]", descriptions.name, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
|
|
220
|
+
buddy.command("make:function [name]", descriptions.function).option("-n, --name [name]", descriptions.name, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (name, options) => {
|
|
211
221
|
log.debug("Running `buddy make:function` ...", options);
|
|
222
|
+
name = name ?? options.name;
|
|
223
|
+
options.name = name;
|
|
224
|
+
if (!name) {
|
|
225
|
+
console.error("You need to specify a name. Read more about the documentation here.");
|
|
226
|
+
process.exit(ExitCode.FatalError);
|
|
227
|
+
}
|
|
212
228
|
await makeFunction(options);
|
|
213
229
|
});
|
|
214
230
|
buddy.command("make:lang [name]", descriptions.language).option("-n, --name [name]", descriptions.name, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (name, options) => {
|
|
@@ -216,26 +232,27 @@ export function make(buddy) {
|
|
|
216
232
|
name = name ?? options.name;
|
|
217
233
|
options.name = name;
|
|
218
234
|
if (!name) {
|
|
219
|
-
|
|
235
|
+
console.error("You need to specify a name. Read more about the documentation here.");
|
|
220
236
|
process.exit(ExitCode.FatalError);
|
|
221
237
|
}
|
|
222
238
|
await makeLanguage(options);
|
|
223
239
|
});
|
|
224
|
-
buddy.command("make:migration [name]", descriptions.migration).option("-n, --name [name]", descriptions.name, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action((name, options) => {
|
|
240
|
+
buddy.command("make:migration [name]", descriptions.migration).option("-n, --name [name]", descriptions.name, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (name, options) => {
|
|
225
241
|
log.debug("Running `buddy make:migration` ...", options);
|
|
226
242
|
name = name ?? options.name;
|
|
227
243
|
options.name = name;
|
|
228
244
|
if (!name) {
|
|
229
|
-
|
|
245
|
+
console.error("You need to specify a migration name");
|
|
230
246
|
process.exit(ExitCode.FatalError);
|
|
231
247
|
}
|
|
248
|
+
await createMigration(options);
|
|
232
249
|
});
|
|
233
250
|
buddy.command("make:model [name]", descriptions.model).option("-n, --name [name]", descriptions.name, { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (name, options) => {
|
|
234
251
|
log.debug("Running `buddy make:model` ...", options);
|
|
235
252
|
name = name ?? options.name;
|
|
236
253
|
options.name = name;
|
|
237
254
|
if (!name) {
|
|
238
|
-
|
|
255
|
+
console.error("You need to specify a model name");
|
|
239
256
|
process.exit(ExitCode.FatalError);
|
|
240
257
|
}
|
|
241
258
|
await createModel(options);
|
|
@@ -246,7 +263,7 @@ export function make(buddy) {
|
|
|
246
263
|
name = name ?? options.name;
|
|
247
264
|
options.name = name;
|
|
248
265
|
if (!name) {
|
|
249
|
-
|
|
266
|
+
console.error("You need to specify a name (e.g. `buddy make:mail OrderShipped`).");
|
|
250
267
|
process.exit(ExitCode.FatalError);
|
|
251
268
|
}
|
|
252
269
|
await makeMail(options);
|
|
@@ -262,7 +279,7 @@ export function make(buddy) {
|
|
|
262
279
|
name = name ?? options.name;
|
|
263
280
|
options.name = name;
|
|
264
281
|
if (!name) {
|
|
265
|
-
|
|
282
|
+
console.error("You need to specify a name. Read more about the documentation here.");
|
|
266
283
|
process.exit(ExitCode.FatalError);
|
|
267
284
|
}
|
|
268
285
|
if (!await createNotification(options)) {
|
|
@@ -284,8 +301,8 @@ export function make(buddy) {
|
|
|
284
301
|
name = name ?? options.name;
|
|
285
302
|
options.name = name;
|
|
286
303
|
if (!name) {
|
|
287
|
-
|
|
288
|
-
|
|
304
|
+
console.error("You need to specify a policy name.");
|
|
305
|
+
console.error("Example: buddy make:policy PostPolicy");
|
|
289
306
|
process.exit(ExitCode.FatalError);
|
|
290
307
|
}
|
|
291
308
|
if (!await makePolicy(options)) {
|
|
@@ -307,8 +324,8 @@ export function make(buddy) {
|
|
|
307
324
|
name = name ?? options.name;
|
|
308
325
|
options.name = name;
|
|
309
326
|
if (!name) {
|
|
310
|
-
|
|
311
|
-
|
|
327
|
+
console.error("You need to specify a resource name.");
|
|
328
|
+
console.error("Example: buddy make:resource UserResource");
|
|
312
329
|
process.exit(ExitCode.FatalError);
|
|
313
330
|
}
|
|
314
331
|
if (!await makeResource(options)) {
|
|
@@ -324,7 +341,7 @@ export function make(buddy) {
|
|
|
324
341
|
});
|
|
325
342
|
process.exit(ExitCode.Success);
|
|
326
343
|
});
|
|
327
|
-
buddy.command("make:queue-table", descriptions.
|
|
344
|
+
buddy.command("make:queue-table", descriptions.queueTable).option("-p, --project [project]", descriptions.project, { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
|
|
328
345
|
log.debug("Running `buddy make queue:table` ...", options);
|
|
329
346
|
await makeQueueTable();
|
|
330
347
|
});
|
|
@@ -333,7 +350,7 @@ export function make(buddy) {
|
|
|
333
350
|
name = name ?? options.name;
|
|
334
351
|
options.name = name;
|
|
335
352
|
if (!name) {
|
|
336
|
-
|
|
353
|
+
console.error("You need to specify a name. Read more about the documentation here.");
|
|
337
354
|
process.exit(ExitCode.FatalError);
|
|
338
355
|
}
|
|
339
356
|
await makeStack(options);
|
|
@@ -343,7 +360,7 @@ export function make(buddy) {
|
|
|
343
360
|
name = name ?? options.name;
|
|
344
361
|
options.name = name;
|
|
345
362
|
if (!name) {
|
|
346
|
-
|
|
363
|
+
console.error("You need to specify a name. Read more about the documentation here.");
|
|
347
364
|
process.exit(ExitCode.FatalError);
|
|
348
365
|
}
|
|
349
366
|
await makePage(options);
|
|
@@ -354,8 +371,8 @@ export function make(buddy) {
|
|
|
354
371
|
name = name ?? options.name;
|
|
355
372
|
options.name = name;
|
|
356
373
|
if (!name) {
|
|
357
|
-
|
|
358
|
-
|
|
374
|
+
console.error("You need to specify a job name.");
|
|
375
|
+
console.error("Example: buddy make:job SendWelcomeEmail");
|
|
359
376
|
process.exit(ExitCode.FatalError);
|
|
360
377
|
}
|
|
361
378
|
if (!await makeJob(options)) {
|
package/dist/commands/migrate.js
CHANGED
|
@@ -181,6 +181,7 @@ function currentDatabaseLabel() {
|
|
|
181
181
|
export function migrate(buddy) {
|
|
182
182
|
const descriptions = {
|
|
183
183
|
migrate: "Migrates your database",
|
|
184
|
+
fresh: "Drop all tables and re-run every migration (destroys all data)",
|
|
184
185
|
project: "Target a specific project",
|
|
185
186
|
verbose: "Enable verbose output",
|
|
186
187
|
auth: "Also migrate auth tables (oauth_clients, oauth_access_tokens, oauth_refresh_tokens, password_resets)",
|
|
@@ -291,7 +292,7 @@ export function migrate(buddy) {
|
|
|
291
292
|
});
|
|
292
293
|
process.exit(ExitCode.Success);
|
|
293
294
|
});
|
|
294
|
-
buddy.command("migrate:fresh", descriptions.
|
|
295
|
+
buddy.command("migrate:fresh", descriptions.fresh).alias("db:fresh").option("-d, --diff", "Show the SQL that would be run", { default: !1 }).option("-p, --project [project]", descriptions.project, { default: !1 }).option("-s, --seed", "Run database seeders after migration", { default: !1 }).option("-a, --auth", descriptions.auth, { default: !0 }).option("--no-auth", "Skip auth/oauth table migrations").option("-f, --force", 'Skip the drop-database confirmation (only honored when the migrateFresh guard is "allow")', { default: !1 }).option("--verbose", descriptions.verbose, { default: !1 }).action(async (options) => {
|
|
295
296
|
log.debug("Running `buddy migrate:fresh` ...", options);
|
|
296
297
|
const perf = await intro("buddy migrate:fresh"), validation = validateModelsExist();
|
|
297
298
|
if (!validation.valid) {
|
package/dist/commands/setup.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { CLI, CliOptions } from '@stacksjs/types';
|
|
|
2
2
|
export declare function setup(buddy: CLI): void;
|
|
3
3
|
export declare function ensurePantryInstalled(): Promise<void>;
|
|
4
4
|
export declare function ensurePantryDependencies(cwd: string): Promise<void>;
|
|
5
|
+
export declare function ensureNodeDependencies(cwd: string): Promise<void>;
|
|
5
6
|
export declare function ensureAppKey(cwd: string): Promise<void>;
|
|
6
7
|
export declare function pantryDatabasePackage(connection: string): DatabasePackage | undefined;
|
|
7
8
|
/**
|