business-stack 0.1.1 → 0.1.3
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/README.md +12 -0
- package/bin/business-stack.cjs +99 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,6 +19,18 @@ Check your environment:
|
|
|
19
19
|
npx business-stack doctor
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
### Using `npx` (no install, or one-off runs)
|
|
23
|
+
|
|
24
|
+
`npx` runs the same `business-stack` binary as a global or local install. Use a version tag so npm does not cache an old release:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx --yes business-stack@latest doctor
|
|
28
|
+
npx --yes business-stack@latest setup --yes
|
|
29
|
+
npx --yes business-stack@latest start
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
On **Windows Command Prompt**, use the same commands; if `npx` is slow the first time, it is downloading the package.
|
|
33
|
+
|
|
22
34
|
## Installation (recommended: project-level)
|
|
23
35
|
|
|
24
36
|
**Prefer a local (project) install** so the stack, generated `.env` files, and SQLite database live under your project’s `node_modules/business-stack/`, match npm’s default `npm i business-stack`, and avoid permission or PATH issues from global installs.
|
package/bin/business-stack.cjs
CHANGED
|
@@ -111,10 +111,29 @@ function writeEnvFile(filePath, content) {
|
|
|
111
111
|
fs.writeFileSync(filePath, `${content.trim()}\n`, "utf8");
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
/** Resolve `bun` / `uv` to a concrete path on Windows (`where`); avoids spawn ENOENT for .cmd shims. */
|
|
115
|
+
function resolveExecutable(cmd) {
|
|
116
|
+
if (process.platform !== "win32") {
|
|
117
|
+
return cmd;
|
|
118
|
+
}
|
|
119
|
+
const r = spawnSync("where.exe", [cmd], {
|
|
120
|
+
encoding: "utf8",
|
|
121
|
+
windowsHide: true,
|
|
122
|
+
});
|
|
123
|
+
if (r.status !== 0 || !r.stdout) {
|
|
124
|
+
return cmd;
|
|
125
|
+
}
|
|
126
|
+
const first = r.stdout
|
|
127
|
+
.split(/\r?\n/)
|
|
128
|
+
.map((s) => s.trim())
|
|
129
|
+
.find(Boolean);
|
|
130
|
+
return first || cmd;
|
|
131
|
+
}
|
|
132
|
+
|
|
114
133
|
function cmdExists(cmd) {
|
|
115
134
|
const isWin = process.platform === "win32";
|
|
116
135
|
const which = isWin ? "where" : "which";
|
|
117
|
-
const r = spawnSync(which, [cmd], { encoding: "utf8" });
|
|
136
|
+
const r = spawnSync(which, [cmd], { encoding: "utf8", windowsHide: isWin });
|
|
118
137
|
return r.status === 0;
|
|
119
138
|
}
|
|
120
139
|
|
|
@@ -274,19 +293,62 @@ function envFileLooksConfigured(stackRoot) {
|
|
|
274
293
|
return hasExistingSecrets(stackRoot);
|
|
275
294
|
}
|
|
276
295
|
|
|
277
|
-
function runCmd(cmd, args, cwd, extraEnv) {
|
|
278
|
-
|
|
296
|
+
function runCmd(label, cmd, args, cwd, extraEnv) {
|
|
297
|
+
console.error(`business-stack: ${label}…`);
|
|
298
|
+
const exe = resolveExecutable(cmd);
|
|
299
|
+
const r = spawnSync(exe, args, {
|
|
279
300
|
cwd,
|
|
280
301
|
stdio: "inherit",
|
|
281
302
|
env: { ...process.env, ...extraEnv },
|
|
282
303
|
shell: false,
|
|
304
|
+
windowsHide: process.platform === "win32",
|
|
283
305
|
});
|
|
306
|
+
if (r.error) {
|
|
307
|
+
console.error(`business-stack: ${label} failed:`, r.error.message);
|
|
308
|
+
console.error(` Command: ${exe} ${args.join(" ")}`);
|
|
309
|
+
console.error(` cwd: ${cwd}`);
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
if (r.signal) {
|
|
313
|
+
console.error(`business-stack: ${label} killed (${r.signal})`);
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
284
316
|
if (r.status !== 0) {
|
|
285
317
|
process.exit(r.status ?? 1);
|
|
286
318
|
}
|
|
287
319
|
}
|
|
288
320
|
|
|
289
|
-
|
|
321
|
+
/**
|
|
322
|
+
* Wait for turbo + servers. Async so Commander keeps the Node event loop alive on Windows
|
|
323
|
+
* until the child exits (avoids exiting immediately with no output).
|
|
324
|
+
*/
|
|
325
|
+
function waitForChild(child, label) {
|
|
326
|
+
return new Promise((resolve) => {
|
|
327
|
+
let settled = false;
|
|
328
|
+
const finish = (code) => {
|
|
329
|
+
if (settled) return;
|
|
330
|
+
settled = true;
|
|
331
|
+
resolve(code ?? 0);
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
child.on("error", (err) => {
|
|
335
|
+
console.error(`business-stack: ${label} — failed to spawn process:`, err.message);
|
|
336
|
+
console.error(" Is Bun installed and on PATH? Run: business-stack doctor");
|
|
337
|
+
finish(1);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
child.on("exit", (code, sig) => {
|
|
341
|
+
if (sig) {
|
|
342
|
+
console.error(`business-stack: ${label} — child exited with signal ${sig}`);
|
|
343
|
+
finish(1);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
finish(code ?? 0);
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function runStart(opts) {
|
|
290
352
|
const stackRoot = getStackRoot();
|
|
291
353
|
if (!envFileLooksConfigured(stackRoot)) {
|
|
292
354
|
console.error(
|
|
@@ -295,6 +357,9 @@ function runStart(opts) {
|
|
|
295
357
|
process.exit(1);
|
|
296
358
|
}
|
|
297
359
|
|
|
360
|
+
console.error("business-stack: starting (install → sync → build → migrate → servers)…");
|
|
361
|
+
console.error(`business-stack: stack root ${stackRoot}`);
|
|
362
|
+
|
|
298
363
|
const fromDb = readStackSecretsEnv(stackRoot);
|
|
299
364
|
const childEnv = { ...process.env };
|
|
300
365
|
for (const [k, v] of Object.entries(fromDb)) {
|
|
@@ -304,43 +369,53 @@ function runStart(opts) {
|
|
|
304
369
|
}
|
|
305
370
|
|
|
306
371
|
if (!opts.skipInstall) {
|
|
307
|
-
runCmd("bun", ["install"], stackRoot, childEnv);
|
|
372
|
+
runCmd("bun install (workspaces)", "bun", ["install"], stackRoot, childEnv);
|
|
308
373
|
}
|
|
309
|
-
runCmd("uv", ["sync"], path.join(stackRoot, "backend"), childEnv);
|
|
374
|
+
runCmd("uv sync (backend)", "uv", ["sync"], path.join(stackRoot, "backend"), childEnv);
|
|
310
375
|
if (!opts.skipBuild) {
|
|
311
|
-
runCmd("bun", ["run", "build"], stackRoot, childEnv);
|
|
376
|
+
runCmd("bun run build", "bun", ["run", "build"], stackRoot, childEnv);
|
|
312
377
|
}
|
|
313
378
|
if (!opts.skipMigrate) {
|
|
314
|
-
runCmd(
|
|
379
|
+
runCmd(
|
|
380
|
+
"alembic upgrade head",
|
|
381
|
+
"uv",
|
|
382
|
+
["run", "alembic", "upgrade", "head"],
|
|
383
|
+
path.join(stackRoot, "backend"),
|
|
384
|
+
childEnv,
|
|
385
|
+
);
|
|
315
386
|
}
|
|
316
387
|
|
|
317
|
-
|
|
388
|
+
console.error("business-stack: launching production servers (turbo start)…");
|
|
389
|
+
|
|
390
|
+
const bunExe = resolveExecutable("bun");
|
|
391
|
+
const child = spawn(bunExe, ["run", "start"], {
|
|
318
392
|
cwd: stackRoot,
|
|
319
393
|
stdio: "inherit",
|
|
320
394
|
env: childEnv,
|
|
321
395
|
shell: false,
|
|
396
|
+
windowsHide: process.platform === "win32",
|
|
322
397
|
});
|
|
323
398
|
|
|
324
|
-
|
|
399
|
+
const shutdown = () => {
|
|
325
400
|
try {
|
|
326
|
-
if (process.platform === "win32") {
|
|
401
|
+
if (process.platform === "win32" && child.pid) {
|
|
327
402
|
spawnSync("taskkill", ["/PID", String(child.pid), "/T", "/F"], {
|
|
328
403
|
stdio: "ignore",
|
|
404
|
+
windowsHide: true,
|
|
329
405
|
});
|
|
330
|
-
} else {
|
|
331
|
-
child.kill(
|
|
406
|
+
} else if (child.pid) {
|
|
407
|
+
child.kill("SIGINT");
|
|
332
408
|
}
|
|
333
409
|
} catch {
|
|
334
410
|
/* ignore */
|
|
335
411
|
}
|
|
336
|
-
}
|
|
412
|
+
};
|
|
337
413
|
|
|
338
|
-
process.
|
|
339
|
-
process.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
});
|
|
414
|
+
process.once("SIGINT", shutdown);
|
|
415
|
+
process.once("SIGTERM", shutdown);
|
|
416
|
+
|
|
417
|
+
const code = await waitForChild(child, "turbo start");
|
|
418
|
+
process.exit(code);
|
|
344
419
|
}
|
|
345
420
|
|
|
346
421
|
function runDev() {
|
|
@@ -352,13 +427,13 @@ function runDev() {
|
|
|
352
427
|
childEnv[k] = String(v);
|
|
353
428
|
}
|
|
354
429
|
}
|
|
355
|
-
runCmd("bun", ["run", "dev"], stackRoot, childEnv);
|
|
430
|
+
runCmd("bun run dev", "bun", ["run", "dev"], stackRoot, childEnv);
|
|
356
431
|
}
|
|
357
432
|
|
|
358
433
|
program
|
|
359
434
|
.name("business-stack")
|
|
360
435
|
.description("Run the business-stack monorepo (Next + Hono gateway + FastAPI)")
|
|
361
|
-
.version("0.1.
|
|
436
|
+
.version("0.1.3");
|
|
362
437
|
|
|
363
438
|
program
|
|
364
439
|
.command("doctor")
|
|
@@ -385,9 +460,9 @@ program
|
|
|
385
460
|
.option("--skip-install", "Skip bun install")
|
|
386
461
|
.option("--skip-build", "Skip turbo build")
|
|
387
462
|
.option("--skip-migrate", "Skip alembic upgrade")
|
|
388
|
-
.action((o) => {
|
|
463
|
+
.action(async (o) => {
|
|
389
464
|
try {
|
|
390
|
-
runStart(o);
|
|
465
|
+
await runStart(o);
|
|
391
466
|
} catch (e) {
|
|
392
467
|
console.error(e instanceof Error ? e.message : e);
|
|
393
468
|
process.exit(1);
|