@rebasepro/cli 0.0.1-canary.2 → 0.0.1-canary.4d4fb3e

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/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("os")) : typeof define === "function" && define.amd ? define(["exports", "chalk", "arg", "inquirer", "path", "fs", "util", "execa", "ncp", "url", "crypto", "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.os));
3
- })(this, (function(exports2, chalk, arg, inquirer, path, fs, util, execa, ncp, url, crypto, os) {
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) {
4
4
  "use strict";
5
5
  var _documentCurrentScript = typeof document !== "undefined" ? document.currentScript : null;
6
6
  function _interopNamespaceDefault(e) {
@@ -46,7 +46,9 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
46
46
  const args = arg(
47
47
  {
48
48
  "--git": Boolean,
49
- "-g": "--git"
49
+ "--install": Boolean,
50
+ "-g": "--git",
51
+ "-i": "--install"
50
52
  },
51
53
  {
52
54
  argv: rawArgs.slice(3),
@@ -79,6 +81,14 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
79
81
  default: true
80
82
  });
81
83
  }
84
+ if (!args["--install"]) {
85
+ questions.push({
86
+ type: "confirm",
87
+ name: "installDeps",
88
+ message: "Install dependencies with pnpm?",
89
+ default: true
90
+ });
91
+ }
82
92
  const answers = await inquirer.prompt(questions);
83
93
  const projectName = nameArg || answers.projectName;
84
94
  const targetDirectory = path.resolve(process.cwd(), projectName);
@@ -86,6 +96,7 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
86
96
  return {
87
97
  projectName,
88
98
  git: args["--git"] || answers.git || false,
99
+ installDeps: args["--install"] || answers.installDeps || false,
89
100
  targetDirectory,
90
101
  templateDirectory
91
102
  };
@@ -106,14 +117,19 @@ ${chalk.bold("Rebase")} — Create a new project 🚀
106
117
  process.exit(1);
107
118
  }
108
119
  console.log(chalk.gray(" Copying project files..."));
109
- await copy(options.templateDirectory, options.targetDirectory, {
110
- clobber: false,
111
- dot: true,
112
- filter: (source) => {
113
- const basename = path.basename(source);
114
- return basename !== "node_modules" && basename !== ".DS_Store";
115
- }
116
- });
120
+ try {
121
+ await copy(options.templateDirectory, options.targetDirectory, {
122
+ clobber: false,
123
+ dot: true,
124
+ filter: (source) => {
125
+ const basename = path.basename(source);
126
+ return basename !== "node_modules" && basename !== ".DS_Store";
127
+ }
128
+ });
129
+ } catch (err) {
130
+ console.error(`${chalk.red.bold("ERROR")} Failed to copy template files: ${err instanceof Error ? err.message : String(err)}`);
131
+ process.exit(1);
132
+ }
117
133
  await replacePlaceholders(options);
118
134
  const envTemplatePath = path.join(options.targetDirectory, ".env.template");
119
135
  const envPath = path.join(options.targetDirectory, ".env");
@@ -144,13 +160,28 @@ POSTGRES_PASSWORD=${dbPassword}
144
160
  console.warn(chalk.yellow(" Warning: Failed to initialize git repository"));
145
161
  }
146
162
  }
163
+ if (options.installDeps) {
164
+ console.log("");
165
+ console.log(chalk.gray(" Installing dependencies with pnpm..."));
166
+ console.log("");
167
+ try {
168
+ await execa("pnpm", ["install"], {
169
+ cwd: options.targetDirectory,
170
+ stdio: "inherit"
171
+ });
172
+ } catch {
173
+ console.warn(chalk.yellow(" Warning: Failed to install dependencies. You may need to run `pnpm install` manually."));
174
+ }
175
+ }
147
176
  console.log("");
148
177
  console.log(`${chalk.green.bold("✓")} Project ${chalk.bold(options.projectName)} created successfully!`);
149
178
  console.log("");
150
179
  console.log(chalk.bold("Next steps:"));
151
180
  console.log("");
152
181
  console.log(` ${chalk.cyan("cd")} ${options.projectName}`);
153
- console.log(` ${chalk.cyan("pnpm install")}`);
182
+ if (!options.installDeps) {
183
+ console.log(` ${chalk.cyan("pnpm install")}`);
184
+ }
154
185
  console.log("");
155
186
  console.log(chalk.gray(" # Set up your database connection in .env"));
156
187
  console.log(chalk.gray(" # Then run:"));
@@ -160,7 +191,7 @@ POSTGRES_PASSWORD=${dbPassword}
160
191
  console.log(chalk.gray("This starts both the backend (Express + PostgreSQL)") + chalk.gray(" and the frontend (Vite + React) concurrently."));
161
192
  console.log("");
162
193
  console.log(chalk.gray("Docs: https://rebase.pro/docs"));
163
- console.log(chalk.gray("GitHub: https://github.com/rebaseco/rebase"));
194
+ console.log(chalk.gray("GitHub: https://github.com/rebasepro/rebase"));
164
195
  console.log("");
