@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.
Files changed (31) hide show
  1. package/LICENSE +17 -196
  2. package/dist/commands/init.d.ts +1 -1
  3. package/dist/index.cjs +194 -49
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.es.js +179 -33
  6. package/dist/index.es.js.map +1 -1
  7. package/package.json +18 -11
  8. package/templates/template/.cursorrules +2 -0
  9. package/templates/template/.env.example +8 -1
  10. package/templates/template/.github/copilot-instructions.md +2 -0
  11. package/templates/template/.windsurfrules +2 -0
  12. package/templates/template/README.md +3 -3
  13. package/templates/template/ai-instructions.md +14 -0
  14. package/templates/template/backend/Dockerfile +1 -1
  15. package/templates/template/backend/drizzle.config.ts +11 -5
  16. package/templates/template/backend/package.json +4 -3
  17. package/templates/template/backend/src/env.ts +2 -43
  18. package/templates/template/backend/src/index.ts +10 -12
  19. package/templates/template/config/collections/index.ts +6 -4
  20. package/templates/template/config/collections/posts.ts +6 -26
  21. package/templates/template/config/collections/roles.ts +72 -0
  22. package/templates/template/config/collections/users.ts +141 -0
  23. package/templates/template/config/index.ts +1 -1
  24. package/templates/template/config/package.json +2 -1
  25. package/templates/template/docker-compose.yml +3 -3
  26. package/templates/template/frontend/package.json +3 -1
  27. package/templates/template/frontend/src/App.tsx +3 -3
  28. package/templates/template/frontend/src/virtual.d.ts +6 -0
  29. package/templates/template/frontend/tsconfig.json +3 -2
  30. package/templates/template/frontend/vite.config.ts +1 -1
  31. 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("ncp"), 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", "util", "execa", "ncp", "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.util, global.execa, global.ncp, global.url, global.crypto, global.sdkGenerator, global.child_process, global.os));
3
- })(this, (function(exports2, chalk, arg, inquirer, path, fs, util, execa, ncp, url, crypto, sdkGenerator, child_process, os) {
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 copy(options.templateDirectory, options.targetDirectory, {
180
- clobber: false,
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 it with:"));
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 configureEnvFile(targetDirectory, databaseUrl) {
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:5432/rebase`
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
- ...pkg.dependencies,
513
- ...pkg.devDependencies
514
- };
515
- for (const dep of Object.keys(deps)) {
516
- if (dep.startsWith("@rebasepro/server-") && dep !== "@rebasepro/server-core") {
517
- return dep;
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 shared/ directories)."));
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("│ Rebase Admin App is ready! │"));
868
+ console.log(chalk.cyan("│ Rebase Admin App is ready! │"));
805
869
  const cleanUrl = stripAnsi(frontendUrl);
806
- const paddedUrl = cleanUrl.padEnd(40);
807
- console.log(chalk.cyan("│ 👉 Frontend URL: ") + chalk.white(paddedUrl) + 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.commandSync(`taskkill /pid ${child.pid} /T /F`);
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
- const backendChild = execa(
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
- ["watch", `--watch="${path.join("..", "config", "**", "*")}"`, "--conditions", "development", "src/index.ts"],
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-core";
1086
- import { hashPassword } from "@rebasepro/server-core/src/auth/password";
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
- const result = await db.update(users)
1102
- .set({ passwordHash: hash })
1103
- .where(eq(users.email, email))
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: users.id,
1106
- email: users.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