@rebasepro/server-postgres 0.25.1 → 0.26.0

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 (83) hide show
  1. package/dist/{BranchService-ClklxyAQ.js → BranchService-CWBnDhcv.js} +25 -9
  2. package/dist/BranchService-CWBnDhcv.js.map +1 -0
  3. package/dist/PostgresBackendDriver.d.ts +13 -0
  4. package/dist/PostgresBootstrapper.d.ts +7 -0
  5. package/dist/auth/services.d.ts +8 -1
  6. package/dist/auth/verify-auth-tables.d.ts +21 -0
  7. package/dist/{backup-cli-gijeXe0u.js → backup-cli-PoRG-zEq.js} +3 -3
  8. package/dist/{backup-cli-gijeXe0u.js.map → backup-cli-PoRG-zEq.js.map} +1 -1
  9. package/dist/{boot-ddl-DYH95Qeg.js → boot-ddl-EsgqDkx0.js} +2 -2
  10. package/dist/{boot-ddl-DYH95Qeg.js.map → boot-ddl-EsgqDkx0.js.map} +1 -1
  11. package/dist/branch-diff-CxE4VlxT.js +1396 -0
  12. package/dist/branch-diff-CxE4VlxT.js.map +1 -0
  13. package/dist/branch-diff-report-DpvVf7hR.js +48 -0
  14. package/dist/branch-diff-report-DpvVf7hR.js.map +1 -0
  15. package/dist/branching/branch-diff-report.d.ts +7 -0
  16. package/dist/branching/branch-diff.d.ts +115 -0
  17. package/dist/{cli-console-CQrQ0lAY.js → cli-console-f0k6_TsJ.js} +38 -5
  18. package/dist/cli-console-f0k6_TsJ.js.map +1 -0
  19. package/dist/cli-console.d.ts +17 -1
  20. package/dist/{cli-errors-DiA0XFIS.js → cli-errors-DsYyNYzd.js} +53 -2
  21. package/dist/cli-errors-DsYyNYzd.js.map +1 -0
  22. package/dist/cli.js +102 -122
  23. package/dist/cli.js.map +1 -1
  24. package/dist/{collections-schema-version-T6cGvrDi.js → collections-schema-version-D7bDI0PJ.js} +13 -9
  25. package/dist/collections-schema-version-D7bDI0PJ.js.map +1 -0
  26. package/dist/{deferred-index-build-BE0I6sYU.js → deferred-index-build-CqaOv1Xx.js} +3 -3
  27. package/dist/{deferred-index-build-BE0I6sYU.js.map → deferred-index-build-CqaOv1Xx.js.map} +1 -1
  28. package/dist/{doctor-BT--1kq3.js → doctor-DW_mlTzz.js} +81 -33
  29. package/dist/doctor-DW_mlTzz.js.map +1 -0
  30. package/dist/{ensure-collection-policies-BnGOURpG.js → ensure-collection-policies-DLWgabQ2.js} +5 -5
  31. package/dist/{ensure-collection-policies-BnGOURpG.js.map → ensure-collection-policies-DLWgabQ2.js.map} +1 -1
  32. package/dist/{ensure-collection-tables-Cet4650m.js → ensure-collection-tables-CFIRq8ni.js} +5 -5
  33. package/dist/{ensure-collection-tables-Cet4650m.js.map → ensure-collection-tables-CFIRq8ni.js.map} +1 -1
  34. package/dist/{ensure-tables-l_OgFQjN.js → ensure-tables-4nA-tfwi.js} +4 -4
  35. package/dist/{ensure-tables-l_OgFQjN.js.map → ensure-tables-4nA-tfwi.js.map} +1 -1
  36. package/dist/{enum-labels-kept-nheku1pB.js → enum-labels-kept-PcPW65xy.js} +2 -2
  37. package/dist/{enum-labels-kept-nheku1pB.js.map → enum-labels-kept-PcPW65xy.js.map} +1 -1
  38. package/dist/history/HistoryService.d.ts +7 -0
  39. package/dist/index.d.ts +20 -0
  40. package/dist/index.es.js +226 -8
  41. package/dist/index.es.js.map +1 -1
  42. package/dist/{db-subcommands-BPoHCxJR.js → migrate-baseline-B-2Ef4Mv.js} +266 -41
  43. package/dist/migrate-baseline-B-2Ef4Mv.js.map +1 -0
  44. package/dist/{migrate-policy-stamps-3eqfhes6.js → migrate-policy-stamps-BQtFHApx.js} +5 -5
  45. package/dist/{migrate-policy-stamps-3eqfhes6.js.map → migrate-policy-stamps-BQtFHApx.js.map} +1 -1
  46. package/dist/{open-rebase-EmqVmS14.js → open-rebase-C8jQ6s5a.js} +292 -46
  47. package/dist/open-rebase-C8jQ6s5a.js.map +1 -0
  48. package/dist/open-rebase.d.ts +9 -1
  49. package/dist/{pg-error-utils-CFEnm2m9.js → pg-error-utils-DhiUTlVx.js} +2 -2
  50. package/dist/{pg-error-utils-CFEnm2m9.js.map → pg-error-utils-DhiUTlVx.js.map} +1 -1
  51. package/dist/{push-index-prebuild-COd7i6wm.js → push-index-prebuild-UTvB5pqH.js} +3 -3
  52. package/dist/{push-index-prebuild-COd7i6wm.js.map → push-index-prebuild-UTvB5pqH.js.map} +1 -1
  53. package/dist/push-kept-defaults-BGOlOqRQ.js +137 -0
  54. package/dist/push-kept-defaults-BGOlOqRQ.js.map +1 -0
  55. package/dist/{push-legacy-renames-CLgYYwPE.js → push-legacy-renames-BwaKi8xF.js} +3 -3
  56. package/dist/{push-legacy-renames-CLgYYwPE.js.map → push-legacy-renames-BwaKi8xF.js.map} +1 -1
  57. package/dist/{push-policy-plan-D2gpQsdv.js → push-policy-plan-DQ2tWceD.js} +2 -2
  58. package/dist/{push-policy-plan-D2gpQsdv.js.map → push-policy-plan-DQ2tWceD.js.map} +1 -1
  59. package/dist/{rls-enforcement-DDyNoOSy.js → rls-enforcement-BOHuHVlc.js} +42 -3
  60. package/dist/rls-enforcement-BOHuHVlc.js.map +1 -0
  61. package/dist/schema/destructive-sql.d.ts +16 -0
  62. package/dist/schema/doctor-cli.js +3 -3
  63. package/dist/schema/introspect-db.js +1 -1
  64. package/dist/schema/migration-freshness.d.ts +69 -0
  65. package/dist/schema/push-kept-defaults.d.ts +57 -0
  66. package/dist/schema/rls-bootstrap-sql.d.ts +47 -10
  67. package/dist/security/rls-enforcement.d.ts +18 -0
  68. package/dist/services/FetchService.d.ts +12 -0
  69. package/dist/services/channel-presence.d.ts +3 -1
  70. package/dist/services/nested-path.d.ts +16 -0
  71. package/dist/services/realtimeService.d.ts +18 -1
  72. package/dist/{websocket-BKZwsjmf.js → websocket-CKYc3V4N.js} +41 -12
  73. package/dist/websocket-CKYc3V4N.js.map +1 -0
  74. package/package.json +7 -7
  75. package/dist/BranchService-ClklxyAQ.js.map +0 -1
  76. package/dist/cli-console-CQrQ0lAY.js.map +0 -1
  77. package/dist/cli-errors-DiA0XFIS.js.map +0 -1
  78. package/dist/collections-schema-version-T6cGvrDi.js.map +0 -1
  79. package/dist/db-subcommands-BPoHCxJR.js.map +0 -1
  80. package/dist/doctor-BT--1kq3.js.map +0 -1
  81. package/dist/open-rebase-EmqVmS14.js.map +0 -1
  82. package/dist/rls-enforcement-DDyNoOSy.js.map +0 -1
  83. package/dist/websocket-BKZwsjmf.js.map +0 -1
