@rebasepro/cli 0.1.2 → 0.2.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/LICENSE +17 -196
- package/dist/commands/init.d.ts +1 -1
- package/dist/index.cjs +194 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +179 -33
- package/dist/index.es.js.map +1 -1
- package/package.json +18 -11
- package/templates/template/.cursorrules +2 -0
- package/templates/template/.env.example +8 -1
- package/templates/template/.github/copilot-instructions.md +2 -0
- package/templates/template/.windsurfrules +2 -0
- package/templates/template/README.md +3 -3
- package/templates/template/ai-instructions.md +14 -0
- package/templates/template/backend/Dockerfile +1 -1
- package/templates/template/backend/drizzle.config.ts +11 -5
- package/templates/template/backend/package.json +4 -3
- package/templates/template/backend/src/env.ts +2 -43
- package/templates/template/backend/src/index.ts +10 -12
- package/templates/template/config/collections/index.ts +6 -4
- package/templates/template/config/collections/posts.ts +6 -26
- package/templates/template/config/collections/roles.ts +72 -0
- package/templates/template/config/collections/users.ts +141 -0
- package/templates/template/config/index.ts +1 -1
- package/templates/template/config/package.json +2 -1
- package/templates/template/docker-compose.yml +3 -3
- package/templates/template/frontend/package.json +3 -1
- package/templates/template/frontend/src/App.tsx +3 -3
- package/templates/template/frontend/src/virtual.d.ts +6 -0
- package/templates/template/frontend/tsconfig.json +3 -2
- package/templates/template/frontend/vite.config.ts +1 -1
- package/templates/template/pnpm-workspace.yaml +7 -0
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function(global, factory) {
|
|
2
|
-
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("chalk"), require("arg"), require("inquirer"), require("path"), require("fs"), require("util"), require("execa"), require("
|
|
3
|
-
})(this, (function(exports2, chalk, arg, inquirer, path, fs, util, execa,
|
|
2
|
+
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("chalk"), require("arg"), require("inquirer"), require("path"), require("fs"), require("net"), require("util"), require("execa"), require("fs/promises"), require("url"), require("crypto"), require("@rebasepro/sdk-generator"), require("child_process"), require("os")) : typeof define === "function" && define.amd ? define(["exports", "chalk", "arg", "inquirer", "path", "fs", "net", "util", "execa", "fs/promises", "url", "crypto", "@rebasepro/sdk-generator", "child_process", "os"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["Rebase CLI"] = {}, global.chalk, global.arg, global.inquirer, global.path, global.fs, global.net, global.util, global.execa, global.promises, global.url, global.crypto, global.sdkGenerator, global.child_process, global.os));
|
|
3
|
+
})(this, (function(exports2, chalk, arg, inquirer, path, fs, net, util, execa, promises, url, crypto, sdkGenerator, child_process, os) {
|
|
4
4
|
"use strict";
|
|
5
5
|
var _documentCurrentScript = typeof document !== "undefined" ? document.currentScript : null;
|
|
6
6
|
function _interopNamespaceDefault(e) {
|
|
@@ -60,7 +60,6 @@
|
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
const access = util.promisify(fs.access);
|
|
63
|
-
const copy = util.promisify(ncp);
|
|
64
63
|
const __filename$2 = 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);
|
|
65
64
|
const __dirname$2 = path.dirname(__filename$2);
|
|
66
65
|
function findParentDir(currentDir, targetName) {
|
|
@@ -87,8 +86,12 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
|
|
|
87
86
|
{
|
|
88
87
|
"--git": Boolean,
|
|
89
88
|
"--install": Boolean,
|
|
89
|
+
"--database-url": String,
|
|
90
|
+
"--introspect": Boolean,
|
|
91
|
+
"--yes": Boolean,
|
|
90
92
|
"-g": "--git",
|
|
91
|
-
"-i": "--install"
|
|
93
|
+
"-i": "--install",
|
|
94
|
+
"-y": "--yes"
|
|
92
95
|
},
|
|
93
96
|
{
|
|
94
97
|
argv: rawArgs.slice(3),
|
|
@@ -97,6 +100,24 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
|
|
|
97
100
|
}
|
|
98
101
|
);
|
|
99
102
|
const nameArg = args._[0];
|
|
103
|
+
const isNonInteractive = args["--yes"] || false;
|
|
104
|
+
if (isNonInteractive) {
|
|
105
|
+
const projectName2 = nameArg || "my-rebase-app";
|
|
106
|
+
const targetDirectory2 = path.resolve(process.cwd(), projectName2);
|
|
107
|
+
const templateDirectory2 = path.resolve(cliRoot, "templates", "template");
|
|
108
|
+
const pmCommands2 = getPMCommands(pm);
|
|
109
|
+
return {
|
|
110
|
+
projectName: path.basename(targetDirectory2),
|
|
111
|
+
git: args["--git"] ?? false,
|
|
112
|
+
installDeps: args["--install"] ?? false,
|
|
113
|
+
targetDirectory: targetDirectory2,
|
|
114
|
+
templateDirectory: templateDirectory2,
|
|
115
|
+
databaseUrl: args["--database-url"] || void 0,
|
|
116
|
+
introspect: args["--introspect"] || false,
|
|
117
|
+
pm,
|
|
118
|
+
pmCommands: pmCommands2
|
|
119
|
+
};
|
|
120
|
+
}
|
|
100
121
|
const questions = [];
|
|
101
122
|
if (!nameArg) {
|
|
102
123
|
questions.push({
|
|
@@ -176,9 +197,8 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
|
|
|
176
197
|
}
|
|
177
198
|
console.log(chalk.gray(" Copying project files..."));
|
|
178
199
|
try {
|
|
179
|
-
await
|
|
180
|
-
|
|
181
|
-
dot: true,
|
|
200
|
+
await promises.cp(options.templateDirectory, options.targetDirectory, {
|
|
201
|
+
recursive: true,
|
|
182
202
|
filter: (source) => {
|
|
183
203
|
const basename = path.basename(source);
|
|
184
204
|
return basename !== "node_modules" && basename !== ".DS_Store";
|
|
@@ -189,11 +209,11 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
|
|
|
189
209
|
process.exit(1);
|
|
190
210
|
}
|
|
191
211
|
await replacePlaceholders(options);
|
|
192
|
-
configureEnvFile(options.targetDirectory, options.databaseUrl);
|
|
212
|
+
await configureEnvFile(options.targetDirectory, options.databaseUrl);
|
|
193
213
|
if (options.git) {
|
|
194
214
|
console.log(chalk.gray(" Initializing git repository..."));
|
|
195
215
|
try {
|
|
196
|
-
await execa("git", ["init"], { cwd: options.targetDirectory });
|
|
216
|
+
await execa.execa("git", ["init"], { cwd: options.targetDirectory });
|
|
197
217
|
} catch {
|
|
198
218
|
console.warn(chalk.yellow(" Warning: Failed to initialize git repository"));
|
|
199
219
|
}
|
|
@@ -206,7 +226,7 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
|
|
|
206
226
|
console.log(chalk.gray(` Installing dependencies with ${pm}...`));
|
|
207
227
|
console.log("");
|
|
208
228
|
try {
|
|
209
|
-
await execa(installCmd[0], installCmd.slice(1), {
|
|
229
|
+
await execa.execa(installCmd[0], installCmd.slice(1), {
|
|
210
230
|
cwd: options.targetDirectory,
|
|
211
231
|
stdio: "inherit"
|
|
212
232
|
});
|
|
@@ -220,7 +240,7 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
|
|
|
220
240
|
console.log(chalk.gray(" Introspecting database and generating collections..."));
|
|
221
241
|
console.log("");
|
|
222
242
|
try {
|
|
223
|
-
await execa(execCmd[0], execCmd.slice(1), {
|
|
243
|
+
await execa.execa(execCmd[0], execCmd.slice(1), {
|
|
224
244
|
cwd: options.targetDirectory,
|
|
225
245
|
stdio: "inherit"
|
|
226
246
|
});
|
|
@@ -249,8 +269,8 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
|
|
|
249
269
|
console.log(chalk.gray(" # Your database is configured! Start the dev server:"));
|
|
250
270
|
} else {
|
|
251
271
|
console.log(chalk.gray(" # A local database configuration has been generated in .env."));
|
|
252
|
-
console.log(chalk.gray(" # If using the included docker-compose.yml, start
|
|
253
|
-
console.log(` ${chalk.cyan("docker compose up -d")}`);
|
|
272
|
+
console.log(chalk.gray(" # If using the included docker-compose.yml, start the database with:"));
|
|
273
|
+
console.log(` ${chalk.cyan("docker compose up -d db")}`);
|
|
254
274
|
console.log("");
|
|
255
275
|
console.log(chalk.gray(" # Then start the dev server:"));
|
|
256
276
|
}
|
|
@@ -270,6 +290,7 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
|
|
|
270
290
|
"backend/package.json",
|
|
271
291
|
"config/package.json",
|
|
272
292
|
"frontend/index.html",
|
|
293
|
+
"pnpm-workspace.yaml",
|
|
273
294
|
"README.md"
|
|
274
295
|
];
|
|
275
296
|
const packageJsonPath = path.resolve(cliRoot, "package.json");
|
|
@@ -282,20 +303,24 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
|
|
|
282
303
|
const viewBin = "npm";
|
|
283
304
|
const getPackageVersion = async (pkgName) => {
|
|
284
305
|
if (versionCache.has(pkgName)) return versionCache.get(pkgName);
|
|
306
|
+
if (process.env.REBASE_E2E === "true") {
|
|
307
|
+
versionCache.set(pkgName, cliVersion);
|
|
308
|
+
return cliVersion;
|
|
309
|
+
}
|
|
285
310
|
let versionToUse = cliVersion;
|
|
286
311
|
try {
|
|
287
|
-
const { stdout } = await execa(viewBin, ["view", `${pkgName}@${cliVersion}`, "version"]);
|
|
312
|
+
const { stdout } = await execa.execa(viewBin, ["view", `${pkgName}@${cliVersion}`, "version"]);
|
|
288
313
|
if (!stdout.trim()) throw new Error("Not found");
|
|
289
314
|
versionToUse = stdout.trim();
|
|
290
315
|
} catch {
|
|
291
316
|
try {
|
|
292
317
|
const tag = cliVersion.includes("canary") ? "canary" : "latest";
|
|
293
|
-
const { stdout } = await execa(viewBin, ["view", `${pkgName}@${tag}`, "version"]);
|
|
318
|
+
const { stdout } = await execa.execa(viewBin, ["view", `${pkgName}@${tag}`, "version"]);
|
|
294
319
|
if (!stdout.trim()) throw new Error("Not found");
|
|
295
320
|
versionToUse = stdout.trim();
|
|
296
321
|
} catch {
|
|
297
322
|
try {
|
|
298
|
-
const { stdout } = await execa(viewBin, ["view", pkgName, "version"]);
|
|
323
|
+
const { stdout } = await execa.execa(viewBin, ["view", pkgName, "version"]);
|
|
299
324
|
versionToUse = stdout.trim() || "latest";
|
|
300
325
|
} catch {
|
|
301
326
|
versionToUse = "latest";
|
|
@@ -330,7 +355,26 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
|
|
|
330
355
|
fs.writeFileSync(fullPath, content, "utf-8");
|
|
331
356
|
}
|
|
332
357
|
}
|
|
333
|
-
function
|
|
358
|
+
async function isPortAvailable(port) {
|
|
359
|
+
return new Promise((resolve) => {
|
|
360
|
+
const server = net.createServer();
|
|
361
|
+
server.once("error", () => {
|
|
362
|
+
resolve(false);
|
|
363
|
+
});
|
|
364
|
+
server.once("listening", () => {
|
|
365
|
+
server.close(() => resolve(true));
|
|
366
|
+
});
|
|
367
|
+
server.listen(port);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
async function findAvailablePort(startPort) {
|
|
371
|
+
let port = startPort;
|
|
372
|
+
while (!await isPortAvailable(port)) {
|
|
373
|
+
port++;
|
|
374
|
+
}
|
|
375
|
+
return port;
|
|
376
|
+
}
|
|
377
|
+
async function configureEnvFile(targetDirectory, databaseUrl) {
|
|
334
378
|
const envExamplePath = path.join(targetDirectory, ".env.example");
|
|
335
379
|
const envPath = path.join(targetDirectory, ".env");
|
|
336
380
|
if (fs.existsSync(envExamplePath) && !fs.existsSync(envPath)) {
|
|
@@ -348,10 +392,21 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
|
|
|
348
392
|
`DATABASE_URL=${databaseUrl}`
|
|
349
393
|
);
|
|
350
394
|
} else {
|
|
395
|
+
const dbPort = await findAvailablePort(5432);
|
|
351
396
|
envContent = envContent.replace(
|
|
352
397
|
/^DATABASE_URL=.*$/m,
|
|
353
|
-
`DATABASE_URL=postgresql://rebase:${dbPassword}@localhost
|
|
398
|
+
`DATABASE_URL=postgresql://rebase:${dbPassword}@localhost:${dbPort}/rebase?options=-c%20search_path=public
|
|
399
|
+
DATABASE_PASSWORD=${dbPassword}`
|
|
354
400
|
);
|
|
401
|
+
const dockerComposePath = path.join(targetDirectory, "docker-compose.yml");
|
|
402
|
+
if (fs.existsSync(dockerComposePath)) {
|
|
403
|
+
let dockerComposeContent = fs.readFileSync(dockerComposePath, "utf-8");
|
|
404
|
+
dockerComposeContent = dockerComposeContent.replace(
|
|
405
|
+
/-\s*"5432:5432"/g,
|
|
406
|
+
`- "${dbPort}:5432"`
|
|
407
|
+
);
|
|
408
|
+
fs.writeFileSync(dockerComposePath, dockerComposeContent, "utf-8");
|
|
409
|
+
}
|
|
355
410
|
}
|
|
356
411
|
fs.writeFileSync(envPath, envContent, "utf-8");
|
|
357
412
|
}
|
|
@@ -449,6 +504,7 @@ Expected a default export of EntityCollection[] or an object with named collecti
|
|
|
449
504
|
console.log("");
|
|
450
505
|
console.log(chalk.cyan(" → Loading collection definitions..."));
|
|
451
506
|
const collections = await loadCollections(resolvedCollectionsDir);
|
|
507
|
+
collections.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
452
508
|
if (collections.length === 0) {
|
|
453
509
|
console.log(chalk.red(" ✗ No collections found. Nothing to generate."));
|
|
454
510
|
process.exit(1);
|
|
@@ -508,15 +564,20 @@ Expected a default export of EntityCollection[] or an object with named collecti
|
|
|
508
564
|
if (!fs.existsSync(pkgPath)) return null;
|
|
509
565
|
try {
|
|
510
566
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
511
|
-
const deps = {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
567
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
568
|
+
const candidates = Object.keys(deps).filter(
|
|
569
|
+
(dep) => dep.startsWith("@rebasepro/server-") && dep !== "@rebasepro/server-core"
|
|
570
|
+
);
|
|
571
|
+
if (candidates.length === 0) return null;
|
|
572
|
+
if (candidates.includes("@rebasepro/server-postgresql")) {
|
|
573
|
+
return "@rebasepro/server-postgresql";
|
|
574
|
+
}
|
|
575
|
+
for (const candidate of candidates) {
|
|
576
|
+
if (resolvePluginCliScript(backendDir, candidate)) {
|
|
577
|
+
return candidate;
|
|
518
578
|
}
|
|
519
579
|
}
|
|
580
|
+
return candidates[0];
|
|
520
581
|
} catch {
|
|
521
582
|
}
|
|
522
583
|
return null;
|
|
@@ -578,7 +639,7 @@ Expected a default export of EntityCollection[] or an object with named collecti
|
|
|
578
639
|
if (!root) {
|
|
579
640
|
console.error(chalk.red("✗ Could not find a Rebase project root."));
|
|
580
641
|
console.error(chalk.gray(" Make sure you are inside a Rebase project directory"));
|
|
581
|
-
console.error(chalk.gray(" (one with backend/, frontend/, and
|
|
642
|
+
console.error(chalk.gray(" (one with backend/, frontend/, and config/ directories)."));
|
|
582
643
|
process.exit(1);
|
|
583
644
|
}
|
|
584
645
|
return root;
|
|
@@ -623,13 +684,13 @@ Expected a default export of EntityCollection[] or an object with named collecti
|
|
|
623
684
|
console.error(chalk.red("✗ Could not find tsx binary."));
|
|
624
685
|
process.exit(1);
|
|
625
686
|
}
|
|
626
|
-
await execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
|
|
687
|
+
await execa.execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
|
|
627
688
|
cwd: backendDir,
|
|
628
689
|
stdio: "inherit",
|
|
629
690
|
env
|
|
630
691
|
});
|
|
631
692
|
} else {
|
|
632
|
-
await execa("node", [pluginCli, ...rawArgs.slice(2)], {
|
|
693
|
+
await execa.execa("node", [pluginCli, ...rawArgs.slice(2)], {
|
|
633
694
|
cwd: backendDir,
|
|
634
695
|
stdio: "inherit",
|
|
635
696
|
env
|
|
@@ -691,13 +752,13 @@ ${chalk.green.bold("introspect Options")}
|
|
|
691
752
|
console.error(chalk.red("✗ Could not find tsx binary."));
|
|
692
753
|
process.exit(1);
|
|
693
754
|
}
|
|
694
|
-
await execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
|
|
755
|
+
await execa.execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
|
|
695
756
|
cwd: backendDir,
|
|
696
757
|
stdio: "inherit",
|
|
697
758
|
env
|
|
698
759
|
});
|
|
699
760
|
} else {
|
|
700
|
-
await execa("node", [pluginCli, ...rawArgs.slice(2)], {
|
|
761
|
+
await execa.execa("node", [pluginCli, ...rawArgs.slice(2)], {
|
|
701
762
|
cwd: backendDir,
|
|
702
763
|
stdio: "inherit",
|
|
703
764
|
env
|
|
@@ -761,10 +822,12 @@ ${chalk.green.bold("Examples")}
|
|
|
761
822
|
"--backend-only": Boolean,
|
|
762
823
|
"--frontend-only": Boolean,
|
|
763
824
|
"--port": Number,
|
|
825
|
+
"--generate": Boolean,
|
|
764
826
|
"--help": Boolean,
|
|
765
827
|
"-b": "--backend-only",
|
|
766
828
|
"-f": "--frontend-only",
|
|
767
829
|
"-p": "--port",
|
|
830
|
+
"-g": "--generate",
|
|
768
831
|
"-h": "--help"
|
|
769
832
|
},
|
|
770
833
|
{
|
|
@@ -782,6 +845,7 @@ ${chalk.green.bold("Examples")}
|
|
|
782
845
|
const frontendDir = findFrontendDir(projectRoot);
|
|
783
846
|
const backendOnly = args["--backend-only"] || false;
|
|
784
847
|
const frontendOnly = args["--frontend-only"] || false;
|
|
848
|
+
const shouldGenerate = args["--generate"] || process.env.REBASE_AUTO_GENERATE === "true" || process.env.REBASE_GENERATE === "true";
|
|
785
849
|
const startPort = resolveStartPort(projectRoot, args["--port"]);
|
|
786
850
|
console.log("");
|
|
787
851
|
console.log(chalk.bold(" 🚀 Rebase Dev Server"));
|
|
@@ -801,10 +865,10 @@ ${chalk.green.bold("Examples")}
|
|
|
801
865
|
console.log("");
|
|
802
866
|
console.log(chalk.cyan("┌────────────────────────────────────────────────────────────┐"));
|
|
803
867
|
console.log(chalk.cyan("│ │"));
|
|
804
|
-
console.log(chalk.cyan("│
|
|
868
|
+
console.log(chalk.cyan("│ ✦ Rebase Admin App is ready! │"));
|
|
805
869
|
const cleanUrl = stripAnsi(frontendUrl);
|
|
806
|
-
const paddedUrl = cleanUrl.padEnd(
|
|
807
|
-
console.log(chalk.cyan("│
|
|
870
|
+
const paddedUrl = cleanUrl.padEnd(41);
|
|
871
|
+
console.log(chalk.cyan("│ ➜ Frontend URL: ") + chalk.white(paddedUrl) + chalk.cyan("│"));
|
|
808
872
|
console.log(chalk.cyan("│ │"));
|
|
809
873
|
console.log(chalk.cyan("└────────────────────────────────────────────────────────────┘"));
|
|
810
874
|
console.log("");
|
|
@@ -823,7 +887,7 @@ ${chalk.green.bold("Examples")}
|
|
|
823
887
|
if (child.pid && !child.killed) {
|
|
824
888
|
try {
|
|
825
889
|
if (process.platform === "win32") {
|
|
826
|
-
execa.
|
|
890
|
+
execa.execaCommandSync(`taskkill /pid ${child.pid} /T /F`);
|
|
827
891
|
} else {
|
|
828
892
|
process.kill(-child.pid, "SIGKILL");
|
|
829
893
|
}
|
|
@@ -850,7 +914,7 @@ ${chalk.green.bold("Examples")}
|
|
|
850
914
|
const pm = detectPackageManager(projectRoot);
|
|
851
915
|
const pmCmds = getPMCommands(pm);
|
|
852
916
|
const runDevCmd = pmCmds.run("dev");
|
|
853
|
-
const frontendChild = execa(
|
|
917
|
+
const frontendChild = execa.execa(
|
|
854
918
|
runDevCmd[0],
|
|
855
919
|
runDevCmd.slice(1),
|
|
856
920
|
{
|
|
@@ -901,9 +965,67 @@ ${chalk.green.bold("Examples")}
|
|
|
901
965
|
console.log(` ${chalk.cyan("▶")} Backend: ${chalk.gray(backendDir)}`);
|
|
902
966
|
console.log(` ${chalk.gray("↳ PORT")} = ${chalk.white(String(startPort))}`);
|
|
903
967
|
let frontendLaunched = false;
|
|
904
|
-
|
|
968
|
+
if (shouldGenerate) {
|
|
969
|
+
console.log(chalk.gray(" → Ensuring schema and SDK are generated on start..."));
|
|
970
|
+
try {
|
|
971
|
+
const activePlugin = getActiveBackendPlugin(backendDir);
|
|
972
|
+
const pluginCli = activePlugin ? resolvePluginCliScript(backendDir, activePlugin) : null;
|
|
973
|
+
if (pluginCli) {
|
|
974
|
+
await execa.execa(tsxBin, [pluginCli, "schema", "generate"], {
|
|
975
|
+
cwd: backendDir,
|
|
976
|
+
stdio: "inherit",
|
|
977
|
+
env
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
await execa.execa("npx", ["rebase", "generate-sdk"], {
|
|
981
|
+
cwd: projectRoot,
|
|
982
|
+
stdio: "inherit",
|
|
983
|
+
env
|
|
984
|
+
});
|
|
985
|
+
console.log(chalk.green(" ✓ Initial schema and SDK generated successfully.\n"));
|
|
986
|
+
} catch (err) {
|
|
987
|
+
console.error(chalk.red(` ✗ Initial schema/SDK generation failed: ${err.message || err}
|
|
988
|
+
`));
|
|
989
|
+
}
|
|
990
|
+
const collectionsDir = path.join(projectRoot, "config", "collections");
|
|
991
|
+
if (fs.existsSync(collectionsDir)) {
|
|
992
|
+
let watchDebounce = null;
|
|
993
|
+
fs.watch(collectionsDir, { recursive: true }, (eventType, filename) => {
|
|
994
|
+
if (!filename || filename.startsWith(".") || filename.endsWith(".tmp")) return;
|
|
995
|
+
if (watchDebounce) clearTimeout(watchDebounce);
|
|
996
|
+
watchDebounce = setTimeout(async () => {
|
|
997
|
+
console.log(chalk.yellow(`
|
|
998
|
+
🔄 Collection change detected (${filename}). Regenerating schema & SDK...`));
|
|
999
|
+
try {
|
|
1000
|
+
const activePlugin = getActiveBackendPlugin(backendDir);
|
|
1001
|
+
const pluginCli = activePlugin ? resolvePluginCliScript(backendDir, activePlugin) : null;
|
|
1002
|
+
if (pluginCli) {
|
|
1003
|
+
await execa.execa(tsxBin, [pluginCli, "schema", "generate"], {
|
|
1004
|
+
cwd: backendDir,
|
|
1005
|
+
stdio: "inherit",
|
|
1006
|
+
env
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
await execa.execa("npx", ["rebase", "generate-sdk"], {
|
|
1010
|
+
cwd: projectRoot,
|
|
1011
|
+
stdio: "inherit",
|
|
1012
|
+
env
|
|
1013
|
+
});
|
|
1014
|
+
console.log(chalk.green(" ✓ Schema & SDK regenerated successfully. Hono will reload."));
|
|
1015
|
+
} catch (err) {
|
|
1016
|
+
console.error(chalk.red(` ✗ Failed to regenerate schema/SDK: ${err.message || err}`));
|
|
1017
|
+
}
|
|
1018
|
+
}, 300);
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
const watchArgs = ["watch", "--conditions", "development", "src/index.ts"];
|
|
1023
|
+
if (!shouldGenerate) {
|
|
1024
|
+
watchArgs.splice(1, 0, `--watch="${path.join("..", "config", "**", "*")}"`);
|
|
1025
|
+
}
|
|
1026
|
+
const backendChild = execa.execa(
|
|
905
1027
|
tsxBin,
|
|
906
|
-
|
|
1028
|
+
watchArgs,
|
|
907
1029
|
{
|
|
908
1030
|
cwd: backendDir,
|
|
909
1031
|
stdio: ["inherit", "pipe", "pipe"],
|
|
@@ -976,6 +1098,7 @@ ${chalk.green.bold("Options")}
|
|
|
976
1098
|
${chalk.blue("--backend-only, -b")} Only start the backend server
|
|
977
1099
|
${chalk.blue("--frontend-only, -f")} Only start the frontend server
|
|
978
1100
|
${chalk.blue("--port, -p")} Backend port (default: auto-detected per project)
|
|
1101
|
+
${chalk.blue("--generate, -g")} Enable automatic schema and SDK generation on startup and file changes
|
|
979
1102
|
|
|
980
1103
|
${chalk.green.bold("Description")}
|
|
981
1104
|
Starts both the backend (tsx watch + Hono) and frontend (Vite)
|
|
@@ -988,6 +1111,10 @@ ${chalk.green.bold("Description")}
|
|
|
988
1111
|
If the assigned port is already in use, the server will automatically
|
|
989
1112
|
try the next available port. The frontend is started only after the
|
|
990
1113
|
backend is ready, and VITE_API_URL is injected automatically.
|
|
1114
|
+
|
|
1115
|
+
By default, automatic schema and SDK generation is disabled on startup
|
|
1116
|
+
and file changes. Pass --generate (-g) or set REBASE_AUTO_GENERATE=true
|
|
1117
|
+
in your environment to enable it.
|
|
991
1118
|
`);
|
|
992
1119
|
}
|
|
993
1120
|
async function buildCommand() {
|
|
@@ -998,7 +1125,7 @@ ${chalk.green.bold("Description")}
|
|
|
998
1125
|
console.log(`${chalk.bold("Rebase")} — Building all workspaces with ${chalk.cyan(pm)}...
|
|
999
1126
|
`);
|
|
1000
1127
|
try {
|
|
1001
|
-
await execa(buildCmd[0], buildCmd.slice(1), {
|
|
1128
|
+
await execa.execa(buildCmd[0], buildCmd.slice(1), {
|
|
1002
1129
|
cwd: projectRoot,
|
|
1003
1130
|
stdio: "inherit"
|
|
1004
1131
|
});
|
|
@@ -1020,7 +1147,7 @@ ${chalk.green.bold("Description")}
|
|
|
1020
1147
|
console.log(`${chalk.bold("Rebase")} — Starting backend server...
|
|
1021
1148
|
`);
|
|
1022
1149
|
try {
|
|
1023
|
-
await execa(startCmd[0], startCmd.slice(1), {
|
|
1150
|
+
await execa.execa(startCmd[0], startCmd.slice(1), {
|
|
1024
1151
|
cwd: projectRoot,
|
|
1025
1152
|
stdio: "inherit",
|
|
1026
1153
|
env
|
|
@@ -1082,12 +1209,12 @@ ${chalk.green.bold("Description")}
|
|
|
1082
1209
|
env.DOTENV_CONFIG_PATH = envFile;
|
|
1083
1210
|
}
|
|
1084
1211
|
const scriptContent = `
|
|
1085
|
-
import { createPostgresDatabaseConnection } from "@rebasepro/server-
|
|
1086
|
-
import { hashPassword } from "@rebasepro/server-core
|
|
1212
|
+
import { createPostgresDatabaseConnection } from "@rebasepro/server-postgresql";
|
|
1213
|
+
import { hashPassword } from "@rebasepro/server-core";
|
|
1087
1214
|
import { eq } from "drizzle-orm";
|
|
1088
|
-
import { users } from "@rebasepro/server-core/src/db/auth-schema";
|
|
1089
1215
|
import * as dotenv from "dotenv";
|
|
1090
1216
|
import path from "path";
|
|
1217
|
+
import fs from "fs";
|
|
1091
1218
|
|
|
1092
1219
|
dotenv.config({ path: "${envFile || path.join(projectRoot, ".env")}" });
|
|
1093
1220
|
|
|
@@ -1098,12 +1225,30 @@ async function resetPassword() {
|
|
|
1098
1225
|
const { db } = createPostgresDatabaseConnection(process.env.DATABASE_URL!);
|
|
1099
1226
|
const hash = await hashPassword(newPassword);
|
|
1100
1227
|
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
.
|
|
1228
|
+
let usersTable;
|
|
1229
|
+
try {
|
|
1230
|
+
const schemaPath = path.resolve("./src/schema.generated.ts");
|
|
1231
|
+
if (fs.existsSync(schemaPath)) {
|
|
1232
|
+
const schema = await import("file://" + schemaPath);
|
|
1233
|
+
usersTable = schema.users || schema.tables?.users;
|
|
1234
|
+
}
|
|
1235
|
+
} catch (e) {
|
|
1236
|
+
// ignore and fallback
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
if (!usersTable) {
|
|
1240
|
+
const pgServer = await import("@rebasepro/server-postgresql");
|
|
1241
|
+
usersTable = pgServer.users;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
const passwordHashKey = (usersTable.passwordHash || "passwordHash" in usersTable) ? "passwordHash" : "password_hash";
|
|
1245
|
+
|
|
1246
|
+
const result = await db.update(usersTable)
|
|
1247
|
+
.set({ [passwordHashKey]: hash })
|
|
1248
|
+
.where(eq(usersTable.email, email))
|
|
1104
1249
|
.returning({
|
|
1105
|
-
id:
|
|
1106
|
-
email:
|
|
1250
|
+
id: usersTable.id,
|
|
1251
|
+
email: usersTable.email
|
|
1107
1252
|
});
|
|
1108
1253
|
|
|
1109
1254
|
if (result.length > 0) {
|
|
@@ -1191,13 +1336,13 @@ ${chalk.green.bold("Examples")}
|
|
|
1191
1336
|
console.error(chalk.red("✗ Could not find tsx binary."));
|
|
1192
1337
|
process.exit(1);
|
|
1193
1338
|
}
|
|
1194
|
-
await execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
|
|
1339
|
+
await execa.execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
|
|
1195
1340
|
cwd: backendDir,
|
|
1196
1341
|
stdio: "inherit",
|
|
1197
1342
|
env
|
|
1198
1343
|
});
|
|
1199
1344
|
} else {
|
|
1200
|
-
await execa("node", [pluginCli, ...rawArgs.slice(2)], {
|
|
1345
|
+
await execa.execa("node", [pluginCli, ...rawArgs.slice(2)], {
|
|
1201
1346
|
cwd: backendDir,
|
|
1202
1347
|
stdio: "inherit",
|
|
1203
1348
|
env
|