@rebasepro/cli 0.12.1-canary.g181d0fe → 0.12.1-canary.g389e9b2

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/bin/rebase.js CHANGED
@@ -88,4 +88,30 @@ if (!existsSync(distEntry)) {
88
88
 
89
89
  const { entry } = await import("../dist/index.es.js");
90
90
 
91
- entry(process.argv);
91
+ /**
92
+ * The CLI's last line of defence.
93
+ *
94
+ * `entry()` returns a promise and nothing was awaiting it, so anything a
95
+ * command threw surfaced as an unhandled rejection: Node's own stack trace,
96
+ * rooted in `dist/index.es.js`, with the CLI's bundled line numbers and no
97
+ * exit code of its own. "Collections directory not found" is a sentence a
98
+ * developer can act on; the same sentence under ten frames of bundle internals
99
+ * reads as a crash in Rebase.
100
+ *
101
+ * The message is the error's own — commands that already print something
102
+ * friendly and exit never reach here. The stack is available behind
103
+ * `--debug`/`REBASE_DEBUG`, because when the message is *not* enough that is
104
+ * the only thing that helps.
105
+ */
106
+ const wantsStack = process.argv.includes("--debug") || process.env.REBASE_DEBUG === "1";
107
+
108
+ entry(process.argv).catch((error) => {
109
+ const message = error instanceof Error ? error.message : String(error);
110
+ process.stderr.write(`\x1b[31m✗ ${message}\x1b[0m\n`);
111
+ if (wantsStack && error instanceof Error && error.stack) {
112
+ process.stderr.write(`\n${error.stack}\n`);
113
+ } else {
114
+ process.stderr.write("\x1b[90m Re-run with --debug for the stack trace.\x1b[0m\n");
115
+ }
116
+ process.exit(1);
117
+ });
package/dist/bundle.d.ts CHANGED
@@ -68,6 +68,41 @@ export declare function detectNativeDependencies(projectRoot: string, declared:
68
68
  * own config package already travels inside the bundle.
69
69
  */
70
70
  export declare function collectDeclaredDependencies(projectRoot: string): Record<string, string>;
71
+ /** One `@rebasepro/*` dependency as some package.json in the project declares it. */
72
+ export interface DeclaredFrameworkDep {
73
+ name: string;
74
+ range: string;
75
+ /** Project-relative package.json it was declared in. */
76
+ file: string;
77
+ }
78
+ export interface FrameworkDepDrift {
79
+ /** Declared at a version that can never reach the CLI's own. */
80
+ behind: DeclaredFrameworkDep[];
81
+ /**
82
+ * The distinct lower bounds found across all declared `@rebasepro/*`, when
83
+ * there is more than one — the project is pinning mixed-era framework
84
+ * packages against each other.
85
+ */
86
+ disagreeing: string[];
87
+ }
88
+ /**
89
+ * Find `@rebasepro/*` dependencies pinned to a version older than this CLI.
90
+ *
91
+ * This is the only place a developer can be told. In development, every
92
+ * `@rebasepro/*` resolves through pnpm's `link:`/`workspace:` overrides to the
93
+ * checkout, so the version STRINGS in package.json are never exercised — the
94
+ * project runs fine locally on whatever is on disk, and the declared numbers are
95
+ * first honoured when the runtime npm-installs them from a bundle in the cloud.
96
+ * A project scaffolded at 0.10.0 therefore keeps working on a developer's
97
+ * machine indefinitely while being, in the cloud, a 0.10.0 driver.
98
+ *
99
+ * That matters because the image supplies only `@rebasepro/server`; the database
100
+ * driver comes from these declarations and a newer runtime never updates it.
101
+ * Every package.json is scanned, `dependencies` and `devDependencies` both,
102
+ * because they have to be bumped together and the one that gets forgotten is the
103
+ * one nobody looks at.
104
+ */
105
+ export declare function detectFrameworkDepDrift(projectRoot: string, cliVersion: string): FrameworkDepDrift;
71
106
  /**
72
107
  * Rewrite relative import specifiers in emitted JavaScript so Node can resolve them.
73
108
  *
@@ -182,3 +217,4 @@ export declare function buildStaticBundle(options: {
182
217
  manifest: RebaseBundleManifest;
183
218
  fileCount: number;
184
219
  };
220
+ export declare function resolveCliVersion(): string;
package/dist/index.es.js CHANGED
@@ -533,7 +533,7 @@ async function requireClient(rawArgs) {
533
533
  * the ingress and the console read — see saas/backend/src/utils/tenant-domain.ts).
534
534
  *
535
535
  * The CLI cannot know this value: it is per-deployment configuration (production
536
- * serves tenants at `apps.rebase.pro`, a dev control plane at `localhost`). It
536
+ * serves tenants at `rebase.website`, a dev control plane at `localhost`). It
537
537
  * used to be hardcoded to `rebase.pro`, so `cloud projects create` congratulated
538
538
  * the user with a URL that resolves nowhere near their app.
539
539
  *
@@ -1573,7 +1573,9 @@ async function configureEnvFile(targetDirectory, databaseUrl) {
1573
1573
  envContent = /^#?\s*REBASE_VERSION=.*$/m.test(envContent) ? envContent.replace(/^#?\s*REBASE_VERSION=.*$/m, `REBASE_VERSION=${runtimeVersion}`) : `${envContent.trimEnd()}\n\n# The Rebase runtime image tag docker-compose.yml pulls.\n# Change this and restart to upgrade; your project bundle is untouched.\nREBASE_VERSION=${runtimeVersion}\n`;
1574
1574
  if (databaseUrl) {
1575
1575
  if (/[\r\n]/.test(databaseUrl)) throw new Error("Invalid DATABASE_URL: multiline values are not allowed.");
1576
- envContent = envContent.replace(/^DATABASE_URL=.*$/m, `DATABASE_URL=${databaseUrl}\nDATABASE_PASSWORD=${dbPassword}`);
1576
+ const { pinSearchPath } = await import("@rebasepro/server-postgres");
1577
+ const pinnedUrl = pinSearchPath(databaseUrl);
1578
+ envContent = envContent.replace(/^DATABASE_URL=.*$/m, `DATABASE_URL=${pinnedUrl}\nDATABASE_PASSWORD=${dbPassword}`);
1577
1579
  } else {
1578
1580
  const dbPort = await findAvailablePort(5432);
1579
1581
  envContent = envContent.replace(/^DATABASE_URL=.*$/m, `DATABASE_URL=postgresql://rebase:${dbPassword}@localhost:${dbPort}/rebase?options=-c%20search_path=public&sslmode=disable\nDATABASE_PASSWORD=${dbPassword}`);
@@ -3513,6 +3515,139 @@ function collectDeclaredDependencies(projectRoot) {
3513
3515
  return declared;
3514
3516
  }
3515
3517
  /**
3518
+ * Lowest version a range could resolve to, or null if it is not a range.
3519
+ *
3520
+ * Kept deliberately tiny and local. The published range grammar here is a caret,
3521
+ * a tilde, an exact version or a `>=` floor, and the alternative — a semver
3522
+ * dependency in the CLI — buys breadth this does not need.
3523
+ */
3524
+ function lowerBoundOf(range) {
3525
+ const raw = range.trim().replace(/^[\^~]/, "").replace(/^>=\s*/, "").replace(/^v/, "");
3526
+ if (!/^\d+(\.\d+){0,2}$/.test(raw)) return null;
3527
+ const [major, minor = 0, patch = 0] = raw.split(".").map(Number);
3528
+ return [
3529
+ major,
3530
+ minor,
3531
+ patch
3532
+ ];
3533
+ }
3534
+ /** Highest version a range could resolve to (exclusive), or null. */
3535
+ function upperBoundOf(range) {
3536
+ const trimmed = range.trim();
3537
+ const min = lowerBoundOf(trimmed);
3538
+ if (!min) return null;
3539
+ if (trimmed.startsWith(">=")) return null;
3540
+ const [major, minor, patch] = min;
3541
+ if (trimmed.startsWith("^")) {
3542
+ if (major > 0) return [
3543
+ major + 1,
3544
+ 0,
3545
+ 0
3546
+ ];
3547
+ if (minor > 0) return [
3548
+ 0,
3549
+ minor + 1,
3550
+ 0
3551
+ ];
3552
+ return [
3553
+ 0,
3554
+ 0,
3555
+ patch + 1
3556
+ ];
3557
+ }
3558
+ if (trimmed.startsWith("~")) return trimmed.replace(/^~v?/, "").split(".").length >= 2 ? [
3559
+ major,
3560
+ minor + 1,
3561
+ 0
3562
+ ] : [
3563
+ major + 1,
3564
+ 0,
3565
+ 0
3566
+ ];
3567
+ const parts = trimmed.replace(/^v/, "").split(".").length;
3568
+ if (parts === 1) return [
3569
+ major + 1,
3570
+ 0,
3571
+ 0
3572
+ ];
3573
+ if (parts === 2) return [
3574
+ major,
3575
+ minor + 1,
3576
+ 0
3577
+ ];
3578
+ return [
3579
+ major,
3580
+ minor,
3581
+ patch + 1
3582
+ ];
3583
+ }
3584
+ function compareTriples(a, b) {
3585
+ for (let i = 0; i < 3; i++) if (a[i] !== b[i]) return a[i] < b[i] ? -1 : 1;
3586
+ return 0;
3587
+ }
3588
+ /**
3589
+ * Whether a declared range could EVER resolve at or above `target`.
3590
+ *
3591
+ * The same question the control plane asks at intake, asked here first. Only a
3592
+ * range whose entire span sits below the target is reported — `^0.10.0` can
3593
+ * never cross to 0.12 whatever npm publishes — because a false alarm on a build
3594
+ * that would have worked trains people to ignore the warning that matters.
3595
+ */
3596
+ function canReach(range, target) {
3597
+ const ceiling = upperBoundOf(range);
3598
+ const floor = lowerBoundOf(target);
3599
+ if (!floor || !lowerBoundOf(range)) return null;
3600
+ if (!ceiling) return true;
3601
+ return compareTriples(ceiling, floor) > 0;
3602
+ }
3603
+ /**
3604
+ * Find `@rebasepro/*` dependencies pinned to a version older than this CLI.
3605
+ *
3606
+ * This is the only place a developer can be told. In development, every
3607
+ * `@rebasepro/*` resolves through pnpm's `link:`/`workspace:` overrides to the
3608
+ * checkout, so the version STRINGS in package.json are never exercised — the
3609
+ * project runs fine locally on whatever is on disk, and the declared numbers are
3610
+ * first honoured when the runtime npm-installs them from a bundle in the cloud.
3611
+ * A project scaffolded at 0.10.0 therefore keeps working on a developer's
3612
+ * machine indefinitely while being, in the cloud, a 0.10.0 driver.
3613
+ *
3614
+ * That matters because the image supplies only `@rebasepro/server`; the database
3615
+ * driver comes from these declarations and a newer runtime never updates it.
3616
+ * Every package.json is scanned, `dependencies` and `devDependencies` both,
3617
+ * because they have to be bumped together and the one that gets forgotten is the
3618
+ * one nobody looks at.
3619
+ */
3620
+ function detectFrameworkDepDrift(projectRoot, cliVersion) {
3621
+ const found = [];
3622
+ for (const relative of [
3623
+ "package.json",
3624
+ "backend/package.json",
3625
+ "config/package.json",
3626
+ "frontend/package.json"
3627
+ ]) {
3628
+ const file = path.join(projectRoot, relative);
3629
+ if (!fs.existsSync(file)) continue;
3630
+ try {
3631
+ const pkg = JSON.parse(fs.readFileSync(file, "utf8"));
3632
+ for (const block of [pkg.dependencies, pkg.devDependencies]) for (const [name, range] of Object.entries(block ?? {})) {
3633
+ if (!name.startsWith("@rebasepro/")) continue;
3634
+ if (typeof range !== "string") continue;
3635
+ found.push({
3636
+ name,
3637
+ range,
3638
+ file: relative
3639
+ });
3640
+ }
3641
+ } catch {}
3642
+ }
3643
+ const behind = found.filter((d) => canReach(d.range, cliVersion) === false);
3644
+ const bounds = new Set(found.map((d) => lowerBoundOf(d.range)).filter((b) => b != null).map((b) => b.join(".")));
3645
+ return {
3646
+ behind,
3647
+ disagreeing: bounds.size > 1 ? [...bounds].sort() : []
3648
+ };
3649
+ }
3650
+ /**
3516
3651
  * Rewrite relative import specifiers in emitted JavaScript so Node can resolve them.
3517
3652
  *
3518
3653
  * TypeScript deliberately does not touch specifiers: `moduleResolution: "bundler"`
@@ -4210,6 +4345,16 @@ async function buildCommand(rawArgs = []) {
4210
4345
  console.log(chalk.yellow(` ⚠ native dependencies detected: ${names}`));
4211
4346
  console.log(chalk.dim(" These cannot run on the managed runtime. See `rebase doctor`."));
4212
4347
  }
4348
+ const drift = detectFrameworkDepDrift(projectRoot, resolveCliVersion());
4349
+ if (drift.behind.length > 0) {
4350
+ console.log(chalk.yellow(` ⚠ framework dependencies older than this CLI (${resolveCliVersion()}):`));
4351
+ for (const dep of drift.behind) console.log(chalk.dim(` ${dep.name}@${dep.range} (${dep.file})`));
4352
+ console.log(chalk.dim(" The image supplies the server, but your bundle supplies the database"));
4353
+ console.log(chalk.dim(" driver — a newer runtime does not update it. Bump these and rebuild."));
4354
+ } else if (drift.disagreeing.length > 0) {
4355
+ console.log(chalk.yellow(` ⚠ mixed @rebasepro versions declared: ${drift.disagreeing.join(", ")}`));
4356
+ console.log(chalk.dim(" These are published together and expect to run together; pin them alike."));
4357
+ }
4213
4358
  if (!args["--no-static"]) {
4214
4359
  const folded = await foldFrontendIntoBundle({
4215
4360
  projectRoot,
@@ -5068,7 +5213,7 @@ function parseAgentFlags(rawArgs) {
5068
5213
  return requested;
5069
5214
  }
5070
5215
  async function skillsInstall(rawArgs = []) {
5071
- const projectDir = process.cwd();
5216
+ const projectDir = findProjectRoot() ?? process.cwd();
5072
5217
  let skillsDir;
5073
5218
  try {
5074
5219
  skillsDir = getSkillsSourceDir();
@@ -5112,7 +5257,8 @@ async function skillsInstall(rawArgs = []) {
5112
5257
  for (const agentKey of agents) {
5113
5258
  const agent = AGENTS[agentKey];
5114
5259
  const count = installForAgent(agentKey, skills, projectDir);
5115
- console.log(` ${chalk.green("✓")} ${chalk.bold(agent.label)} — ${count} skills installed to ${chalk.gray(agent.targetDir)}`);
5260
+ const shown = path.relative(process.cwd(), path.join(projectDir, agent.targetDir)) || agent.targetDir;
5261
+ console.log(` ${chalk.green("✓")} ${chalk.bold(agent.label)} — ${count} skills installed to ${chalk.gray(shown)}`);
5116
5262
  }
5117
5263
  console.log("");
5118
5264
  console.log(chalk.gray(" Skills are project-local. Commit them to share with your team."));
@@ -9883,9 +10029,10 @@ async function entry(args) {
9883
10029
  argv: args.slice(3),
9884
10030
  permissive: true
9885
10031
  });
10032
+ const sdkRoot = sdkArgs["--help"] ? process.cwd() : requireProjectRoot();
9886
10033
  await generateSdkCommand({
9887
- collectionsDir: sdkArgs["--collections-dir"] || "./config/collections",
9888
- output: sdkArgs["--output"] || "./generated/sdk",
10034
+ collectionsDir: sdkArgs["--collections-dir"] || path.join(sdkRoot, "config/collections"),
10035
+ output: sdkArgs["--output"] || path.join(sdkRoot, "generated/sdk"),
9889
10036
  from: sdkArgs["--from"],
9890
10037
  token: sdkArgs["--token"],
9891
10038
  help: sdkArgs["--help"],
@@ -9995,6 +10142,6 @@ ${chalk.gray("Documentation: https://rebase.pro/docs")}
9995
10142
  `);
9996
10143
  }
9997
10144
  //#endregion
9998
- export { CURRENT_RUNTIME_RANGE, DEFAULT_BUNDLE_DIR, DEFAULT_CONFIG_DIR, DEFAULT_CRONS_DIR, DEFAULT_FUNCTIONS_DIR, DEFAULT_SCHEMA_FILE, MANIFEST_FILENAME, ManifestError, TEMPLATE_PLACEHOLDER_FILES, appsCommand, assessManagedCompatibility, authCommand, buildBundle, buildCommand, buildInitQuestions, buildStaticBundle, buildableApps, cloudCommand, collectDeclaredDependencies, configureEnvFile, createRebaseApp, dbCommand, detectNativeDependencies, detectPackageManager, detectStorageAuthorize, devCommand, doctorCommand, ejectCommand, entry, findBackendApp, findBackendDir, findEnvFile, findFrontendDir, findProjectRoot, findUnusedServerEntry, foldStaticIntoBundle, formatCdTarget, generateSdkCommand, getActiveBackendPlugin, getPMCommands, isIdentifierLike, isPnpmAvailable, loadManifest, manifestExists, manifestPath, normalizeEsmSpecifiers, pnpmAvailabilityFromProbe, printInitHelp, requireBackendDir, requireProjectRoot, resetPnpmAvailabilityCache, resolveBackendPaths, resolveExampleBaseUrl, resolveLocalBin, resolvePluginCliScript, resolveTsx, schemaCommand, startCommand, synthesizeManifest, validateManifest, validateProjectName, validateTsxInstallation, writeManifest };
10145
+ export { CURRENT_RUNTIME_RANGE, DEFAULT_BUNDLE_DIR, DEFAULT_CONFIG_DIR, DEFAULT_CRONS_DIR, DEFAULT_FUNCTIONS_DIR, DEFAULT_SCHEMA_FILE, MANIFEST_FILENAME, ManifestError, TEMPLATE_PLACEHOLDER_FILES, appsCommand, assessManagedCompatibility, authCommand, buildBundle, buildCommand, buildInitQuestions, buildStaticBundle, buildableApps, cloudCommand, collectDeclaredDependencies, configureEnvFile, createRebaseApp, dbCommand, detectFrameworkDepDrift, detectNativeDependencies, detectPackageManager, detectStorageAuthorize, devCommand, doctorCommand, ejectCommand, entry, findBackendApp, findBackendDir, findEnvFile, findFrontendDir, findProjectRoot, findUnusedServerEntry, foldStaticIntoBundle, formatCdTarget, generateSdkCommand, getActiveBackendPlugin, getPMCommands, isIdentifierLike, isPnpmAvailable, loadManifest, manifestExists, manifestPath, normalizeEsmSpecifiers, pnpmAvailabilityFromProbe, printInitHelp, requireBackendDir, requireProjectRoot, resetPnpmAvailabilityCache, resolveBackendPaths, resolveCliVersion, resolveExampleBaseUrl, resolveLocalBin, resolvePluginCliScript, resolveTsx, schemaCommand, startCommand, synthesizeManifest, validateManifest, validateProjectName, validateTsxInstallation, writeManifest };
9999
10146
 
10000
10147
  //# sourceMappingURL=index.es.js.map