package/dist/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { createRequire as __createRequire } from "module";
2
2
  __createRequire(import.meta.url);
3
3
  import { n as require_arg, o as __toESM, t as promptConfirm } from "./cli-prompt-DXG0yk-h.js";
4
- import { A as getUndeclaredConstraintExcludes, B as withAppClient, C as dropDevDatabase, D as getSearchExcludes, E as getForeignIndexExcludes, F as queryUndeclarableConstraints, G as prepareGeneratedSqlDir, H as DESIRED_STATE_URL, I as readSearchDdl, J as detectDestructiveStatements, K as decidePushSafety, L as readTriggersDdl, M as loadCollectionsForCli, N as queryColumnTypes, O as getTableExcludes, P as queryGeneratedColumnDependencies, Q as forAtlas, R as readVectorDdl, S as diagnoseMissingBin, T as ensureDevDatabaseExists, U as GENERATED_SQL_DIR, V as workspaceMemberDir, W as generatedSqlDir, X as fromAlterTable, Y as extractPlanStatements, Z as isSyncedPlan, _ as closeAppClients, a as diagnoseDbError, b as describeReinstallCommand, c as isEconnrefused, d as reportCommandFailure, et as redactConnectionPasswords, f as ExcludeIntrospectionError, g as atlasDevDatabaseUrl, h as applyVectorDdl, i as diagnoseAtlasFailure, j as getVectorExcludes, k as getTriggerExcludes, l as isSslNotEnabled, m as applyTriggersDdl, o as formatForeignGeneratedColumnBanner, p as applySearchDdl, q as destructiveOptInFlag, s as isAuthFailure, t as checkDatabaseConnectivity, v as describeBuildScriptRemedy, w as dropGeneratedColumns, x as detectProjectPackageManager, y as describeDevAddCommand, z as resolveLocalBin } from "./cli-errors-DiA0XFIS.js";
4
+ import { $ as forAtlas, A as getUndeclaredConstraintExcludes, B as withAppClient, C as dropDevDatabase, D as getSearchExcludes, E as getForeignIndexExcludes, F as queryUndeclarableConstraints, G as prepareGeneratedSqlDir, H as DESIRED_STATE_URL, I as readSearchDdl, J as detectDestructiveStatements, K as decidePushSafety, L as readTriggersDdl, M as loadCollectionsForCli, N as queryColumnTypes, O as getTableExcludes, P as queryGeneratedColumnDependencies, Q as warnIfYesApprovedDestructive, R as readVectorDdl, S as diagnoseMissingBin, T as ensureDevDatabaseExists, U as GENERATED_SQL_DIR, V as workspaceMemberDir, W as generatedSqlDir, X as fromAlterTable, Y as extractPlanStatements, Z as isSyncedPlan, _ as closeAppClients, a as diagnoseDbError, b as describeReinstallCommand, c as isEconnrefused, d as reportCommandFailure, f as ExcludeIntrospectionError, g as atlasDevDatabaseUrl, h as applyVectorDdl, i as diagnoseAtlasFailure, j as getVectorExcludes, k as getTriggerExcludes, l as isSslNotEnabled, m as applyTriggersDdl, o as formatForeignGeneratedColumnBanner, p as applySearchDdl, q as destructiveOptInFlag, s as isAuthFailure, t as checkDatabaseConnectivity, tt as redactConnectionPasswords, v as describeBuildScriptRemedy, w as dropGeneratedColumns, x as detectProjectPackageManager, y as describeDevAddCommand, z as resolveLocalBin } from "./cli-errors-DsYyNYzd.js";
5
5
  import { a as outWarn, i as outError, o as atlasExcludePattern, r as out, s as excludePatternParts, t as chalk } from "./source-fZb92pN8.js";