165
196
  }
166
197
  async function replacePlaceholders(options) {
@@ -179,6 +210,696 @@ POSTGRES_PASSWORD=${dbPassword}
179
210
  fs.writeFileSync(fullPath, content, "utf-8");
180
211
  }
181
212
  }
213
+ async function loadCollections(collectionsDir) {
214
+ const absDir = path.resolve(collectionsDir);
215
+ if (!fs.existsSync(absDir)) {
216
+ throw new Error(`Collections directory not found: ${absDir}`);
217
+ }
218
+ let jiti;
219
+ try {
220
+ const jitiModule = await import("jiti");
221
+ jiti = jitiModule.default || jitiModule;
222
+ } catch {
223
+ throw new Error(
224
+ "Could not load 'jiti'. Install it with: pnpm add -D jiti\njiti is required to dynamically import TypeScript collection definitions."
225
+ );
226
+ }
227
+ const jitiInstance = jiti(absDir, {
228
+ interopDefault: true,
229
+ esmResolve: true
230
+ });
231
+ const indexCandidates = ["index.ts", "index.js", "index.mjs"];
232
+ let indexPath = null;
233
+ for (const candidate of indexCandidates) {
234
+ const p = path.join(absDir, candidate);
235
+ if (fs.existsSync(p)) {
236
+ indexPath = p;
237
+ break;
238
+ }
239
+ }
240
+ if (!indexPath) {
241
+ console.log(chalk.yellow(" No index file found, scanning individual collection files..."));
242
+ const collections = [];
243
+ const files = fs.readdirSync(absDir).filter(
244
+ (f) => (f.endsWith(".ts") || f.endsWith(".js")) && !f.startsWith(".")
245
+ );
246
+ for (const file of files) {
247
+ try {
248
+ const mod2 = jitiInstance(path.join(absDir, file));
249
+ const exported2 = mod2.default || mod2;
250
+ if (exported2 && typeof exported2 === "object" && "slug" in exported2) {
251
+ collections.push(exported2);
252
+ } else if (Array.isArray(exported2)) {
253
+ collections.push(...exported2);
254
+ }
255
+ } catch (err) {
256
+ console.warn(chalk.yellow(` ⚠ Skipping ${file}: ${err.message}`));
257
+ }
258
+ }
259
+ return collections;
260
+ }
261
+ const mod = jitiInstance(indexPath);
262
+ const exported = mod.default || mod;
263
+ if (Array.isArray(exported)) {
264
+ return exported;
265
+ } else if (typeof exported === "object" && exported !== null) {
266
+ if ("collections" in exported && Array.isArray(exported.collections)) {
267
+ return exported.collections;
268
+ }
269
+ const collections = [];
270
+ for (const value of Object.values(exported)) {
271
+ if (value && typeof value === "object" && "slug" in value) {
272
+ collections.push(value);
273
+ }
274
+ }
275
+ if (collections.length > 0) return collections;
276
+ }
277
+ throw new Error(
278
+ `Could not extract collections from ${indexPath}.
279
+ Expected a default export of EntityCollection[] or an object with named collection exports.`
280
+ );
281
+ }
282
+ function writeFiles(outputDir, files) {
283
+ const absOutput = path.resolve(outputDir);
284
+ fs.mkdirSync(absOutput, { recursive: true });
285
+ for (const file of files) {
286
+ const filePath = path.join(absOutput, file.path);
287
+ const dir = path.dirname(filePath);
288
+ if (!fs.existsSync(dir)) {
289
+ fs.mkdirSync(dir, { recursive: true });
290
+ }
291
+ fs.writeFileSync(filePath, file.content, "utf-8");
292
+ }
293
+ }
294
+ async function generateSdkCommand(args) {
295
+ const { collectionsDir, output, cwd } = args;
296
+ const resolvedCollectionsDir = path.isAbsolute(collectionsDir) ? collectionsDir : path.join(cwd, collectionsDir);
297
+ const resolvedOutput = path.isAbsolute(output) ? output : path.join(cwd, output);
298
+ console.log("");
299
+ console.log(chalk.bold(" 🔧 Rebase SDK Generator"));
300
+ console.log("");
301
+ console.log(` ${chalk.gray("Collections:")} ${resolvedCollectionsDir}`);
302
+ console.log(` ${chalk.gray("Output:")} ${resolvedOutput}`);
303
+ console.log("");
304
+ console.log(chalk.cyan(" → Loading collection definitions..."));
305
+ const collections = await loadCollections(resolvedCollectionsDir);
306
+ if (collections.length === 0) {
307
+ console.log(chalk.red(" ✗ No collections found. Nothing to generate."));
308
+ process.exit(1);
309
+ }
310
+ console.log(chalk.green(` ✓ Found ${collections.length} collection(s): ${collections.map((c) => c.slug).join(", ")}`));
311
+ console.log("");
312
+ console.log(chalk.cyan(" → Generating SDK files..."));
313
+ const files = sdkGenerator.generateSDK(collections);
314
+ console.log(chalk.green(` ✓ Generated ${files.length} file(s)`));
315
+ console.log(chalk.cyan(` → Writing to ${resolvedOutput}...`));
316
+ writeFiles(resolvedOutput, files);
317
+ console.log("");
318
+ console.log(chalk.green.bold(" ✓ SDK generated successfully!"));
319
+ console.log("");
320
+ console.log(chalk.gray(" Usage:"));
321
+ console.log(chalk.gray(` import { createRebaseClient } from '@rebasepro/client';`));
322
+ console.log(chalk.gray(` import type { Database } from './${path.relative(cwd, path.join(resolvedOutput, "database.types"))}';`));
323
+ console.log("");
324
+ console.log(chalk.gray(" const rebase = createRebaseClient<Database>({"));
325
+ console.log(chalk.gray(" baseUrl: 'http://localhost:3001',"));
326
+ console.log(chalk.gray(" // token: 'your-jwt-token',"));
327
+ console.log(chalk.gray(" });"));
328
+ console.log("");
329
+ console.log(chalk.gray(` const { data } = await rebase.collection('${collections[0]?.slug || "my_collection"}').find();`));
330
+ console.log("");
331
+ }
332
+ function findProjectRoot(startDir = process.cwd()) {
333
+ let dir = path.resolve(startDir);
334
+ const root = path.parse(dir).root;
335
+ while (dir !== root) {
336
+ const pkgPath = path.join(dir, "package.json");
337
+ if (fs.existsSync(pkgPath)) {
338
+ try {
339
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
340
+ if (pkg.workspaces && Array.isArray(pkg.workspaces)) {
341
+ const hasBackend = pkg.workspaces.some(
342
+ (w) => w === "backend" || w.includes("backend")
343
+ );
344
+ if (hasBackend) return dir;
345
+ }
346
+ } catch {
347
+ }
348
+ if (fs.existsSync(path.join(dir, "backend")) && fs.existsSync(path.join(dir, "shared"))) {
349
+ return dir;
350
+ }
351
+ }
352
+ dir = path.dirname(dir);
353
+ }
354
+ return null;
355
+ }
356
+ function findBackendDir(projectRoot) {
357
+ const backendDir = path.join(projectRoot, "backend");
358
+ return fs.existsSync(backendDir) ? backendDir : null;
359
+ }
360
+ function getActiveBackendPlugin(backendDir) {
361
+ const pkgPath = path.join(backendDir, "package.json");
362
+ if (!fs.existsSync(pkgPath)) return null;
363
+ try {
364
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
365
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
366
+ for (const dep of Object.keys(deps)) {
367
+ if (dep.startsWith("@rebasepro/server-") && dep !== "@rebasepro/server-core") {
368
+ return dep;
369
+ }
370
+ }
371
+ } catch {
372
+ }
373
+ return null;
374
+ }
375
+ function resolvePluginCliScript(backendDir, pluginName) {
376
+ const candidates = [
377
+ path.join(backendDir, "node_modules", pluginName, "src", "cli.ts"),
378
+ path.join(backendDir, "node_modules", pluginName, "dist", "cli.js"),
379
+ // For monorepo dev mode:
380
+ path.resolve(backendDir, "..", "..", "packages", pluginName.replace("@rebasepro/", ""), "src", "cli.ts"),
381
+ path.resolve(backendDir, "..", "packages", pluginName.replace("@rebasepro/", ""), "src", "cli.ts")
382
+ ];
383
+ for (const candidate of candidates) {
384
+ if (fs.existsSync(candidate)) return candidate;
385
+ }
386
+ return null;
387
+ }
388
+ function findFrontendDir(projectRoot) {
389
+ const frontendDir = path.join(projectRoot, "frontend");
390
+ return fs.existsSync(frontendDir) ? frontendDir : null;
391
+ }
392
+ function findEnvFile(projectRoot) {
393
+ const candidates = [
394
+ path.join(projectRoot, ".env"),
395
+ path.join(projectRoot, "backend", ".env")
396
+ ];
397
+ for (const candidate of candidates) {
398
+ if (fs.existsSync(candidate)) return candidate;
399
+ }
400
+ return null;
401
+ }
402
+ function resolveLocalBin(projectRoot, binName) {
403
+ const candidates = [
404
+ path.join(projectRoot, "backend", "node_modules", ".bin", binName),
405
+ path.join(projectRoot, "node_modules", ".bin", binName)
406
+ ];
407
+ let parent = path.dirname(projectRoot);
408
+ const rootDir = path.parse(parent).root;
409
+ while (parent !== rootDir) {
410
+ candidates.push(path.join(parent, "node_modules", ".bin", binName));
411
+ parent = path.dirname(parent);
412
+ }
413
+ for (const candidate of candidates) {
414
+ if (fs.existsSync(candidate)) return candidate;
415
+ }
416
+ try {
417
+ const globalPath = child_process.execSync(`which ${binName}`, { encoding: "utf-8" }).trim();
418
+ if (globalPath && fs.existsSync(globalPath)) return globalPath;
419
+ } catch {
420
+ }
421
+ return null;
422
+ }
423
+ function resolveTsx(projectRoot) {
424
+ return resolveLocalBin(projectRoot, "tsx");
425
+ }
426
+ function requireProjectRoot() {
427
+ const root = findProjectRoot();
428
+ if (!root) {
429
+ console.error(chalk.red("✗ Could not find a Rebase project root."));
430
+ console.error(chalk.gray(" Make sure you are inside a Rebase project directory"));
431
+ console.error(chalk.gray(" (one with backend/, frontend/, and shared/ directories)."));
432
+ process.exit(1);
433
+ }
434
+ return root;
435
+ }
436
+ function requireBackendDir(projectRoot) {
437
+ const backendDir = findBackendDir(projectRoot);
438
+ if (!backendDir) {
439
+ console.error(chalk.red("✗ Could not find a backend/ directory."));
440
+ console.error(chalk.gray(` Expected at: ${path.join(projectRoot, "backend")}`));
441
+ process.exit(1);
442
+ }
443
+ return backendDir;
444
+ }
445
+ async function schemaCommand(subcommand, rawArgs) {
446
+ if (!subcommand || subcommand === "--help") {
447
+ printSchemaHelp();
448
+ return;
449
+ }
450
+ const projectRoot = requireProjectRoot();
451
+ const backendDir = requireBackendDir(projectRoot);
452
+ const activePlugin = getActiveBackendPlugin(backendDir);
453
+ if (!activePlugin) {
454
+ console.error(chalk.red("✗ Could not detect an active database plugin."));
455
+ console.error(chalk.gray(" Make sure a package like @rebasepro/server-postgresql is installed in backend/package.json."));
456
+ process.exit(1);
457
+ }
458
+ const pluginCli = resolvePluginCliScript(backendDir, activePlugin);
459
+ if (!pluginCli) {
460
+ console.error(chalk.red(`✗ Could not find CLI entry point for ${activePlugin}.`));
461
+ process.exit(1);
462
+ }
463
+ const envFile = findEnvFile(projectRoot);
464
+ const env = { ...process.env };
465
+ if (envFile) {
466
+ env.DOTENV_CONFIG_PATH = envFile;
467
+ }
468
+ try {
469
+ const isTs = pluginCli.endsWith(".ts");
470
+ if (isTs) {
471
+ const tsxBin = resolveTsx(projectRoot);
472
+ if (!tsxBin) {
473
+ console.error(chalk.red("✗ Could not find tsx binary."));
474
+ process.exit(1);
475
+ }
476
+ await execa(tsxBin, [pluginCli, "schema", subcommand, ...rawArgs.slice(2)], {
477
+ cwd: backendDir,
478
+ stdio: "inherit",
479
+ env
480
+ });
481
+ } else {
482
+ await execa("node", [pluginCli, "schema", subcommand, ...rawArgs.slice(2)], {
483
+ cwd: backendDir,
484
+ stdio: "inherit",
485
+ env
486
+ });
487
+ }
488
+ } catch (err) {
489
+ process.exit(1);
490
+ }
491
+ }
492
+ function printSchemaHelp() {
493
+ console.log(`
494
+ ${chalk.bold("rebase schema")} — Schema management commands
495
+
496
+ ${chalk.green.bold("Usage")}
497
+ rebase schema ${chalk.blue("<command>")} [options]
498
+
499
+ ${chalk.green.bold("Commands")}
500
+ ${chalk.gray("(Commands are provided by your active database driver plugin)")}
501
+ ${chalk.blue.bold("generate")} Generate Schema from collection definitions
502
+
503
+ ${chalk.green.bold("generate Options")}
504
+ ${chalk.blue("--collections, -c")} Path to collections directory
505
+ ${chalk.blue("--output, -o")} Output path for generated schema
506
+ ${chalk.blue("--watch, -w")} Watch for changes and regenerate automatically
507
+ `);
508
+ }
509
+ async function dbCommand(subcommand, rawArgs) {
510
+ if (!subcommand || subcommand === "--help") {
511
+ printDbHelp();
512
+ return;
513
+ }
514
+ const projectRoot = requireProjectRoot();
515
+ const backendDir = requireBackendDir(projectRoot);
516
+ const activePlugin = getActiveBackendPlugin(backendDir);
517
+ if (!activePlugin) {
518
+ console.error(chalk.red("✗ Could not detect an active database plugin."));
519
+ console.error(chalk.gray(" Make sure a package like @rebasepro/server-postgresql is installed in backend/package.json."));
520
+ process.exit(1);
521
+ }
522
+ const pluginCli = resolvePluginCliScript(backendDir, activePlugin);
523
+ if (!pluginCli) {
524
+ console.error(chalk.red(`✗ Could not find CLI entry point for ${activePlugin}.`));
525
+ process.exit(1);
526
+ }
527
+ const envFile = findEnvFile(projectRoot);
528
+ const env = { ...process.env };
529
+ if (envFile) {
530
+ env.DOTENV_CONFIG_PATH = envFile;
531
+ }
532
+ try {
533
+ const isTs = pluginCli.endsWith(".ts");
534
+ if (isTs) {
535
+ const tsxBin = resolveTsx(projectRoot);
536
+ if (!tsxBin) {
537
+ console.error(chalk.red("✗ Could not find tsx binary."));
538
+ process.exit(1);
539
+ }
540
+ await execa(tsxBin, [pluginCli, "db", subcommand, ...rawArgs.slice(2)], {
541
+ cwd: backendDir,
542
+ stdio: "inherit",
543
+ env
544
+ });
545
+ } else {
546
+ await execa("node", [pluginCli, "db", subcommand, ...rawArgs.slice(2)], {
547
+ cwd: backendDir,
548
+ stdio: "inherit",
549
+ env
550
+ });
551
+ }
552
+ } catch (err) {
553
+ process.exit(1);
554
+ }
555
+ }
556
+ function printDbHelp() {
557
+ console.log(`
558
+ ${chalk.bold("rebase db")} — Database management commands
559
+
560
+ ${chalk.green.bold("Usage")}
561
+ rebase db ${chalk.blue("<command>")} [options]
562
+
563
+ ${chalk.green.bold("Commands")}
564
+ ${chalk.gray("(Commands are provided by your active database driver plugin)")}
565
+ ${chalk.blue.bold("push")} Apply schema directly to database (development)
566
+ ${chalk.blue.bold("pull")} Introspect database → Schema
567
+ ${chalk.blue.bold("generate")} Generate migration files
568
+ ${chalk.blue.bold("migrate")} Run pending migrations
569
+ ${chalk.blue.bold("studio")} Open Studio viewer
570
+ ${chalk.blue.bold("branch")} Database branching (create, list, delete, info)
571
+
572
+ ${chalk.green.bold("Examples")}
573
+ ${chalk.gray("# Quick development workflow")}
574
+ rebase schema generate && rebase db push
575
+
576
+ ${chalk.gray("# Production migration workflow")}
577
+ rebase db generate
578
+ rebase db migrate
579
+
580
+ ${chalk.gray("# Create a database branch")}
581
+ rebase db branch create feature_auth
582
+ `);
583
+ }
584
+ async function devCommand(rawArgs) {
585
+ const args = arg(
586
+ {
587
+ "--backend-only": Boolean,
588
+ "--frontend-only": Boolean,
589
+ "--port": Number,
590
+ "--help": Boolean,
591
+ "-b": "--backend-only",
592
+ "-f": "--frontend-only",
593
+ "-p": "--port",
594
+ "-h": "--help"
595
+ },
596
+ {
597
+ argv: rawArgs.slice(3),
598
+ // skip "node rebase dev"
599
+ permissive: true
600
+ }
601
+ );
602
+ if (args["--help"]) {
603
+ printDevHelp();
604
+ return;
605
+ }
606
+ const projectRoot = requireProjectRoot();
607
+ const backendDir = findBackendDir(projectRoot);
608
+ const frontendDir = findFrontendDir(projectRoot);
609
+ const backendOnly = args["--backend-only"] || false;
610
+ const frontendOnly = args["--frontend-only"] || false;
611
+ console.log("");
612
+ console.log(chalk.bold(" 🚀 Rebase Dev Server"));
613
+ console.log("");
614
+ const children = [];
615
+ let frontendUrl = "";
616
+ let backendUrl = "";
617
+ let debounceSummary = null;
618
+ let bannerPrinted = false;
619
+ const stripAnsi = (str) => str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
620
+ function printSummary() {
621
+ if (!frontendUrl || !backendUrl) return;
622
+ if (debounceSummary) clearTimeout(debounceSummary);
623
+ debounceSummary = setTimeout(() => {
624
+ if (bannerPrinted) return;
625
+ console.log("");
626
+ console.log(chalk.cyan("┌────────────────────────────────────────────────────────────┐"));
627
+ console.log(chalk.cyan("│ │"));
628
+ console.log(chalk.cyan("│ ✨ Rebase Admin App is ready! │"));
629
+ const cleanUrl = stripAnsi(frontendUrl);
630
+ const paddedUrl = cleanUrl.padEnd(40);
631
+ console.log(chalk.cyan(`│ 👉 Frontend URL: `) + chalk.white(paddedUrl) + chalk.cyan(`│`));
632
+ console.log(chalk.cyan("│ │"));
633
+ console.log(chalk.cyan("└────────────────────────────────────────────────────────────┘"));
634
+ console.log("");
635
+ bannerPrinted = true;
636
+ }, 500);
637
+ }
638
+ const cleanup = () => {
639
+ children.forEach((child) => {
640
+ if (!child.killed) {
641
+ child.kill("SIGTERM");
642
+ }
643
+ });
644
+ process.exit(0);
645
+ };
646
+ process.on("SIGINT", cleanup);
647
+ process.on("SIGTERM", cleanup);
648
+ if (!frontendOnly && backendDir) {
649
+ const tsxBin = resolveTsx(projectRoot);
650
+ if (!tsxBin) {
651
+ console.error(chalk.red(" ✗ Could not find tsx binary for backend."));
652
+ console.error(chalk.gray(" Install it with: pnpm add -D tsx"));
653
+ process.exit(1);
654
+ }
655
+ const envFile = findEnvFile(projectRoot);
656
+ const env = { ...process.env };
657
+ if (envFile) {
658
+ env.DOTENV_CONFIG_PATH = envFile;
659
+ }
660
+ if (args["--port"]) {
661
+ env.PORT = String(args["--port"]);
662
+ }
663
+ path.join(backendDir, "src", "index.ts");
664
+ const watchDirs = [
665
+ `--watch="${path.join("..", "shared", "**", "*")}"`
666
+ ];
667
+ console.log(` ${chalk.cyan("▶")} Backend: ${chalk.gray(backendDir)}`);
668
+ const backendChild = execa(
669
+ tsxBin,
670
+ ["watch", ...watchDirs, "--conditions", "development", "src/index.ts"],
671
+ {
672
+ cwd: backendDir,
673
+ stdio: ["inherit", "pipe", "pipe"],
674
+ env,
675
+ shell: true
676
+ }
677
+ );
678
+ backendChild.catch(() => {
679
+ });
680
+ backendChild.stdout?.on("data", (data) => {
681
+ const lines = data.toString().split("\n").filter(Boolean);
682
+ lines.forEach((line) => {
683
+ console.log(`${chalk.cyan.bold("[backend]")} ${line}`);
684
+ const cleanLine = stripAnsi(line);
685
+ if (cleanLine.includes("Server running at http://")) {
686
+ backendUrl = "started";
687
+ printSummary();
688
+ }
689
+ });
690
+ });
691
+ backendChild.stderr?.on("data", (data) => {
692
+ const lines = data.toString().split("\n").filter(Boolean);
693
+ lines.forEach((line) => {
694
+ console.log(`${chalk.cyan.bold("[backend]")} ${line}`);
695
+ });
696
+ });
697
+ children.push(backendChild);
698
+ } else if (!frontendOnly && !backendDir) {
699
+ console.warn(chalk.yellow(" ⚠ No backend/ directory found, skipping backend."));
700
+ }
701
+ if (!backendOnly && frontendDir) {
702
+ console.log(` ${chalk.magenta("▶")} Frontend: ${chalk.gray(frontendDir)}`);
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);
734
+ } else if (!backendOnly && !frontendDir) {
735
+ console.warn(chalk.yellow(" ⚠ No frontend/ directory found, skipping frontend."));
736
+ }
737
+ if (children.length === 0) {
738
+ console.error(chalk.red(" ✗ Nothing to start. Check your project structure."));
739
+ process.exit(1);
740
+ }
741
+ console.log("");
742
+ console.log(chalk.gray(" Press Ctrl+C to stop all servers."));
743
+ console.log("");
744
+ await Promise.all(
745
+ children.map(
746
+ (child) => new Promise((resolve) => {
747
+ child.finally(() => resolve());
748
+ })
749
+ )
750
+ );
751
+ }
752
+ function printDevHelp() {
753
+ console.log(`
754
+ ${chalk.bold("rebase dev")} — Start the development server
755
+
756
+ ${chalk.green.bold("Usage")}
757
+ rebase dev [options]
758
+
759
+ ${chalk.green.bold("Options")}
760
+ ${chalk.blue("--backend-only, -b")} Only start the backend server
761
+ ${chalk.blue("--frontend-only, -f")} Only start the frontend server
762
+ ${chalk.blue("--port, -p")} Backend port (default: 3001)
763
+
764
+ ${chalk.green.bold("Description")}
765
+ Starts both the backend (tsx watch + Express) and frontend (Vite)
766
+ dev servers concurrently with color-coded output prefixes.
767
+ `);
768
+ }
769
+ async function authCommand(subcommand, rawArgs) {
770
+ if (!subcommand || subcommand === "--help") {
771
+ printAuthHelp();
772
+ return;
773
+ }
774
+ switch (subcommand) {
775
+ case "reset-password":
776
+ await resetPassword(rawArgs);
777
+ break;
778
+ default:
779
+ console.error(chalk.red(`Unknown auth command: ${subcommand}`));
780
+ console.log("");
781
+ printAuthHelp();
782
+ process.exit(1);
783
+ }
784
+ }
785
+ async function resetPassword(rawArgs) {
786
+ const args = arg(
787
+ {
788
+ "--email": String,
789
+ "--password": String,
790
+ "-e": "--email",
791
+ "-p": "--password"
792
+ },
793
+ {
794
+ argv: rawArgs.slice(4),
795
+ // skip "node rebase auth reset-password"
796
+ permissive: true
797
+ }
798
+ );
799
+ const email = args["--email"] || args._[0];
800
+ const newPassword = args["--password"] || args._[1];
801
+ if (!email) {
802
+ console.error(chalk.red("✗ Email is required."));
803
+ console.log("");
804
+ console.log(chalk.gray(" Usage: rebase auth reset-password <email> [new-password]"));
805
+ console.log(chalk.gray(" rebase auth reset-password --email user@example.com --password NewPass123!"));
806
+ process.exit(1);
807
+ }
808
+ const projectRoot = requireProjectRoot();
809
+ const backendDir = requireBackendDir(projectRoot);
810
+ const tsxBin = resolveTsx(projectRoot);
811
+ if (!tsxBin) {
812
+ console.error(chalk.red("✗ Could not find tsx binary."));
813
+ process.exit(1);
814
+ }
815
+ const envFile = findEnvFile(projectRoot);
816
+ const env = { ...process.env };
817
+ if (envFile) {
818
+ env.DOTENV_CONFIG_PATH = envFile;
819
+ }
820
+ const scriptContent = `
821
+ import { createPostgresDatabaseConnection } from "@rebasepro/server-core";
822
+ import { hashPassword } from "@rebasepro/server-core/src/auth/password";
823
+ import { eq } from "drizzle-orm";
824
+ import { users } from "@rebasepro/server-core/src/db/auth-schema";
825
+ import * as dotenv from "dotenv";
826
+ import path from "path";
827
+
828
+ dotenv.config({ path: "${envFile || path.join(projectRoot, ".env")}" });
829
+
830
+ const email = "${email}";
831
+ const newPassword = "${newPassword || "NewPassword123!"}";
832
+
833
+ async function resetPassword() {
834
+ const { db } = createPostgresDatabaseConnection(process.env.DATABASE_URL!);
835
+ const hash = await hashPassword(newPassword);
836
+
837
+ const result = await db.update(users)
838
+ .set({ passwordHash: hash })
839
+ .where(eq(users.email, email))
840
+ .returning({
841
+ id: users.id,
842
+ email: users.email
843
+ });
844
+
845
+ if (result.length > 0) {
846
+ console.log("✅ Password reset for: " + result[0].email);
847
+ ${!newPassword ? 'console.log(" New password: " + newPassword);' : ""}
848
+ } else {
849
+ console.log("✗ User not found: " + email);
850
+ }
851
+ process.exit(0);
852
+ }
853
+
854
+ resetPassword().catch(console.error);
855
+ `;
856
+ const tmpScriptPath = path.join(backendDir, ".tmp-reset-password.ts");
857
+ fs.writeFileSync(tmpScriptPath, scriptContent, "utf-8");
858
+ console.log("");
859
+ console.log(chalk.bold(" 🔑 Rebase Auth — Reset Password"));
860
+ console.log("");
861
+ console.log(` ${chalk.gray("Email:")} ${email}`);
862
+ if (newPassword) {
863
+ console.log(` ${chalk.gray("Password:")} ${"*".repeat(newPassword.length)}`);
864
+ }
865
+ console.log("");
866
+ const child = child_process.spawn(tsxBin, [tmpScriptPath], {
867
+ cwd: backendDir,
868
+ stdio: "inherit",
869
+ env
870
+ });
871
+ return new Promise((resolve) => {
872
+ child.on("close", (code) => {
873
+ try {
874
+ fs.unlinkSync(tmpScriptPath);
875
+ } catch {
876
+ }
877
+ if (code !== 0) {
878
+ process.exit(code ?? 1);
879
+ }
880
+ resolve();
881
+ });
882
+ });
883
+ }
884
+ function printAuthHelp() {
885
+ console.log(`
886
+ ${chalk.bold("rebase auth")} — Authentication management commands
887
+
888
+ ${chalk.green.bold("Usage")}
889
+ rebase auth ${chalk.blue("<command>")} [options]
890
+
891
+ ${chalk.green.bold("Commands")}
892
+ ${chalk.blue.bold("reset-password")} Reset a user's password
893
+
894
+ ${chalk.green.bold("reset-password Options")}
895
+ ${chalk.blue("--email, -e")} User's email address
896
+ ${chalk.blue("--password, -p")} New password (default: NewPassword123!)
897
+
898
+ ${chalk.green.bold("Examples")}
899
+ rebase auth reset-password user@example.com
900
+ rebase auth reset-password --email user@example.com --password MyNewPass!
901
+ `);
902
+ }
182
903
  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);
