@rebasepro/cli 0.0.1-canary.4d4fb3e → 0.0.1-canary.ca2cb6e
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/commands/cli.test.d.ts +1 -0
- package/dist/commands/dev.test.d.ts +1 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/generate_sdk.d.ts +1 -1
- package/dist/commands/init.test.d.ts +1 -0
- package/dist/index.cjs +188 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +188 -62
- package/dist/index.es.js.map +1 -1
- package/dist/utils/project.test.d.ts +1 -0
- package/package.json +67 -66
- package/templates/template/.env.template +22 -1
- package/templates/template/README.md +2 -2
- package/templates/template/backend/Dockerfile +6 -6
- package/templates/template/backend/functions/hello.ts +24 -6
- package/templates/template/backend/package.json +2 -2
- package/templates/template/backend/src/env.ts +52 -0
- package/templates/template/backend/src/index.ts +86 -34
- package/templates/template/backend/tsconfig.json +1 -1
- package/templates/template/config/collections/authors.ts +45 -0
- package/templates/template/config/collections/index.ts +5 -0
- package/templates/template/{shared → config}/collections/posts.ts +39 -1
- package/templates/template/config/collections/tags.ts +27 -0
- package/templates/template/{shared → config}/package.json +1 -1
- package/templates/template/docker-compose.yml +12 -0
- package/templates/template/frontend/Dockerfile +3 -3
- package/templates/template/frontend/package.json +2 -2
- package/templates/template/frontend/src/App.tsx +11 -12
- package/templates/template/frontend/src/main.tsx +2 -2
- package/templates/template/frontend/vite.config.ts +1 -1
- package/templates/template/pnpm-workspace.yaml +1 -1
- package/templates/template/scripts/example.ts +91 -0
- package/templates/template/shared/collections/index.ts +0 -3
- /package/templates/template/{shared → config}/index.ts +0 -0
- /package/templates/template/{shared → config}/tsconfig.json +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function doctorCommand(rawArgs: string[]): Promise<void>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CLI command: generate-sdk
|
|
3
3
|
*
|
|
4
|
-
* Reads collection definitions from a specified directory (default: ./
|
|
4
|
+
* Reads collection definitions from a specified directory (default: ./config/collections),
|
|
5
5
|
* generates a typed JS SDK, and writes it to the output directory (default: ./generated/sdk).
|
|
6
6
|
*
|
|
7
7
|
* Uses jiti for dynamic TypeScript import of collection files.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.cjs
CHANGED
|
@@ -90,8 +90,8 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
|
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
92
|
const answers = await inquirer.prompt(questions);
|
|
93
|
-
const
|
|
94
|
-
const
|
|
93
|
+
const targetDirectory = path.resolve(process.cwd(), nameArg || answers.projectName);
|
|
94
|
+
const projectName = path.basename(targetDirectory);
|
|
95
95
|
const templateDirectory = path.resolve(cliRoot, "templates", "template");
|
|
96
96
|
return {
|
|
97
97
|
projectName,
|
|
@@ -188,7 +188,7 @@ POSTGRES_PASSWORD=${dbPassword}
|
|
|
188
188
|
console.log("");
|
|
189
189
|
console.log(` ${chalk.cyan("pnpm dev")}`);
|
|
190
190
|
console.log("");
|
|
191
|
-
console.log(chalk.gray("This starts both the backend (
|
|
191
|
+
console.log(chalk.gray("This starts both the backend (Hono + PostgreSQL)") + chalk.gray(" and the frontend (Vite + React) concurrently."));
|
|
192
192
|
console.log("");
|
|
193
193
|
console.log(chalk.gray("Docs: https://rebase.pro/docs"));
|
|
194
194
|
console.log(chalk.gray("GitHub: https://github.com/rebasepro/rebase"));
|
|
@@ -199,14 +199,21 @@ POSTGRES_PASSWORD=${dbPassword}
|
|
|
199
199
|
"package.json",
|
|
200
200
|
"frontend/package.json",
|
|
201
201
|
"backend/package.json",
|
|
202
|
-
"
|
|
202
|
+
"config/package.json",
|
|
203
203
|
"frontend/index.html"
|
|
204
204
|
];
|
|
205
|
+
const packageJsonPath = path.resolve(cliRoot, "package.json");
|
|
206
|
+
let cliVersion = "latest";
|
|
207
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
208
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
209
|
+
cliVersion = pkg.version || "latest";
|
|
210
|
+
}
|
|
205
211
|
for (const file of filesToProcess) {
|
|
206
212
|
const fullPath = path.resolve(options.targetDirectory, file);
|
|
207
213
|
if (!fs.existsSync(fullPath)) continue;
|
|
208
214
|
let content = fs.readFileSync(fullPath, "utf-8");
|
|
209
215
|
content = content.replace(/\{\{PROJECT_NAME\}\}/g, options.projectName);
|
|
216
|
+
content = content.replace(/("@rebasepro\/[^"]+":\s*)"workspace:\*"/g, `$1"${cliVersion}"`);
|
|
210
217
|
fs.writeFileSync(fullPath, content, "utf-8");
|
|
211
218
|
}
|
|
212
219
|
}
|
|
@@ -318,7 +325,7 @@ Expected a default export of EntityCollection[] or an object with named collecti
|
|
|
318
325
|
console.log(chalk.green.bold(" ✓ SDK generated successfully!"));
|
|
319
326
|
console.log("");
|
|
320
327
|
console.log(chalk.gray(" Usage:"));
|
|
321
|
-
console.log(chalk.gray(
|
|
328
|
+
console.log(chalk.gray(" import { createRebaseClient } from '@rebasepro/client';"));
|
|
322
329
|
console.log(chalk.gray(` import type { Database } from './${path.relative(cwd, path.join(resolvedOutput, "database.types"))}';`));
|
|
323
330
|
console.log("");
|
|
324
331
|
console.log(chalk.gray(" const rebase = createRebaseClient<Database>({"));
|
|
@@ -345,7 +352,7 @@ Expected a default export of EntityCollection[] or an object with named collecti
|
|
|
345
352
|
}
|
|
346
353
|
} catch {
|
|
347
354
|
}
|
|
348
|
-
if (fs.existsSync(path.join(dir, "backend")) && fs.existsSync(path.join(dir, "
|
|
355
|
+
if (fs.existsSync(path.join(dir, "backend")) && fs.existsSync(path.join(dir, "config"))) {
|
|
349
356
|
return dir;
|
|
350
357
|
}
|
|
351
358
|
}
|
|
@@ -362,7 +369,10 @@ Expected a default export of EntityCollection[] or an object with named collecti
|
|
|
362
369
|
if (!fs.existsSync(pkgPath)) return null;
|
|
363
370
|
try {
|
|
364
371
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
365
|
-
const deps = {
|
|
372
|
+
const deps = {
|
|
373
|
+
...pkg.dependencies,
|
|
374
|
+
...pkg.devDependencies
|
|
375
|
+
};
|
|
366
376
|
for (const dep of Object.keys(deps)) {
|
|
367
377
|
if (dep.startsWith("@rebasepro/server-") && dep !== "@rebasepro/server-core") {
|
|
368
378
|
return dep;
|
|
@@ -473,13 +483,13 @@ Expected a default export of EntityCollection[] or an object with named collecti
|
|
|
473
483
|
console.error(chalk.red("✗ Could not find tsx binary."));
|
|
474
484
|
process.exit(1);
|
|
475
485
|
}
|
|
476
|
-
await execa(tsxBin, [pluginCli,
|
|
486
|
+
await execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
|
|
477
487
|
cwd: backendDir,
|
|
478
488
|
stdio: "inherit",
|
|
479
489
|
env
|
|
480
490
|
});
|
|
481
491
|
} else {
|
|
482
|
-
await execa("node", [pluginCli,
|
|
492
|
+
await execa("node", [pluginCli, ...rawArgs.slice(2)], {
|
|
483
493
|
cwd: backendDir,
|
|
484
494
|
stdio: "inherit",
|
|
485
495
|
env
|
|
@@ -537,13 +547,13 @@ ${chalk.green.bold("generate Options")}
|
|
|
537
547
|
console.error(chalk.red("✗ Could not find tsx binary."));
|
|
538
548
|
process.exit(1);
|
|
539
549
|
}
|
|
540
|
-
await execa(tsxBin, [pluginCli,
|
|
550
|
+
await execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
|
|
541
551
|
cwd: backendDir,
|
|
542
552
|
stdio: "inherit",
|
|
543
553
|
env
|
|
544
554
|
});
|
|
545
555
|
} else {
|
|
546
|
-
await execa("node", [pluginCli,
|
|
556
|
+
await execa("node", [pluginCli, ...rawArgs.slice(2)], {
|
|
547
557
|
cwd: backendDir,
|
|
548
558
|
stdio: "inherit",
|
|
549
559
|
env
|
|
@@ -581,6 +591,27 @@ ${chalk.green.bold("Examples")}
|
|
|
581
591
|
rebase db branch create feature_auth
|
|
582
592
|
`);
|
|
583
593
|
}
|
|
594
|
+
const DEV_PORT_FILENAME = ".rebase-dev-port";
|
|
595
|
+
function getProjectPort(projectRoot) {
|
|
596
|
+
let hash = 0;
|
|
597
|
+
for (let i = 0; i < projectRoot.length; i++) {
|
|
598
|
+
hash = (hash << 5) - hash + projectRoot.charCodeAt(i) | 0;
|
|
599
|
+
}
|
|
600
|
+
return 3001 + Math.abs(hash) % 999;
|
|
601
|
+
}
|
|
602
|
+
function resolveStartPort(projectRoot, explicitPort) {
|
|
603
|
+
if (explicitPort) return explicitPort;
|
|
604
|
+
if (process.env.PORT) return parseInt(process.env.PORT, 10);
|
|
605
|
+
try {
|
|
606
|
+
const portFile = path.join(projectRoot, DEV_PORT_FILENAME);
|
|
607
|
+
if (fs.existsSync(portFile)) {
|
|
608
|
+
const saved = parseInt(fs.readFileSync(portFile, "utf-8").trim(), 10);
|
|
609
|
+
if (saved > 0 && saved < 65536) return saved;
|
|
610
|
+
}
|
|
611
|
+
} catch {
|
|
612
|
+
}
|
|
613
|
+
return getProjectPort(projectRoot);
|
|
614
|
+
}
|
|
584
615
|
async function devCommand(rawArgs) {
|
|
585
616
|
const args = arg(
|
|
586
617
|
{
|
|
@@ -608,6 +639,7 @@ ${chalk.green.bold("Examples")}
|
|
|
608
639
|
const frontendDir = findFrontendDir(projectRoot);
|
|
609
640
|
const backendOnly = args["--backend-only"] || false;
|
|
610
641
|
const frontendOnly = args["--frontend-only"] || false;
|
|
642
|
+
const startPort = resolveStartPort(projectRoot, args["--port"]);
|
|
611
643
|
console.log("");
|
|
612
644
|
console.log(chalk.bold(" 🚀 Rebase Dev Server"));
|
|
613
645
|
console.log("");
|
|
@@ -616,6 +648,7 @@ ${chalk.green.bold("Examples")}
|
|
|
616
648
|
let backendUrl = "";
|
|
617
649
|
let debounceSummary = null;
|
|
618
650
|
let bannerPrinted = false;
|
|
651
|
+
let resolvedBackendPort = null;
|
|
619
652
|
const stripAnsi = (str) => str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
|
|
620
653
|
function printSummary() {
|
|
621
654
|
if (!frontendUrl || !backendUrl) return;
|
|
@@ -628,7 +661,7 @@ ${chalk.green.bold("Examples")}
|
|
|
628
661
|
console.log(chalk.cyan("│ ✨ Rebase Admin App is ready! │"));
|
|
629
662
|
const cleanUrl = stripAnsi(frontendUrl);
|
|
630
663
|
const paddedUrl = cleanUrl.padEnd(40);
|
|
631
|
-
console.log(chalk.cyan(
|
|
664
|
+
console.log(chalk.cyan("│ 👉 Frontend URL: ") + chalk.white(paddedUrl) + chalk.cyan("│"));
|
|
632
665
|
console.log(chalk.cyan("│ │"));
|
|
633
666
|
console.log(chalk.cyan("└────────────────────────────────────────────────────────────┘"));
|
|
634
667
|
console.log("");
|
|
@@ -636,15 +669,74 @@ ${chalk.green.bold("Examples")}
|
|
|
636
669
|
}, 500);
|
|
637
670
|
}
|
|
638
671
|
const cleanup = () => {
|
|
672
|
+
try {
|
|
673
|
+
const portFile = path.join(projectRoot, DEV_PORT_FILENAME);
|
|
674
|
+
if (fs.existsSync(portFile)) fs.unlinkSync(portFile);
|
|
675
|
+
const urlFile = path.join(projectRoot, ".rebase-dev-url");
|
|
676
|
+
if (fs.existsSync(urlFile)) fs.unlinkSync(urlFile);
|
|
677
|
+
} catch {
|
|
678
|
+
}
|
|
639
679
|
children.forEach((child) => {
|
|
640
|
-
if (!child.killed) {
|
|
641
|
-
|
|
680
|
+
if (child.pid && !child.killed) {
|
|
681
|
+
try {
|
|
682
|
+
if (process.platform === "win32") {
|
|
683
|
+
execa.commandSync(`taskkill /pid ${child.pid} /T /F`);
|
|
684
|
+
} else {
|
|
685
|
+
process.kill(-child.pid, "SIGKILL");
|
|
686
|
+
}
|
|
687
|
+
} catch (e) {
|
|
688
|
+
try {
|
|
689
|
+
child.kill("SIGKILL");
|
|
690
|
+
} catch (err) {
|
|
691
|
+
}
|
|
692
|
+
}
|
|
642
693
|
}
|
|
643
694
|
});
|
|
644
695
|
process.exit(0);
|
|
645
696
|
};
|
|
646
697
|
process.on("SIGINT", cleanup);
|
|
647
698
|
process.on("SIGTERM", cleanup);
|
|
699
|
+
function startFrontend(backendPort) {
|
|
700
|
+
if (!frontendDir) return;
|
|
701
|
+
console.log(` ${chalk.magenta("▶")} Frontend: ${chalk.gray(frontendDir)}`);
|
|
702
|
+
const frontendEnv = { ...process.env };
|
|
703
|
+
if (backendPort) {
|
|
704
|
+
frontendEnv.VITE_API_URL = `http://localhost:${backendPort}`;
|
|
705
|
+
console.log(` ${chalk.gray("↳ VITE_API_URL")} = ${chalk.white(`http://localhost:${backendPort}`)}`);
|
|
706
|
+
}
|
|
707
|
+
const frontendChild = execa(
|
|
708
|
+
"pnpm",
|
|
709
|
+
["run", "dev"],
|
|
710
|
+
{
|
|
711
|
+
cwd: frontendDir,
|
|
712
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
713
|
+
env: frontendEnv,
|
|
714
|
+
shell: true,
|
|
715
|
+
detached: process.platform !== "win32"
|
|
716
|
+
}
|
|
717
|
+
);
|
|
718
|
+
frontendChild.catch(() => {
|
|
719
|
+
});
|
|
720
|
+
frontendChild.stdout?.on("data", (data) => {
|
|
721
|
+
const lines = data.toString().split("\n").filter(Boolean);
|
|
722
|
+
lines.forEach((line) => {
|
|
723
|
+
console.log(`${chalk.magenta.bold("[admin]")} ${line}`);
|
|
724
|
+
const cleanLine = stripAnsi(line);
|
|
725
|
+
const urlMatch = cleanLine.match(/(http:\/\/(?:localhost|127\.0\.0\.1):\d+)/);
|
|
726
|
+
if (cleanLine.includes("Local:") && urlMatch) {
|
|
727
|
+
frontendUrl = urlMatch[1];
|
|
728
|
+
printSummary();
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
});
|
|
732
|
+
frontendChild.stderr?.on("data", (data) => {
|
|
733
|
+
const lines = data.toString().split("\n").filter(Boolean);
|
|
734
|
+
lines.forEach((line) => {
|
|
735
|
+
console.log(`${chalk.magenta.bold("[admin]")} ${line}`);
|
|
736
|
+
});
|
|
737
|
+
});
|
|
738
|
+
children.push(frontendChild);
|
|
739
|
+
}
|
|
648
740
|
if (!frontendOnly && backendDir) {
|
|
649
741
|
const tsxBin = resolveTsx(projectRoot);
|
|
650
742
|
if (!tsxBin) {
|
|
@@ -657,22 +749,19 @@ ${chalk.green.bold("Examples")}
|
|
|
657
749
|
if (envFile) {
|
|
658
750
|
env.DOTENV_CONFIG_PATH = envFile;
|
|
659
751
|
}
|
|
660
|
-
|
|
661
|
-
env.PORT = String(args["--port"]);
|
|
662
|
-
}
|
|
663
|
-
path.join(backendDir, "src", "index.ts");
|
|
664
|
-
const watchDirs = [
|
|
665
|
-
`--watch="${path.join("..", "shared", "**", "*")}"`
|
|
666
|
-
];
|
|
752
|
+
env.PORT = String(startPort);
|
|
667
753
|
console.log(` ${chalk.cyan("▶")} Backend: ${chalk.gray(backendDir)}`);
|
|
754
|
+
console.log(` ${chalk.gray("↳ PORT")} = ${chalk.white(String(startPort))}`);
|
|
755
|
+
let frontendLaunched = false;
|
|
668
756
|
const backendChild = execa(
|
|
669
757
|
tsxBin,
|
|
670
|
-
["watch",
|
|
758
|
+
["watch", `--watch="${path.join("..", "config", "**", "*")}"`, "--conditions", "development", "src/index.ts"],
|
|
671
759
|
{
|
|
672
760
|
cwd: backendDir,
|
|
673
761
|
stdio: ["inherit", "pipe", "pipe"],
|
|
674
762
|
env,
|
|
675
|
-
shell: true
|
|
763
|
+
shell: true,
|
|
764
|
+
detached: process.platform !== "win32"
|
|
676
765
|
}
|
|
677
766
|
);
|
|
678
767
|
backendChild.catch(() => {
|
|
@@ -682,9 +771,19 @@ ${chalk.green.bold("Examples")}
|
|
|
682
771
|
lines.forEach((line) => {
|
|
683
772
|
console.log(`${chalk.cyan.bold("[backend]")} ${line}`);
|
|
684
773
|
const cleanLine = stripAnsi(line);
|
|
685
|
-
|
|
774
|
+
const serverMatch = cleanLine.match(/Server running at http:\/\/(?:localhost|127\.0\.0\.1):(\d+)/);
|
|
775
|
+
if (serverMatch) {
|
|
776
|
+
resolvedBackendPort = parseInt(serverMatch[1], 10);
|
|
686
777
|
backendUrl = "started";
|
|
687
778
|
printSummary();
|
|
779
|
+
const urlFile = path.join(projectRoot, ".rebase-dev-url");
|
|
780
|
+
fs.writeFileSync(urlFile, `http://localhost:${resolvedBackendPort}`, "utf-8");
|
|
781
|
+
const portFile = path.join(projectRoot, DEV_PORT_FILENAME);
|
|
782
|
+
fs.writeFileSync(portFile, String(resolvedBackendPort), "utf-8");
|
|
783
|
+
if (!backendOnly && frontendDir && !frontendLaunched) {
|
|
784
|
+
frontendLaunched = true;
|
|
785
|
+
startFrontend(resolvedBackendPort);
|
|
786
|
+
}
|
|
688
787
|
}
|
|
689
788
|
});
|
|
690
789
|
});
|
|
@@ -698,39 +797,8 @@ ${chalk.green.bold("Examples")}
|
|
|
698
797
|
} else if (!frontendOnly && !backendDir) {
|
|
699
798
|
console.warn(chalk.yellow(" ⚠ No backend/ directory found, skipping backend."));
|
|
700
799
|
}
|
|
701
|
-
if (!backendOnly && frontendDir) {
|
|
702
|
-
|
|
703
|
-
const frontendChild = execa(
|
|
704
|
-
"pnpm",
|
|
705
|
-
["run", "dev"],
|
|
706
|
-
{
|
|
707
|
-
cwd: frontendDir,
|
|
708
|
-
stdio: ["inherit", "pipe", "pipe"],
|
|
709
|
-
env: process.env,
|
|
710
|
-
shell: true
|
|
711
|
-
}
|
|
712
|
-
);
|
|
713
|
-
frontendChild.catch(() => {
|
|
714
|
-
});
|
|
715
|
-
frontendChild.stdout?.on("data", (data) => {
|
|
716
|
-
const lines = data.toString().split("\n").filter(Boolean);
|
|
717
|
-
lines.forEach((line) => {
|
|
718
|
-
console.log(`${chalk.magenta.bold("[frontend]")} ${line}`);
|
|
719
|
-
const cleanLine = stripAnsi(line);
|
|
720
|
-
const urlMatch = cleanLine.match(/(http:\/\/(?:localhost|127\.0\.0\.1):\d+)/);
|
|
721
|
-
if (cleanLine.includes("Local:") && urlMatch) {
|
|
722
|
-
frontendUrl = urlMatch[1];
|
|
723
|
-
printSummary();
|
|
724
|
-
}
|
|
725
|
-
});
|
|
726
|
-
});
|
|
727
|
-
frontendChild.stderr?.on("data", (data) => {
|
|
728
|
-
const lines = data.toString().split("\n").filter(Boolean);
|
|
729
|
-
lines.forEach((line) => {
|
|
730
|
-
console.log(`${chalk.magenta.bold("[frontend]")} ${line}`);
|
|
731
|
-
});
|
|
732
|
-
});
|
|
733
|
-
children.push(frontendChild);
|
|
800
|
+
if (!backendOnly && frontendDir && (frontendOnly || !backendDir)) {
|
|
801
|
+
startFrontend(null);
|
|
734
802
|
} else if (!backendOnly && !frontendDir) {
|
|
735
803
|
console.warn(chalk.yellow(" ⚠ No frontend/ directory found, skipping frontend."));
|
|
736
804
|
}
|
|
@@ -759,11 +827,19 @@ ${chalk.green.bold("Usage")}
|
|
|
759
827
|
${chalk.green.bold("Options")}
|
|
760
828
|
${chalk.blue("--backend-only, -b")} Only start the backend server
|
|
761
829
|
${chalk.blue("--frontend-only, -f")} Only start the frontend server
|
|
762
|
-
${chalk.blue("--port, -p")} Backend port (default:
|
|
830
|
+
${chalk.blue("--port, -p")} Backend port (default: auto-detected per project)
|
|
763
831
|
|
|
764
832
|
${chalk.green.bold("Description")}
|
|
765
|
-
Starts both the backend (tsx watch +
|
|
833
|
+
Starts both the backend (tsx watch + Hono) and frontend (Vite)
|
|
766
834
|
dev servers concurrently with color-coded output prefixes.
|
|
835
|
+
|
|
836
|
+
Each project automatically receives a unique default port derived
|
|
837
|
+
from its directory path, preventing collisions when running multiple
|
|
838
|
+
Rebase instances simultaneously.
|
|
839
|
+
|
|
840
|
+
If the assigned port is already in use, the server will automatically
|
|
841
|
+
try the next available port. The frontend is started only after the
|
|
842
|
+
backend is ready, and VITE_API_URL is injected automatically.
|
|
767
843
|
`);
|
|
768
844
|
}
|
|
769
845
|
async function authCommand(subcommand, rawArgs) {
|
|
@@ -900,6 +976,49 @@ ${chalk.green.bold("Examples")}
|
|
|
900
976
|
rebase auth reset-password --email user@example.com --password MyNewPass!
|
|
901
977
|
`);
|
|
902
978
|
}
|
|
979
|
+
async function doctorCommand(rawArgs) {
|
|
980
|
+
const projectRoot = requireProjectRoot();
|
|
981
|
+
const backendDir = requireBackendDir(projectRoot);
|
|
982
|
+
const activePlugin = getActiveBackendPlugin(backendDir);
|
|
983
|
+
if (!activePlugin) {
|
|
984
|
+
console.error(chalk.red("✗ Could not detect an active database plugin."));
|
|
985
|
+
console.error(chalk.gray(" Make sure a package like @rebasepro/server-postgresql is installed in backend/package.json."));
|
|
986
|
+
process.exit(1);
|
|
987
|
+
}
|
|
988
|
+
const pluginCli = resolvePluginCliScript(backendDir, activePlugin);
|
|
989
|
+
if (!pluginCli) {
|
|
990
|
+
console.error(chalk.red(`✗ Could not find CLI entry point for ${activePlugin}.`));
|
|
991
|
+
process.exit(1);
|
|
992
|
+
}
|
|
993
|
+
const envFile = findEnvFile(projectRoot);
|
|
994
|
+
const env = { ...process.env };
|
|
995
|
+
if (envFile) {
|
|
996
|
+
env.DOTENV_CONFIG_PATH = envFile;
|
|
997
|
+
}
|
|
998
|
+
try {
|
|
999
|
+
const isTs = pluginCli.endsWith(".ts");
|
|
1000
|
+
if (isTs) {
|
|
1001
|
+
const tsxBin = resolveTsx(projectRoot);
|
|
1002
|
+
if (!tsxBin) {
|
|
1003
|
+
console.error(chalk.red("✗ Could not find tsx binary."));
|
|
1004
|
+
process.exit(1);
|
|
1005
|
+
}
|
|
1006
|
+
await execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
|
|
1007
|
+
cwd: backendDir,
|
|
1008
|
+
stdio: "inherit",
|
|
1009
|
+
env
|
|
1010
|
+
});
|
|
1011
|
+
} else {
|
|
1012
|
+
await execa("node", [pluginCli, ...rawArgs.slice(2)], {
|
|
1013
|
+
cwd: backendDir,
|
|
1014
|
+
stdio: "inherit",
|
|
1015
|
+
env
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
} catch {
|
|
1019
|
+
process.exit(1);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
903
1022
|
const __filename$1 = url.fileURLToPath(typeof document === "undefined" && typeof location === "undefined" ? require("url").pathToFileURL(__filename).href : typeof document === "undefined" ? location.href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index.cjs", document.baseURI).href);
|
|
904
1023
|
const __dirname$1 = path.dirname(__filename$1);
|
|
905
1024
|
function getVersion() {
|
|
@@ -931,7 +1050,7 @@ ${chalk.green.bold("Examples")}
|
|
|
931
1050
|
}
|
|
932
1051
|
const command = parsedArgs._[0];
|
|
933
1052
|
const subcommand = parsedArgs._[1];
|
|
934
|
-
const namespacedCommands = ["schema", "db", "dev", "auth"];
|
|
1053
|
+
const namespacedCommands = ["schema", "db", "dev", "auth", "doctor"];
|
|
935
1054
|
if (!command || parsedArgs["--help"] && !namespacedCommands.includes(command)) {
|
|
936
1055
|
printHelp();
|
|
937
1056
|
return;
|
|
@@ -941,7 +1060,7 @@ ${chalk.green.bold("Examples")}
|
|
|
941
1060
|
case "init":
|
|
942
1061
|
await createRebaseApp(args);
|
|
943
1062
|
break;
|
|
944
|
-
case "generate-sdk":
|
|
1063
|
+
case "generate-sdk": {
|
|
945
1064
|
const sdkArgs = arg(
|
|
946
1065
|
{
|
|
947
1066
|
"--collections-dir": String,
|
|
@@ -955,11 +1074,12 @@ ${chalk.green.bold("Examples")}
|
|
|
955
1074
|
}
|
|
956
1075
|
);
|
|
957
1076
|
await generateSdkCommand({
|
|
958
|
-
collectionsDir: sdkArgs["--collections-dir"] || "./
|
|
1077
|
+
collectionsDir: sdkArgs["--collections-dir"] || "./config/collections",
|
|
959
1078
|
output: sdkArgs["--output"] || "./generated/sdk",
|
|
960
1079
|
cwd: process.cwd()
|
|
961
1080
|
});
|
|
962
1081
|
break;
|
|
1082
|
+
}
|
|
963
1083
|
case "schema":
|
|
964
1084
|
await schemaCommand(effectiveSubcommand, args);
|
|
965
1085
|
break;
|
|
@@ -972,6 +1092,9 @@ ${chalk.green.bold("Examples")}
|
|
|
972
1092
|
case "auth":
|
|
973
1093
|
await authCommand(effectiveSubcommand, args);
|
|
974
1094
|
break;
|
|
1095
|
+
case "doctor":
|
|
1096
|
+
await doctorCommand(args);
|
|
1097
|
+
break;
|
|
975
1098
|
default:
|
|
976
1099
|
console.log(chalk.red(`Unknown command: ${command}`));
|
|
977
1100
|
console.log("");
|
|
@@ -1008,6 +1131,9 @@ ${chalk.green.bold("Auth")}
|
|
|
1008
1131
|
${chalk.blue.bold("auth reset-password")} Reset a user's password
|
|
1009
1132
|
${chalk.blue.bold("auth")} ${chalk.gray("--help")} Show auth command help
|
|
1010
1133
|
|
|
1134
|
+
${chalk.green.bold("Diagnostics")}
|
|
1135
|
+
${chalk.blue.bold("doctor")} Detect schema drift between collections, schema, and DB
|
|
1136
|
+
|
|
1011
1137
|
${chalk.green.bold("Options")}
|
|
1012
1138
|
${chalk.blue("--version, -v")} Show version number
|
|
1013
1139
|
${chalk.blue("--help, -h")} Show this help message
|