6
6
  import { t as nextStepAfterDbGenerate } from "./generate-next-step-DgK5BlBn.js";
7
- import { a as resolveBranchSwitches, c as assertKnownFlags, f as parseDriverLine, g as dropLegacyAuthSchema, m as RLS_BOOTSTRAP_SQL, n as isDriverDbSubcommand, o as unexpectedBranchArgs, p as resolveDriverSwitches, r as backupActionOf, t as DRIVER_DB_SUBCOMMANDS, u as collectionsPathIn, y as execa } from "./db-subcommands-BPoHCxJR.js";
7
+ import { S as dropLegacyAuthSchema, T as execa, b as RLS_BOOTSTRAP_SQL, c as isDriverDbSubcommand, d as resolveBranchSwitches, f as unexpectedBranchArgs, g as collectionsPathIn, i as latestRecordedMigration, l as backupActionOf, m as assertKnownFlags, o as planBaseline, r as describeBaselinePlan, s as DRIVER_DB_SUBCOMMANDS, t as baselineVersionProblem, v as parseDriverLine, y as resolveDriverSwitches } from "./migrate-baseline-B-2Ef4Mv.js";
8
8
  import { formatRelativeTime } from "@rebasepro/utils";
9
9
  import path from "path";
10
10
  import fs from "fs";
@@ -890,102 +890,6 @@ var CommandStopped = class extends Error {
890
890
  }
891
891
  };
892
892
  //#endregion