183
904
  const __dirname$1 = path.dirname(__filename$1);
184
905
  function getVersion() {
@@ -209,16 +930,52 @@ POSTGRES_PASSWORD=${dbPassword}
209
930
  return;
210
931
  }
211
932
  const command = parsedArgs._[0];
212
- if (!command || parsedArgs["--help"]) {
933
+ const subcommand = parsedArgs._[1];
934
+ const namespacedCommands = ["schema", "db", "dev", "auth"];
935
+ if (!command || parsedArgs["--help"] && !namespacedCommands.includes(command)) {
213
936
  printHelp();
214
937
  return;
215
938
  }
216
- if (command === "init") {
217
- await createRebaseApp(args);
218
- } else {
219
- console.log(chalk.red(`Unknown command: ${command}`));
220
- console.log("");
221
- printHelp();
939
+ const effectiveSubcommand = parsedArgs["--help"] ? "--help" : subcommand;
940
+ switch (command) {
941
+ case "init":
942
+ await createRebaseApp(args);
943
+ break;
944
+ case "generate-sdk":
945
+ const sdkArgs = arg(
946
+ {
947
+ "--collections-dir": String,
948
+ "--output": String,
949
+ "-c": "--collections-dir",
950
+ "-o": "--output"
951
+ },
952
+ {
953
+ argv: args.slice(3),
954
+ permissive: true
955
+ }
956
+ );
957
+ await generateSdkCommand({
958
+ collectionsDir: sdkArgs["--collections-dir"] || "./shared/collections",
959
+ output: sdkArgs["--output"] || "./generated/sdk",
960
+ cwd: process.cwd()
961
+ });
962
+ break;
963
+ case "schema":
964
+ await schemaCommand(effectiveSubcommand, args);
965
+ break;
966
+ case "db":
967
+ await dbCommand(effectiveSubcommand, args);
968
+ break;
969
+ case "dev":
970
+ await devCommand(args);
971
+ break;
972
+ case "auth":
973
+ await authCommand(effectiveSubcommand, args);
974
+ break;
975
+ default:
976
+ console.log(chalk.red(`Unknown command: ${command}`));
977
+ console.log("");
978
+ printHelp();
222
979
  }
223
980
  }
224
981
  function printHelp() {
@@ -229,7 +986,27 @@ ${chalk.green.bold("Usage")}
229
986
  rebase ${chalk.blue("<command>")} [options]
230
987
 
231
988
  ${chalk.green.bold("Commands")}
232
- ${chalk.blue.bold("init")} Create a new Rebase project
989
+ ${chalk.blue.bold("init")} Create a new Rebase project
990
+ ${chalk.blue.bold("dev")} Start the development server
991
+
992
+ ${chalk.green.bold("Schema")}
993
+ ${chalk.blue.bold("schema generate")} Generate Drizzle schema from collections
994
+ ${chalk.blue.bold("schema")} ${chalk.gray("--help")} Show schema command help
995
+
996
+ ${chalk.green.bold("Database")}
997
+ ${chalk.blue.bold("db push")} Apply schema directly to database ${chalk.gray("(dev)")}
998
+ ${chalk.blue.bold("db pull")} Introspect database → Drizzle schema
999
+ ${chalk.blue.bold("db generate")} Generate SQL migration files
1000
+ ${chalk.blue.bold("db migrate")} Run pending migrations
1001
+ ${chalk.blue.bold("db studio")} Open Drizzle Studio
1002
+ ${chalk.blue.bold("db")} ${chalk.gray("--help")} Show database command help
1003
+
1004
+ ${chalk.green.bold("SDK")}
1005
+ ${chalk.blue.bold("generate-sdk")} Generate a typed JS SDK from collections
1006
+
1007
+ ${chalk.green.bold("Auth")}
1008
+ ${chalk.blue.bold("auth reset-password")} Reset a user's password
1009
+ ${chalk.blue.bold("auth")} ${chalk.gray("--help")} Show auth command help
233
1010
 
234
1011
  ${chalk.green.bold("Options")}
235
1012
  ${chalk.blue("--version, -v")} Show version number
@@ -281,13 +1058,27 @@ ${chalk.gray("Documentation: https://rebase.pro/docs")}
281
1058
  console.log("You are not logged in.");
282
1059
  }
283
1060
  }