893
- //#region src/schema/migrate-baseline.ts
894
- /**
895
- * What `rebase db migrate --baseline <version>` does, given the history the
896
- * database already has.
897
- *
898
- * The flag promises to record `<version>` as applied and migrate from the next
899
- * one. Atlas's own `migrate apply --baseline` keeps that promise only on a
900
- * database with no revision history: once one row exists it ignores the flag
901
- * without a word, applies the migration anyway, and the CLI had already printed
902
- * "Recording … as already applied". That is the normal second time: a project
903
- * baselines once, then `rebase dev` adds a column at boot, `db generate` writes
904
- * the migration for it, and `db migrate` fails on `column … already exists
905
- * (42701)` however the baseline is spelled.
906
- *
907
- * So the history is read first. With none, Atlas's baseline does the work.
908
- * Behind `<version>`, `atlas migrate set` marks every migration up to it as
909
- * applied, which is what a baseline means. At or past it, there is nothing to
910
- * record, and the command says so instead of claiming it recorded something.
911
- */
912
- /**
913
- * Atlas's version order for the versions Rebase writes: numeric timestamps,
914
- * compared as numbers so a shorter one is not read as later; anything else as
915
- * text.
916
- */
917
- function compareMigrationVersions(a, b) {
918
- if (/^\d+$/.test(a) && /^\d+$/.test(b)) {
919
- const x = BigInt(a);
920
- const y = BigInt(b);
921
- return x === y ? 0 : x < y ? -1 : 1;
922
- }
923
- return a.localeCompare(b);
924
- }
925
- /**
926
- * The version Atlas records for a migration file: its numeric prefix
927
- * (`20260906101530_init.sql` → `20260906101530`), or the name without `.sql`.
928
- */
929
- function migrationVersionOf(file) {
930
- return /^(\d+)/.exec(file)?.[1] ?? file.replace(/\.sql$/, "");
931
- }
932
- /** How many versions a refused baseline names, newest last. */
933
- var VERSIONS_NAMED = 5;
934
- /**
935
- * Why `--baseline <version>` cannot run, or `null` when it names a migration
936
- * file in `drizzle/migrations`.
937
- *
938
- * Asked before anything else, the database included. A version no file has
939
- * was refused only by Atlas, after the command had said "Marking every
940
- * migration up to abc as applied…"; and one older than every file read
941
- * "Nothing to record" and migrated on, though it named nothing to baseline.
942
- *
943
- * @param files the migration files, sorted oldest first.
944
- * @returns the refusal, its first line the error and the rest the detail.
945
- */
946
- function baselineVersionProblem(version, files) {
947
- const versions = files.map(migrationVersionOf);
948
- if (versions.length === 0) return `--baseline ${version}: There are no migrations in drizzle/migrations to baseline. Nothing was run.\nRun \`rebase db generate\` to write the first one.`;
949
- if (versions.includes(version)) return null;
950
- const named = versions.slice(-5).join(", ");
951
- return `--baseline ${version} names no migration in drizzle/migrations. Nothing was run.\n${versions.length > VERSIONS_NAMED ? `The newest ${VERSIONS_NAMED} of the ${versions.length} versions there` : "The versions there"}: ${named}. A baseline is one of them: the newest migration the database already holds.`;
952
- }
953
- /** How to keep `--baseline <version>`'s promise, given the newest recorded version. */
954
- function planBaseline(version, latestRecorded) {
955
- if (latestRecorded === null) return { kind: "atlas-baseline" };
956
- if (compareMigrationVersions(version, latestRecorded) > 0) return {
957
- kind: "set",
958
- latest: latestRecorded
959
- };
960
- return {
961
- kind: "already-recorded",
962
- latest: latestRecorded
963
- };
964
- }
965
- /** What the command says before it migrates, for each plan. */
966
- function describeBaselinePlan(version, plan) {
967
- switch (plan.kind) {
968
- case "atlas-baseline": return `Recording ${version} as already applied, then migrating from the next one.`;
969
- case "set": return `This database already records migrations up to ${plan.latest}, and Atlas applies --baseline only to a database with none.\n Marking every migration up to ${version} as applied with \`atlas migrate set\`, then migrating from the next one.`;
970
- case "already-recorded": return `Nothing to record: this database already records migrations up to ${plan.latest}, which includes ${version}.\n Migrating from the next one.`;
971
- }
972
- }
973
- /**
974
- * The newest version Atlas has recorded on this database, or `null` when it
975
- * has recorded none (no revisions table yet included).
976
- *
977
- * The table is `rebase.atlas_schema_revisions`: the CLI passes
978
- * `--revisions-schema rebase` to every `migrate apply`.
979
- */
980
- async function latestRecordedMigration(databaseUrl) {
981
- return withAppClient(databaseUrl, async (client) => {
982
- if (!(await client.query("SELECT to_regclass('rebase.atlas_schema_revisions') IS NOT NULL AS present")).rows[0]?.present) return null;
983
- const versions = (await client.query("SELECT version FROM rebase.atlas_schema_revisions WHERE version NOT LIKE '.%'")).rows.map((row) => row.version);
984
- if (versions.length === 0) return null;
985
- return versions.reduce((latest, version) => compareMigrationVersions(version, latest) > 0 ? version : latest);
986
- });
987
- }
988
- //#endregion
989
893
  //#region src/cli-push.ts
990
894
  /**
991
895
  * Apply a planned push.
@@ -1348,7 +1252,7 @@ async function runPluginCommand(args) {
1348
1252
  else if (domain === "schema") await schemaCommand(subcommand, args);
1349
1253
  else if (domain === "doctor") await doctorPluginCommand(args);
1350
1254
  else if (domain === "console") {
1351
- const { consoleCommand } = await import("./cli-console-CQrQ0lAY.js");
1255
+ const { consoleCommand } = await import("./cli-console-f0k6_TsJ.js");
1352
1256
  process.exitCode = await consoleCommand(args);
1353
1257
  } else {
1354
1258
  outError(chalk.red(`Unknown domain command: ${domain}`));
@@ -1515,7 +1419,7 @@ async function dbCommand(subcommand, rawArgs) {
1515
1419
  return;
1516
1420
  }
1517
1421
  if (subcommand === "backup" || subcommand === "restore" || subcommand === "backups") {
1518
- const { backupCommand, restoreCommand, backupsCommand } = await import("./backup-cli-gijeXe0u.js").then((n) => n.t);
1422
+ const { backupCommand, restoreCommand, backupsCommand } = await import("./backup-cli-PoRG-zEq.js").then((n) => n.t);
1519
1423
  if (subcommand === "backup" && backupActionOf(rawArgs) === "list") await backupsCommand(rawArgs);
1520
1424
  else if (subcommand === "backup") await backupCommand(rawArgs);
1521
1425
  else if (subcommand === "restore") await restoreCommand(rawArgs);
@@ -1636,6 +1540,7 @@ async function dbCommand(subcommand, rawArgs) {
1636
1540
  if (databaseUrl && !dryRun) await ensureAuthSchemaAndFunctions(databaseUrl);
1637
1541
  if (databaseUrl) await renameLegacyForeignKeys(databaseUrl, collectionsPath, dryRun);
1638
1542
  const prebuilt = databaseUrl ? await prebuildForeignKeyIndexes(databaseUrl, collectionsPath, dryRun) : [];
1543
+ if (databaseUrl) await keepDatabaseOwnedDefaults(databaseUrl, collectionsPath);
1639
1544
  const plan = await runAtlas("schema", [
1640
1545
  "apply",
1641
1546
  "--to",
@@ -1700,6 +1605,7 @@ async function dbCommand(subcommand, rawArgs) {
1700
1605
  for (const line of destructiveLines(destructive)) outWarn(line);
1701
1606
  outWarn("");
1702
1607
  outWarn(chalk.yellow(" Applying them needs `rebase db push --allow-destructive`. Back up first: rebase db backup"));
1608
+ outWarn(chalk.gray(" (`--yes` still approves them in this release, with a deprecation warning; from the next it will not.)"));
1703
1609
  outWarn("");
1704
1610
  }
1705
1611
  out(chalk.gray(" The auth schema step was skipped, because it writes. A real push runs it first."));
@@ -1734,7 +1640,10 @@ async function dbCommand(subcommand, rawArgs) {
1734
1640
  out(chalk.gray(" Aborted. No changes were made."));
1735
1641
  throw new CommandStopped("The destructive push was declined.");
1736
1642
  }
1737
- } else outWarn(chalk.yellow(` Proceeding because ${optIn ?? "--allow-destructive"} was passed.`));
1643
+ } else {
1644
+ outWarn(chalk.yellow(` Proceeding because ${optIn ?? "--allow-destructive"} was passed.`));
1645
+ warnIfYesApprovedDestructive(optIn, destructive.length);
1646
+ }
1738
1647
  }
1739
1648
  if (policyPlan?.summary) {
1740
1649
  out(chalk.gray(policyPlan.summary));
@@ -1825,7 +1734,7 @@ async function dbCommand(subcommand, rawArgs) {
1825
1734
  if (databaseUrl) {
1826
1735
  await ensureRlsUserRole(databaseUrl);
1827
1736
  await retireLegacyAuthSchema(databaseUrl);
1828
- const { stampMigratedPolicies } = await import("./migrate-policy-stamps-3eqfhes6.js");
1737
+ const { stampMigratedPolicies } = await import("./migrate-policy-stamps-BQtFHApx.js");
1829
1738
  await stampMigratedPolicies(databaseUrl, collectionsPath);
1830
1739
  }
1831
1740
  }
@@ -1844,12 +1753,43 @@ async function dbCommand(subcommand, rawArgs) {
1844
1753
  *
1845
1754
  * @returns `schema.index` of each one missing when this started.
1846
1755
  */
1756
+ /**
1757
+ * Appends to `schema.sql` the live DEFAULT of every column declared
1758
+ * `databaseDefault: true`, so Atlas plans no `DROP DEFAULT` for it.
1759
+ *
1760
+ * Fails CLOSED, like the exclude lists: without the catalogue the desired state
1761
+ * says "no DEFAULT", and the push would drop each of them. A project declaring
1762
+ * no such column reads nothing.
1763
+ */
1764
+ async function keepDatabaseOwnedDefaults(databaseUrl, collectionsPath) {
1765
+ const { loadCollections } = await import("./doctor-DW_mlTzz.js").then((n) => n.t);
1766
+ const { databaseOwnedDefaultColumns, readKeptDefaults, withKeptDefaults } = await import("./push-kept-defaults-BGOlOqRQ.js");
1767
+ const columns = databaseOwnedDefaultColumns(await loadCollections(path.resolve(process.cwd(), collectionsPath)));
1768
+ if (columns.length === 0) return;
1769
+ let kept;
1770
+ try {
1771
+ kept = await withAppClient(databaseUrl, (client) => readKeptDefaults({ query: async (text) => ({ rows: (await client.query(text)).rows }) }, columns));
1772
+ } catch (err) {
1773
+ outError(chalk.red("\n✗ Aborting push: could not read the DEFAULTs of the columns declared `databaseDefault: true`."));
1774
+ outError(chalk.gray(` ${err instanceof Error ? err.message : String(err)}`));
1775
+ outError(chalk.gray(" Refusing to plan without them: the push would drop each of those DEFAULTs."));
1776
+ const hint = diagnoseDbError(err, databaseUrl);
1777
+ if (hint) outError(hint);
1778
+ throw new CommandStopped("Could not read the DEFAULTs to keep.");
1779
+ }
1780
+ if (kept.length === 0) return;
1781
+ const desiredSqlFile = path.join(generatedSqlDir(), "schema.sql");
1782
+ fs.writeFileSync(desiredSqlFile, withKeptDefaults(fs.readFileSync(desiredSqlFile, "utf-8"), kept));
1783
+ out(chalk.gray(` Keeping ${kept.length} DEFAULT(s) the database holds, for columns declared \`databaseDefault: true\`:`));
1784
+ for (const each of kept) out(chalk.gray(` ${each.table}.${each.column} DEFAULT ${each.expression}`));
1785
+ out("");
1786
+ }
1847
1787
  /** Renames relation columns that carry their pre-0.13 derived name, as boot does; a dry-run only names them. */