1061
+ exports2.authCommand = authCommand;
284
1062
  exports2.createRebaseApp = createRebaseApp;
1063
+ exports2.dbCommand = dbCommand;
1064
+ exports2.devCommand = devCommand;
285
1065
  exports2.entry = entry;
1066
+ exports2.findBackendDir = findBackendDir;
1067
+ exports2.findEnvFile = findEnvFile;
1068
+ exports2.findFrontendDir = findFrontendDir;
1069
+ exports2.findProjectRoot = findProjectRoot;
1070
+ exports2.getActiveBackendPlugin = getActiveBackendPlugin;
286
1071
  exports2.getTokens = getTokens;
287
1072
  exports2.login = login;
288
1073
  exports2.logout = logout;
289
1074
  exports2.parseJwt = parseJwt;
290
1075
  exports2.refreshCredentials = refreshCredentials;
1076
+ exports2.requireBackendDir = requireBackendDir;
1077
+ exports2.requireProjectRoot = requireProjectRoot;
1078
+ exports2.resolveLocalBin = resolveLocalBin;
1079
+ exports2.resolvePluginCliScript = resolvePluginCliScript;
1080
+ exports2.resolveTsx = resolveTsx;
1081
+ exports2.schemaCommand = schemaCommand;
291
1082
  Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
292
1083
  }));
293
1084
  //# sourceMappingURL=index.cjs.map