1848
1788
  async function renameLegacyForeignKeys(databaseUrl, collectionsPath, dryRun) {
1849
1789
  let renames;
1850
1790
  try {
1851
- const { loadCollections } = await import("./doctor-BT--1kq3.js").then((n) => n.t);
1852
- const { legacyForeignKeyRenames } = await import("./push-legacy-renames-CLgYYwPE.js");
1791
+ const { loadCollections } = await import("./doctor-DW_mlTzz.js").then((n) => n.t);
1792
+ const { legacyForeignKeyRenames } = await import("./push-legacy-renames-BwaKi8xF.js");
1853
1793
  const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
1854
1794
  renames = await withAppClient(databaseUrl, (client) => legacyForeignKeyRenames({ query: async (text) => ({ rows: (await client.query(text)).rows }) }, collections));
1855
1795
  } catch {
@@ -1868,8 +1808,8 @@ async function renameLegacyForeignKeys(databaseUrl, collectionsPath, dryRun) {
1868
1808
  /** Names the enum labels the database keeps after the collections dropped them. See `schema/enum-labels-kept.ts`. */
1869
1809
  async function reportKeptEnumLabels(databaseUrl, collectionsPath) {
1870
1810
  try {
1871
- const { loadCollections } = await import("./doctor-BT--1kq3.js").then((n) => n.t);
1872
- const { keptEnumLabels, formatKeptEnumLabelsNotice } = await import("./enum-labels-kept-nheku1pB.js");
1811
+ const { loadCollections } = await import("./doctor-DW_mlTzz.js").then((n) => n.t);
1812
+ const { keptEnumLabels, formatKeptEnumLabelsNotice } = await import("./enum-labels-kept-PcPW65xy.js");
1873
1813
  const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
1874
1814
  const kept = await withAppClient(databaseUrl, (client) => keptEnumLabels({ query: async (text) => ({ rows: (await client.query(text)).rows }) }, collections));
1875
1815
  if (kept.length > 0) outWarn(chalk.yellow(formatKeptEnumLabelsNotice(kept)));
@@ -1878,8 +1818,8 @@ async function reportKeptEnumLabels(databaseUrl, collectionsPath) {
1878
1818
  async function prebuildForeignKeyIndexes(databaseUrl, collectionsPath, dryRun) {
1879
1819
  let missing;
1880
1820
  try {
1881
- const { loadCollections } = await import("./doctor-BT--1kq3.js").then((n) => n.t);
1882
- const { missingForeignKeyIndexes } = await import("./push-index-prebuild-COd7i6wm.js");
1821
+ const { loadCollections } = await import("./doctor-DW_mlTzz.js").then((n) => n.t);
1822
+ const { missingForeignKeyIndexes } = await import("./push-index-prebuild-UTvB5pqH.js");
1883
1823
  const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
1884
1824
  missing = await withAppClient(databaseUrl, (client) => missingForeignKeyIndexes({ query: async (text) => ({ rows: (await client.query(text)).rows }) }, collections));
1885
1825
  } catch {
@@ -1889,7 +1829,7 @@ async function prebuildForeignKeyIndexes(databaseUrl, collectionsPath, dryRun) {
1889
1829
  if (missing.length === 0 || dryRun) return names;
1890
1830
  out(chalk.gray(` Building ${missing.length} foreign-key index(es) on existing tables first — CONCURRENTLY, so writes go on:`));
1891
1831
  try {
1892
- const { buildDeferredIndexes } = await import("./deferred-index-build-BE0I6sYU.js").then((n) => n.r);
1832
+ const { buildDeferredIndexes } = await import("./deferred-index-build-CqaOv1Xx.js").then((n) => n.r);
1893
1833
  const outcome = await withAppClient(databaseUrl, (client) => buildDeferredIndexes(async () => ({
1894
1834
  dedicated: true,
1895
1835
  query: async (text) => ({ rows: (await client.query(text)).rows }),
@@ -1937,9 +1877,9 @@ async function lockManagedTables(databaseUrl, collectionsPath) {
1937
1877
  let unsecured;
1938
1878
  let userRole;
1939
1879
  try {
1940
- const { lockCollectionTables } = await import("./ensure-collection-policies-BnGOURpG.js");
1941
- const { loadCollections } = await import("./doctor-BT--1kq3.js").then((n) => n.t);
1942
- userRole = (await import("./rls-enforcement-DDyNoOSy.js").then((n) => n.c)).REBASE_USER_ROLE;
1880
+ const { lockCollectionTables } = await import("./ensure-collection-policies-DLWgabQ2.js");
1881
+ const { loadCollections } = await import("./doctor-DW_mlTzz.js").then((n) => n.t);
1882
+ userRole = (await import("./rls-enforcement-BOHuHVlc.js").then((n) => n.l)).REBASE_USER_ROLE;
1943
1883
  const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
1944
1884
  ({unsecured} = await withAppClient(databaseUrl, (client) => lockCollectionTables({ query: async (text) => ({ rows: (await client.query(text)).rows }) }, collections)));
1945
1885
  } catch (err) {
@@ -1974,8 +1914,8 @@ async function lockManagedTables(databaseUrl, collectionsPath) {
1974
1914
  async function ensureAuthTables(databaseUrl, collectionsPath) {
1975
1915
  try {
1976
1916
  const { drizzle } = await import("drizzle-orm/node-postgres");
1977
- const { ensureAuthTablesExist } = await import("./ensure-tables-l_OgFQjN.js").then((n) => n.n);
1978
- const { loadCollections } = await import("./doctor-BT--1kq3.js").then((n) => n.t);
1917
+ const { ensureAuthTablesExist } = await import("./ensure-tables-4nA-tfwi.js").then((n) => n.n);
1918
+ const { loadCollections } = await import("./doctor-DW_mlTzz.js").then((n) => n.t);
1979
1919
  const authCollection = (await loadCollections(path.resolve(process.cwd(), collectionsPath))).find((c) => {
1980
1920
  const a = c.auth;
1981
1921
  return a === true || typeof a === "object" && a !== null && a.enabled === true;
@@ -1995,7 +1935,7 @@ async function ensureRlsUserRole(databaseUrl) {
1995
1935
  const schemas = ["public", "rebase"];
1996
1936
  let rls;
1997
1937
  try {
1998
- rls = await import("./rls-enforcement-DDyNoOSy.js").then((n) => n.c);
1938
+ rls = await import("./rls-enforcement-BOHuHVlc.js").then((n) => n.l);
1999
1939
  } catch (err) {
2000
1940
  outError(chalk.red(`\n ✗ Could not load the RLS provisioning module: ${err instanceof Error ? err.message : String(err)}`));
2001
1941
  return;
@@ -2055,9 +1995,9 @@ async function applyPolicies(databaseUrl, collectionsPath) {
2055
1995
  */
2056
1996
  async function policyStamping(databaseUrl, collectionsPath, policiesSql) {
2057
1997
  try {
2058
- const { loadCollections } = await import("./doctor-BT--1kq3.js").then((n) => n.t);
1998
+ const { loadCollections } = await import("./doctor-DW_mlTzz.js").then((n) => n.t);
2059
1999
  const { planCollectionPolicies } = await import("./generate-postgres-ddl-logic-DPl1oH_z.js").then((n) => n.o);
2060
- const { policyStampSql } = await import("./ensure-collection-policies-BnGOURpG.js");
2000
+ const { policyStampSql } = await import("./ensure-collection-policies-DLWgabQ2.js");
2061
2001
  const { pinSearchPath } = await import("./connection-czvI7TZL.js").then((n) => n.n);
2062
2002
  return {
2063
2003
  statements: stampableTables(planCollectionPolicies(await loadCollections(path.resolve(process.cwd(), collectionsPath))), policiesSql).map((plan) => policyStampSql(plan)).filter((statement) => statement !== void 0),
@@ -2075,8 +2015,8 @@ async function policyStamping(databaseUrl, collectionsPath, policiesSql) {
2075
2015
  */
2076
2016
  async function readPolicyPushPlan(databaseUrl, collectionsPath) {
2077
2017
  try {
2078
- const { formatPolicyPushPlan, planPolicyPush, summarizePolicyPushPlan } = await import("./push-policy-plan-D2gpQsdv.js");
2079
- const { loadCollections } = await import("./doctor-BT--1kq3.js").then((n) => n.t);
2018
+ const { formatPolicyPushPlan, planPolicyPush, summarizePolicyPushPlan } = await import("./push-policy-plan-DQ2tWceD.js");
2019
+ const { loadCollections } = await import("./doctor-DW_mlTzz.js").then((n) => n.t);
2080
2020
  const { pinSearchPath } = await import("./connection-czvI7TZL.js").then((n) => n.n);
2081
2021
  const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
2082
2022
  const plan = await withAppClient(databaseUrl, (client) => planPolicyPush({ query: async (text, values) => ({ rows: (await client.query(text, values)).rows }) }, collections, { searchPath: pinSearchPath(databaseUrl) === databaseUrl ? void 0 : "public" }));
@@ -2105,7 +2045,7 @@ async function readPolicyPushPlan(databaseUrl, collectionsPath) {
2105
2045
  async function reconcilePolicies(databaseUrl, collectionsPath) {
2106
2046
  try {
2107
2047
  const { checkPolicyDrift, dropOrphanedPolicies, formatPolicyDrift, hasDrift } = await import("./policy-drift-X4A-xAzv.js").then((n) => n.s);
2108
- const { loadCollections } = await import("./doctor-BT--1kq3.js").then((n) => n.t);
2048
+ const { loadCollections } = await import("./doctor-DW_mlTzz.js").then((n) => n.t);
2109
2049
  const collections = await loadCollections(path.resolve(process.cwd(), collectionsPath));
2110
2050
  await withAppClient(databaseUrl, async (client) => {
2111
2051
  const drift = await checkPolicyDrift(client, collections);
@@ -2148,6 +2088,8 @@ var BRANCH_FLAGS = {
2148
2088
  "--older-than": String,
2149
2089
  "--include-dev-diff": Boolean,
2150
2090
  "--from": String,
2091
+ "--json": Boolean,
2092
+ "--sample-limit": Number,
2151
2093
  "--yes": Boolean,
2152
2094
  "-y": "--yes"
2153
2095
  };
@@ -2189,7 +2131,7 @@ async function branchCommand(rawArgs) {
2189
2131
  process.exit(1);
2190
2132
  }
2191
2133
  const { DatabasePoolManager } = await import("./databasePoolManager-3stMNFL-.js").then((n) => n.n);
2192
- const { BranchService, reopenClosedDatabases, reopenedDatabasesNotice } = await import("./BranchService-ClklxyAQ.js").then((n) => n.n);
2134
+ const { BranchService, reopenClosedDatabases, reopenedDatabasesNotice } = await import("./BranchService-CWBnDhcv.js").then((n) => n.n);
2193
2135
  const { drizzle } = await import("drizzle-orm/node-postgres");
2194
2136
  const { Pool } = await import("pg");
2195
2137
  const healed = await reopenClosedDatabases(databaseUrl, [branchAction === "create" ? createSource(rawArgs) : void 0, process.env[ACTIVE_BRANCH_DATABASE_ENV]]);
@@ -2287,6 +2229,41 @@ async function branchCommand(rawArgs) {
2287
2229
  out("");
2288
2230
  break;
2289
2231
  }
2232
+ case "diff": {
2233
+ const name = rawArgs[3];
2234
+ if (!name || name.startsWith("-")) {
2235
+ outError(chalk.red("✗ Branch name is required."));
2236
+ out(chalk.gray(" Usage: rebase db branch diff <name> [--json] [--sample-limit <n>]"));
2237
+ process.exit(1);
2238
+ }
2239
+ const parsed = (0, import_arg.default)(BRANCH_FLAGS, {
2240
+ argv: rawArgs.slice(4),
2241
+ permissive: true
2242
+ });
2243
+ const { diffDatabaseBranch } = await import("./branch-diff-CxE4VlxT.js").then((n) => n.r);
2244
+ const { branchDiffReport } = await import("./branch-diff-report-DpvVf7hR.js");
2245
+ const admin = {
2246
+ executeSql: async (text, options) => (await poolManager.getPool(options?.database ?? poolManager.defaultDatabaseName).query(text, options?.params)).rows,
2247
+ createBranch: (branchName, options) => branchService.createBranch(branchName, options),
2248
+ deleteBranch: (branchName) => branchService.deleteBranch(branchName),
2249
+ listBranches: () => branchService.listBranches(),
2250
+ getBranchInfo: async (branchName) => await branchService.hasBranchRegistry() ? branchService.getBranchInfo(branchName) : void 0
2251
+ };
2252
+ let diff;
2253
+ try {
2254
+ diff = await diffDatabaseBranch(admin, name, { sampleLimit: parsed["--sample-limit"] });
2255
+ } catch (error) {
2256
+ outError(chalk.red(` ✗ ${error instanceof Error ? error.message : String(error)}`));
2257
+ process.exit(1);
2258
+ }
2259
+ if (parsed["--json"] === true) out(JSON.stringify(diff, null, 2));
2260
+ else {
2261
+ out("");
2262
+ for (const line of branchDiffReport(diff)) out(` ${line}`);
2263
+ out("");
2264
+ }
2265
+ break;
2266
+ }
2290
2267
  case "prune": {
2291
2268
  const parsed = (0, import_arg.default)(BRANCH_FLAGS, {
2292
2269
  argv: rawArgs.slice(3),
@@ -2392,6 +2369,7 @@ ${chalk.green.bold("Commands")}
2392
2369
  ${chalk.blue.bold("switch")} [<name>|--off] Point this checkout at a branch
2393
2370
  ${chalk.blue.bold("delete")} <name> Delete a branch
2394
2371
  ${chalk.blue.bold("info")} <name> Show branch details
2372
+ ${chalk.blue.bold("diff")} <name> [--json] What the branch changed: schema, policies, rows
2395
2373
  ${chalk.blue.bold("prune")} [--older-than <14|14d|2w>] Remove branches nothing is using
2396
2374
 
2397
2375
  ${chalk.green.bold("Why prune")}
@@ -2405,6 +2383,8 @@ ${chalk.green.bold("Options")}
2405
2383
  Postgres refuses to copy or drop a database
2406
2384
  anything else is connected to — usually your
2407
2385
  own \`rebase dev\`.
2386
+ ${chalk.blue.bold("--json")} diff: print the whole diff as JSON, with up
2387
+ to --sample-limit rows a table (default 100).
2408
2388
  ${chalk.blue.bold("--include-dev-diff")} Also prune the Atlas scratch databases
2409
2389
  \`db push\` leaves behind.
2410
2390
  ${chalk.blue.bold("--yes, -y")} Skip prune's confirmation prompt. Dropping a
@@ -2698,7 +2678,7 @@ async function schemaStaleCommand(rawArgs) {
2698
2678
  const schemaFile = path.resolve(process.cwd(), outputPath);
2699
2679
  const fix = Boolean(argsList["--fix"]);
2700
2680
  const generatedExists = fs.existsSync(schemaFile);
2701
- const { loadCollections } = await import("./doctor-BT--1kq3.js").then((n) => n.t);
2681
+ const { loadCollections } = await import("./doctor-DW_mlTzz.js").then((n) => n.t);
2702
2682
  const { findLegacyForeignKeyNames, compareGeneratedDeclarations, staleVerdict } = await import("./generated-schema-staleness-BvNF3i-s.js").then((n) => n.r);
2703
2683
  const { generateSchema } = await import("./generate-drizzle-schema-logic-CPzCRD-8.js").then((n) => n.n);
2704
2684
  const { relationalCollections } = await import("@rebasepro/common");