@rebasepro/server-postgres 0.25.0 → 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 (144) hide show
  1. package/dist/{BranchService-iXXZeVYD.js → BranchService-CWBnDhcv.js} +88 -12
  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/backup-cli.d.ts +40 -3
  8. package/dist/backup/backup-service.d.ts +11 -0
  9. package/dist/{backup-service-Bb39plam.js → backup-cli-PoRG-zEq.js} +777 -10
  10. package/dist/backup-cli-PoRG-zEq.js.map +1 -0
  11. package/dist/{boot-ddl-s_3Hkz94.js → boot-ddl-EsgqDkx0.js} +3 -3
  12. package/dist/{boot-ddl-s_3Hkz94.js.map → boot-ddl-EsgqDkx0.js.map} +1 -1
  13. package/dist/branch-argv.d.ts +15 -0
  14. package/dist/branch-diff-CxE4VlxT.js +1396 -0
  15. package/dist/branch-diff-CxE4VlxT.js.map +1 -0
  16. package/dist/branch-diff-report-DpvVf7hR.js +48 -0
  17. package/dist/branch-diff-report-DpvVf7hR.js.map +1 -0
  18. package/dist/branching/branch-diff-report.d.ts +7 -0
  19. package/dist/branching/branch-diff.d.ts +115 -0
  20. package/dist/{cli-console-DQ_K-sdM.js → cli-console-f0k6_TsJ.js} +41 -9
  21. package/dist/cli-console-f0k6_TsJ.js.map +1 -0
  22. package/dist/cli-console.d.ts +17 -1
  23. package/dist/{cli-errors-DwQnNATs.js → cli-errors-DsYyNYzd.js} +90 -11
  24. package/dist/cli-errors-DsYyNYzd.js.map +1 -0
  25. package/dist/cli-errors.d.ts +14 -0
  26. package/dist/cli-flags.d.ts +51 -0
  27. package/dist/{cli-prompt-egtNKkZl.js → cli-prompt-DXG0yk-h.js} +39 -4
  28. package/dist/{cli-prompt-egtNKkZl.js.map → cli-prompt-DXG0yk-h.js.map} +1 -1
  29. package/dist/cli.js +121 -271
  30. package/dist/cli.js.map +1 -1
  31. package/dist/{collections-schema-version-T6cGvrDi.js → collections-schema-version-D7bDI0PJ.js} +13 -9
  32. package/dist/collections-schema-version-D7bDI0PJ.js.map +1 -0
  33. package/dist/{column-plan-helpers-D_V4oaZh.js → column-plan-helpers-IG6uFb1s.js} +2 -2
  34. package/dist/{column-plan-helpers-D_V4oaZh.js.map → column-plan-helpers-IG6uFb1s.js.map} +1 -1
  35. package/dist/{connection-C938nR3E.js → connection-czvI7TZL.js} +57 -10
  36. package/dist/connection-czvI7TZL.js.map +1 -0
  37. package/dist/{databasePoolManager-Yqdw14wY.js → databasePoolManager-3stMNFL-.js} +4 -6
  38. package/dist/databasePoolManager-3stMNFL-.js.map +1 -0
  39. package/dist/db-command-line.d.ts +14 -0
  40. package/dist/db-subcommands.d.ts +11 -0
  41. package/dist/{deferred-index-build-Cis_HoGn.js → deferred-index-build-CqaOv1Xx.js} +8 -8
  42. package/dist/{deferred-index-build-Cis_HoGn.js.map → deferred-index-build-CqaOv1Xx.js.map} +1 -1
  43. package/dist/{doctor-DvJH-IQG.js → doctor-DW_mlTzz.js} +186 -42
  44. package/dist/doctor-DW_mlTzz.js.map +1 -0
  45. package/dist/{ensure-collection-policies-CCqayoUz.js → ensure-collection-policies-DLWgabQ2.js} +13 -10
  46. package/dist/ensure-collection-policies-DLWgabQ2.js.map +1 -0
  47. package/dist/{ensure-collection-tables-DvvImXDe.js → ensure-collection-tables-CFIRq8ni.js} +9 -9
  48. package/dist/{ensure-collection-tables-DvvImXDe.js.map → ensure-collection-tables-CFIRq8ni.js.map} +1 -1
  49. package/dist/{ensure-tables-Dpgwx5sy.js → ensure-tables-4nA-tfwi.js} +5 -5
  50. package/dist/{ensure-tables-Dpgwx5sy.js.map → ensure-tables-4nA-tfwi.js.map} +1 -1
  51. package/dist/{enum-labels-kept-CUlUlRkO.js → enum-labels-kept-PcPW65xy.js} +3 -3
  52. package/dist/{enum-labels-kept-CUlUlRkO.js.map → enum-labels-kept-PcPW65xy.js.map} +1 -1
  53. package/dist/{generate-drizzle-schema-C3EAP9HE.js → generate-drizzle-schema-DC0BOUpn.js} +3 -3
  54. package/dist/{generate-drizzle-schema-C3EAP9HE.js.map → generate-drizzle-schema-DC0BOUpn.js.map} +1 -1
  55. package/dist/{generate-drizzle-schema-logic-DA3zM8zc.js → generate-drizzle-schema-logic-CPzCRD-8.js} +4 -4
  56. package/dist/{generate-drizzle-schema-logic-DA3zM8zc.js.map → generate-drizzle-schema-logic-CPzCRD-8.js.map} +1 -1
  57. package/dist/{generate-files-ClmOUHXj.js → generate-files-BP-4W_gm.js} +6 -6
  58. package/dist/{generate-files-ClmOUHXj.js.map → generate-files-BP-4W_gm.js.map} +1 -1
  59. package/dist/{generate-next-step-DCeS4LrJ.js → generate-next-step-DgK5BlBn.js} +2 -2
  60. package/dist/{generate-next-step-DCeS4LrJ.js.map → generate-next-step-DgK5BlBn.js.map} +1 -1
  61. package/dist/{generate-postgres-ddl-logic-4eUP4t8w.js → generate-postgres-ddl-logic-DPl1oH_z.js} +4 -4
  62. package/dist/{generate-postgres-ddl-logic-4eUP4t8w.js.map → generate-postgres-ddl-logic-DPl1oH_z.js.map} +1 -1
  63. package/dist/generated-schema-diff-eZCklRZQ.js +1025 -0
  64. package/dist/generated-schema-diff-eZCklRZQ.js.map +1 -0
  65. package/dist/{generated-schema-staleness-FlxM2i4i.js → generated-schema-staleness-BvNF3i-s.js} +2 -2
  66. package/dist/{generated-schema-staleness-FlxM2i4i.js.map → generated-schema-staleness-BvNF3i-s.js.map} +1 -1
  67. package/dist/history/HistoryService.d.ts +7 -0
  68. package/dist/index.d.ts +21 -0
  69. package/dist/index.es.js +288 -11
  70. package/dist/index.es.js.map +1 -1
  71. package/dist/{introspect-db-logic-CI5CU_n0.js → introspect-db-logic-DgQVzccW.js} +2 -2
  72. package/dist/{introspect-db-logic-CI5CU_n0.js.map → introspect-db-logic-DgQVzccW.js.map} +1 -1
  73. package/dist/{rls-bootstrap-sql-Bd-hJk08.js → migrate-baseline-B-2Ef4Mv.js} +718 -41
  74. package/dist/migrate-baseline-B-2Ef4Mv.js.map +1 -0
  75. package/dist/migrate-policy-stamps-BQtFHApx.js +89 -0
  76. package/dist/migrate-policy-stamps-BQtFHApx.js.map +1 -0
  77. package/dist/{open-rebase-BKA7Nyoe.js → open-rebase-C8jQ6s5a.js} +324 -1047
  78. package/dist/open-rebase-C8jQ6s5a.js.map +1 -0
  79. package/dist/open-rebase.d.ts +9 -1
  80. package/dist/{pg-error-utils-Bv5n2PLn.js → pg-error-utils-DhiUTlVx.js} +2 -2
  81. package/dist/{pg-error-utils-Bv5n2PLn.js.map → pg-error-utils-DhiUTlVx.js.map} +1 -1
  82. package/dist/{plan-schema-CXOucCw-.js → plan-schema-Czf3q4-Q.js} +4 -4
  83. package/dist/{plan-schema-CXOucCw-.js.map → plan-schema-Czf3q4-Q.js.map} +1 -1
  84. package/dist/{policy-drift-vm0BcvcE.js → policy-drift-X4A-xAzv.js} +3 -3
  85. package/dist/{policy-drift-vm0BcvcE.js.map → policy-drift-X4A-xAzv.js.map} +1 -1
  86. package/dist/pool-disconnects.d.ts +28 -0
  87. package/dist/{push-index-prebuild-DIequ4bV.js → push-index-prebuild-UTvB5pqH.js} +4 -4
  88. package/dist/{push-index-prebuild-DIequ4bV.js.map → push-index-prebuild-UTvB5pqH.js.map} +1 -1
  89. package/dist/push-kept-defaults-BGOlOqRQ.js +137 -0
  90. package/dist/push-kept-defaults-BGOlOqRQ.js.map +1 -0
  91. package/dist/{push-legacy-renames-BqJut4F3.js → push-legacy-renames-BwaKi8xF.js} +4 -4
  92. package/dist/{push-legacy-renames-BqJut4F3.js.map → push-legacy-renames-BwaKi8xF.js.map} +1 -1
  93. package/dist/{push-policy-plan-CvX8hiPb.js → push-policy-plan-DQ2tWceD.js} +5 -5
  94. package/dist/{push-policy-plan-CvX8hiPb.js.map → push-policy-plan-DQ2tWceD.js.map} +1 -1
  95. package/dist/{render-ddl-CQNwZdse.js → render-ddl-4v88y-Gv.js} +4 -4
  96. package/dist/{render-ddl-CQNwZdse.js.map → render-ddl-4v88y-Gv.js.map} +1 -1
  97. package/dist/{rls-enforcement-D7P4-ZYX.js → rls-enforcement-BOHuHVlc.js} +43 -4
  98. package/dist/rls-enforcement-BOHuHVlc.js.map +1 -0
  99. package/dist/schema/destructive-sql.d.ts +16 -0
  100. package/dist/schema/doctor-cli.js +5 -5
  101. package/dist/schema/doctor.d.ts +20 -2
  102. package/dist/schema/ensure-collection-policies.d.ts +4 -1
  103. package/dist/schema/generate-drizzle-schema.js +1 -1
  104. package/dist/schema/generate-postgres-ddl.js +2 -2
  105. package/dist/schema/generated-schema-diff.d.ts +21 -0
  106. package/dist/schema/introspect-db.js +5 -6
  107. package/dist/schema/introspect-db.js.map +1 -1
  108. package/dist/schema/migrate-policy-stamps.d.ts +23 -0
  109. package/dist/schema/migration-freshness.d.ts +69 -0
  110. package/dist/schema/push-kept-defaults.d.ts +57 -0
  111. package/dist/schema/push-policy-plan.d.ts +5 -0
  112. package/dist/schema/rls-bootstrap-sql.d.ts +47 -10
  113. package/dist/security/rls-enforcement.d.ts +18 -0
  114. package/dist/services/BranchService.d.ts +20 -2
  115. package/dist/services/FetchService.d.ts +12 -0
  116. package/dist/services/channel-presence.d.ts +3 -1
  117. package/dist/services/nested-path.d.ts +16 -0
  118. package/dist/services/pg-notify-listener.d.ts +2 -0
  119. package/dist/services/realtimeService.d.ts +18 -1
  120. package/dist/{source-Bv5KPeIx.js → source-fZb92pN8.js} +2 -2
  121. package/dist/{source-Bv5KPeIx.js.map → source-fZb92pN8.js.map} +1 -1
  122. package/dist/{vector-index-BFhyEaiz.js → vector-index-DsQ3V2d2.js} +2 -2
  123. package/dist/{vector-index-BFhyEaiz.js.map → vector-index-DsQ3V2d2.js.map} +1 -1
  124. package/dist/{websocket-MC0zTBar.js → websocket-CKYc3V4N.js} +56 -14
  125. package/dist/websocket-CKYc3V4N.js.map +1 -0
  126. package/package.json +7 -7
  127. package/dist/BranchService-iXXZeVYD.js.map +0 -1
  128. package/dist/backup-cli-B2CAn9fR.js +0 -647
  129. package/dist/backup-cli-B2CAn9fR.js.map +0 -1
  130. package/dist/backup-service-Bb39plam.js.map +0 -1
  131. package/dist/cli-console-DQ_K-sdM.js.map +0 -1
  132. package/dist/cli-errors-DwQnNATs.js.map +0 -1
  133. package/dist/cli-flags-D-b0qL9T.js +0 -206
  134. package/dist/cli-flags-D-b0qL9T.js.map +0 -1
  135. package/dist/collections-schema-version-T6cGvrDi.js.map +0 -1
  136. package/dist/connection-C938nR3E.js.map +0 -1
  137. package/dist/databasePoolManager-Yqdw14wY.js.map +0 -1
  138. package/dist/doctor-DvJH-IQG.js.map +0 -1
  139. package/dist/ensure-collection-policies-CCqayoUz.js.map +0 -1
  140. package/dist/open-rebase-BKA7Nyoe.js.map +0 -1
  141. package/dist/rls-bootstrap-sql-Bd-hJk08.js.map +0 -1
  142. package/dist/rls-enforcement-D7P4-ZYX.js.map +0 -1
  143. package/dist/rolldown-runtime-V7rFbMRe.js +0 -39
  144. package/dist/websocket-MC0zTBar.js.map +0 -1
@@ -1,8 +1,10 @@
1
1
  import { createRequire as __createRequire } from "module";
2
2
  __createRequire(import.meta.url);
3
- import { i as __toESM, n as __exportAll, r as __require, t as __commonJSMin } from "./rolldown-runtime-V7rFbMRe.js";
3
+ import { a as __require, i as __exportAll, n as require_arg, o as __toESM, r as __commonJSMin } from "./cli-prompt-DXG0yk-h.js";
4
+ import { B as withAppClient, tt as redactConnectionPasswords } from "./cli-errors-DsYyNYzd.js";
4
5
  import { LEGACY_RLS_SCHEMA, REBASE_SCHEMA } from "@rebasepro/types";
5
6
  import { sqlRows } from "@rebasepro/common";
7
+ import { suggestNearMiss } from "@rebasepro/utils";
6
8
  import process$1, { execArgv, execPath, hrtime, platform } from "node:process";
7
9
  import { constants } from "node:os";
8
10
  import tty from "node:tty";
@@ -6192,6 +6194,7 @@ var { sendMessage, getOneMessage, getEachMessage, getCancelSignal } = getIpcExpo
6192
6194
  //#region src/schema/rls-bootstrap-sql.ts
6193
6195
  var rls_bootstrap_sql_exports = /* @__PURE__ */ __exportAll({
6194
6196
  DROP_LEGACY_AUTH_SCHEMA_SQL: () => DROP_LEGACY_AUTH_SCHEMA_SQL,
6197
+ LEGACY_AUTH_SCHEMA_CONTENTS_SQL: () => LEGACY_AUTH_SCHEMA_CONTENTS_SQL,
6195
6198
  LEGACY_RLS_DEPENDENTS_SQL: () => LEGACY_RLS_DEPENDENTS_SQL,
6196
6199
  LEGACY_RLS_FUNCTION_DEPENDENTS_SQL: () => LEGACY_RLS_FUNCTION_DEPENDENTS_SQL,
6197
6200
  RLS_BOOTSTRAP_SQL: () => RLS_BOOTSTRAP_SQL,
@@ -6356,6 +6359,60 @@ async function rlsRuntimeIsCurrent(run) {
6356
6359
  /** The same statements as one script, for migration files and raw clients. */
6357
6360
  var RLS_BOOTSTRAP_SQL = "-- Rebase schema + RLS helper functions (required by the policies below)\n" + RLS_BOOTSTRAP_STATEMENTS.map((s) => `${s};`).join("\n\n") + "\n";
6358
6361
  /**
6362
+ * The three functions every pre-1.0 release put in `auth`. (The auth tables
6363
+ * lived in `rebase`, or wherever the users collection was declared.)
6364
+ *
6365
+ * `uid()` is matched on either spelling of the principal because Rebase wrote
6366
+ * both. Until v0.10.0 its body was
6367
+ * `SELECT NULLIF(current_setting('app.user_id', true), '')`; v0.10.0 renamed
6368
+ * the GUC to `app.uid` and kept `app.user_id` as a fallback; v0.14.0 moved the
6369
+ * helpers to `rebase`. A database last booted before v0.10.0 therefore holds an
6370
+ * `auth.uid()` that reads `app.user_id` alone — as does anything that copied
6371
+ * that body, the Rebase control plane included — and matching `app.uid` only
6372
+ * left every one of them in place, with the schema around it.
6373
+ *
6374
+ * This is the one place either spelling is listed: the drop and the report of
6375
+ * what it left are both generated from it, so they cannot disagree about which
6376
+ * functions are Rebase's.
6377
+ */
6378
+ var LEGACY_AUTH_HELPERS = [
6379
+ {
6380
+ name: "uid",
6381
+ returns: "text",
6382
+ reads: ["app.uid", "app.user_id"]
6383
+ },
6384
+ {
6385
+ name: "jwt",
6386
+ returns: "jsonb",
6387
+ reads: ["app.jwt"]
6388
+ },
6389
+ {
6390
+ name: "roles",
6391
+ returns: "text",
6392
+ reads: ["app.user_roles"]
6393
+ }
6394
+ ];
6395
+ /**
6396
+ * `pg_proc p` has a Rebase helper's name, arity and result type: what
6397
+ * `CREATE OR REPLACE` would have left, whatever the body.
6398
+ *
6399
+ * Two things that share that shape are excluded outright: a body reading
6400
+ * `request.jwt` (Supabase's helpers and anything modelled on them) and a
6401
+ * function an extension installed (`pg_session_jwt` puts an `auth.jwt()
6402
+ * RETURNS jsonb` in the same schema).
6403
+ */
6404
+ var isLegacyHelperShaped = (helper) => `(
6405
+ p.proname = '${helper.name}' AND p.pronargs = 0
6406
+ AND pg_get_function_result(p.oid) = '${helper.returns}'
6407
+ AND strpos(p.prosrc, 'request.jwt') = 0
6408
+ AND NOT EXISTS (
6409
+ SELECT 1 FROM pg_depend e
6410
+ WHERE e.classid = 'pg_proc'::regclass AND e.objid = p.oid AND e.deptype = 'e'
6411
+ ))`;
6412
+ /** `pg_proc p` is a helper Rebase wrote: its shape, and a body reading one of its GUCs. */
6413
+ var isLegacyHelper = (helper) => `${isLegacyHelperShaped(helper)}
6414
+ AND (${helper.reads.map((guc) => `strpos(p.prosrc, '${guc}') > 0`).join(" OR ")})`;
6415
+ /**
6359
6416
  * Removes the pre-1.0 `auth` schema, but only when Rebase is what put it there.
6360
6417
  *
6361
6418
  * ## Why this is safe against a Supabase database
@@ -6363,11 +6420,12 @@ var RLS_BOOTSTRAP_SQL = "-- Rebase schema + RLS helper functions (required by th
6363
6420
  * Two independent guards, and both have to pass:
6364
6421
  *
6365
6422
  * 1. **Each function is identified before it is dropped.** Ours returns `text`
6366
- * and reads the `app.uid` GUC; Supabase's returns `uuid` and reads
6367
- * `request.jwt.claims`. Nothing is dropped on a signature we did not write,
6368
- * so a Supabase database — where our `CREATE OR REPLACE` could never have
6369
- * succeeded in the first place, Postgres refusing to change a return type —
6370
- * matches nothing and this is a no-op.
6423
+ * and reads the `app.uid` or `app.user_id` GUC; Supabase's returns `uuid`
6424
+ * and reads `request.jwt.claims`. Nothing is dropped on a signature we did
6425
+ * not write, so a Supabase database — where our `CREATE OR REPLACE` could
6426
+ * never have succeeded in the first place, Postgres refusing to change a
6427
+ * return type — matches nothing and this is a no-op. See
6428
+ * {@link LEGACY_AUTH_HELPERS} for exactly what is matched.
6371
6429
  * 2. **`DROP SCHEMA … RESTRICT`**, never CASCADE. If anything else at all still
6372
6430
  * lives in `auth` (Supabase's `users` table, its other helpers), the drop
6373
6431
  * fails and the schema stays. CASCADE here would be unrecoverable.
@@ -6388,41 +6446,21 @@ DECLARE
6388
6446
  BEGIN
6389
6447
  -- Each function is matched on its own result type and body, so a schema
6390
6448
  -- that merely shares the name keeps everything it has.
6391
- IF EXISTS (
6392
- SELECT 1 FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
6393
- WHERE n.nspname = 'auth' AND p.proname = 'uid'
6394
- AND pg_get_function_result(p.oid) = 'text'
6395
- AND p.prosrc LIKE '%app.uid%'
6396
- ) THEN
6397
- DROP FUNCTION auth.uid();
6398
- dropped_any := true;
6399
- END IF;
6400
-
6401
- IF EXISTS (
6402
- SELECT 1 FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
6403
- WHERE n.nspname = 'auth' AND p.proname = 'jwt'
6404
- AND pg_get_function_result(p.oid) = 'jsonb'
6405
- AND p.prosrc LIKE '%app.jwt%'
6406
- ) THEN
6407
- DROP FUNCTION auth.jwt();
6408
- dropped_any := true;
6409
- END IF;
6410
-
6411
- IF EXISTS (
6449
+ ${LEGACY_AUTH_HELPERS.map((helper) => ` IF EXISTS (
6412
6450
  SELECT 1 FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
6413
- WHERE n.nspname = 'auth' AND p.proname = 'roles'
6414
- AND pg_get_function_result(p.oid) = 'text'
6415
- AND p.prosrc LIKE '%app.user_roles%'
6451
+ WHERE n.nspname = '${LEGACY_RLS_SCHEMA}' AND ${isLegacyHelper(helper)}
6416
6452
  ) THEN
6417
- DROP FUNCTION auth.roles();
6453
+ DROP FUNCTION ${LEGACY_RLS_SCHEMA}.${helper.name}();
6418
6454
  dropped_any := true;
6419
- END IF;
6455
+ END IF;`).join("\n\n")}
6420
6456
 
6421
6457
  -- RESTRICT: only an empty schema goes. Anything else in there — including a
6422
- -- Supabase installation left untouched above — keeps it.
6458
+ -- Supabase installation left untouched above — keeps it. Swallowed so the
6459
+ -- helpers dropped above stay dropped; dropLegacyAuthSchema looks afterwards
6460
+ -- and says what is left.
6423
6461
  IF dropped_any THEN
6424
6462
  BEGIN
6425
- EXECUTE 'DROP SCHEMA auth RESTRICT';
6463
+ EXECUTE 'DROP SCHEMA ${LEGACY_RLS_SCHEMA} RESTRICT';
6426
6464
  EXCEPTION WHEN OTHERS THEN
6427
6465
  NULL;
6428
6466
  END;
@@ -6431,6 +6469,40 @@ END
6431
6469
  $rebase_drop_legacy$;
6432
6470
  `;
6433
6471
  /**
6472
+ * What the `auth` schema holds, one row per object keeping it alive.
6473
+ *
6474
+ * No rows: the schema does not exist. A single row with a `null` object: it
6475
+ * exists and is empty. Read from the namespace entries in `pg_depend`, which
6476
+ * are what `DROP SCHEMA … RESTRICT` itself consults — minus whatever goes with
6477
+ * its owner (a serial's sequence, an identity column's), which Postgres does not
6478
+ * name either.
6479
+ */
6480
+ var LEGACY_AUTH_SCHEMA_CONTENTS_SQL = `
6481
+ SELECT pg_describe_object(d.classid, d.objid, d.objsubid) AS object,
6482
+ CASE
6483
+ WHEN d.classid <> 'pg_proc'::regclass THEN NULL
6484
+ WHEN EXISTS (
6485
+ SELECT 1 FROM pg_proc p
6486
+ WHERE p.oid = d.objid
6487
+ AND (${LEGACY_AUTH_HELPERS.map((helper) => `(${isLegacyHelper(helper)})`).join(" OR ")})
6488
+ ) THEN 'ours'
6489
+ WHEN EXISTS (
6490
+ SELECT 1 FROM pg_proc p
6491
+ WHERE p.oid = d.objid
6492
+ AND (${LEGACY_AUTH_HELPERS.map(isLegacyHelperShaped).join(" OR ")})
6493
+ ) THEN 'lookalike'
6494
+ END AS helper
6495
+ FROM pg_namespace n
6496
+ LEFT JOIN pg_depend d
6497
+ ON d.refclassid = 'pg_namespace'::regclass AND d.refobjid = n.oid AND d.deptype = 'n'
6498
+ AND NOT EXISTS (
6499
+ SELECT 1 FROM pg_depend o
6500
+ WHERE o.classid = d.classid AND o.objid = d.objid AND o.deptype IN ('a', 'i')
6501
+ )
6502
+ WHERE n.nspname = '${LEGACY_RLS_SCHEMA}'
6503
+ ORDER BY helper NULLS LAST, object
6504
+ `;
6505
+ /**
6434
6506
  * Policies whose body still calls `auth.uid()` / `auth.roles()` / `auth.jwt()`.
6435
6507
  *
6436
6508
  * Postgres will not drop a function a policy depends on, so this is exactly the
@@ -6461,11 +6533,12 @@ var LEGACY_RLS_DEPENDENTS_SQL = `
6461
6533
  * exists, which fails at *query* time rather than at boot.
6462
6534
  *
6463
6535
  * A downstream project building on these helpers is not hypothetical: the Rebase
6464
- * control plane defines `auth.is_org_member(uuid)` and `auth.is_org_admin(uuid)`
6536
+ * control plane defined `auth.is_org_member(uuid)` and `auth.is_org_admin(uuid)`
6465
6537
  * in this very schema, each calling `auth.uid()` in a string body, and eleven of
6466
- * its row-level-security policies go through them. Every one of those would have
6467
- * started failing the first time a recompile left no policy referencing
6468
- * `auth.uid()` directly — the drop's own precondition.
6538
+ * its row-level-security policies went through them. Every one of those would
6539
+ * have started failing the first time a recompile left no policy referencing
6540
+ * `auth.uid()` directly — the drop's own precondition. (Its migration 0065 has
6541
+ * since moved both to `public`, calling `rebase.uid()`.)
6469
6542
  *
6470
6543
  * Matching on the body text is the only option available, and it is deliberately
6471
6544
  * broad: a false positive costs a schema that stays one release longer and says
@@ -6489,8 +6562,30 @@ var LEGACY_RLS_FUNCTION_DEPENDENTS_SQL = `
6489
6562
  * table not recompiled *yet* — and wrong for the one that never resolves: a
6490
6563
  * hand-written policy nothing will ever rewrite. Then the schema stays forever
6491
6564
  * and nothing ever says why.
6565
+ *
6566
+ * The drop itself had the same blind spot. A helper it did not recognise as
6567
+ * Rebase's stayed, and so did anything else in the schema, behind a
6568
+ * `DROP SCHEMA … RESTRICT` whose failure is swallowed so the helpers it did
6569
+ * drop stay dropped. The control plane's `auth.uid()` was one such: it read
6570
+ * `app.user_id` alone, the drop looked for `app.uid`, and the schema stayed
6571
+ * with no word said. So the schema is read twice. Before, so that one with no
6572
+ * Rebase helper in it is left alone: one already gone, as on every boot after
6573
+ * the drop, or one that was never Rebase's, like Supabase's — whose own
6574
+ * policies the checks below would otherwise report as blockers. After, to name
6575
+ * whatever is still there.
6492
6576
  */
6493
6577
  async function dropLegacyAuthSchema(run, report) {
6578
+ let before;
6579
+ try {
6580
+ before = sqlRows(await run(LEGACY_AUTH_SCHEMA_CONTENTS_SQL));
6581
+ } catch {
6582
+ return;
6583
+ }
6584
+ if (!before.some((o) => o.helper !== null)) return;
6585
+ if (!before.some((o) => o.helper === "ours")) {
6586
+ report.warn(describeLegacyAuthLeftovers(before, void 0));
6587
+ return;
6588
+ }
6494
6589
  let blockers;
6495
6590
  try {
6496
6591
  blockers = sqlRows(await run(LEGACY_RLS_DEPENDENTS_SQL));
@@ -6511,13 +6606,595 @@ async function dropLegacyAuthSchema(run, report) {
6511
6606
  report.warn(`The pre-1.0 \`${LEGACY_RLS_SCHEMA}\` schema cannot be removed yet: ${borrowers.length} ${borrowers.length === 1 ? "function calls" : "functions call"} \`${LEGACY_RLS_SCHEMA}.uid()\` and friends from its own body. Postgres records no dependency for that — a SQL body written as a string literal is never parsed — so dropping the helpers would leave these pointing at nothing, and they would fail when a query reached them rather than now. Repoint them at \`${REBASE_SCHEMA}.uid()\`, after which the schema goes on its own:\n` + borrowers.map((b) => ` • ${b.schema}.${b.function}()`).join("\n"));
6512
6607
  return;
6513
6608
  }
6609
+ let failure;
6514
6610
  try {
6515
6611
  await run(DROP_LEGACY_AUTH_SCHEMA_SQL);
6516
6612
  } catch (err) {
6517
- report.info(`Left the \`${LEGACY_RLS_SCHEMA}\` schema in place: ` + (err instanceof Error ? err.message : String(err)));
6613
+ const detail = err instanceof Error && "detail" in err && typeof err.detail === "string" ? err.detail : "";
6614
+ failure = (err instanceof Error ? err.message : String(err)) + (detail ? ` (${detail.replace(/\n/g, "; ")})` : "");
6615
+ }
6616
+ let left;
6617
+ try {
6618
+ left = sqlRows(await run(LEGACY_AUTH_SCHEMA_CONTENTS_SQL));
6619
+ } catch {
6620
+ if (failure) report.info(`Left the \`${LEGACY_RLS_SCHEMA}\` schema in place: ${failure}`);
6621
+ return;
6622
+ }
6623
+ if (left.length === 0) return;
6624
+ report.warn(describeLegacyAuthLeftovers(left, failure));
6625
+ }
6626
+ /** At most this many objects are named; a schema this full is plainly in use. */
6627
+ var MAX_LEFTOVERS_NAMED = 20;
6628
+ /**
6629
+ * The warning for an `auth` schema the drop left standing.
6630
+ *
6631
+ * Said once for objects that are not Rebase's: on the next boot the schema holds
6632
+ * no helper, so {@link dropLegacyAuthSchema} leaves it alone — it is somebody
6633
+ * else's schema by then. A helper that is still there, Rebase's or a lookalike,
6634
+ * keeps it said on every boot until it goes.
6635
+ */
6636
+ function describeLegacyAuthLeftovers(left, failure) {
6637
+ const schema = LEGACY_RLS_SCHEMA;
6638
+ const objects = left.filter((o) => o.object !== null);
6639
+ if (objects.length === 0) return `The pre-1.0 \`${schema}\` schema is empty, but this connection could not drop it — most likely because it does not own the schema. Run \`DROP SCHEMA ${schema};\` as its owner.`;
6640
+ const why = failure ? `dropping Rebase's old helpers from it failed: ${failure}.` : "`DROP SCHEMA … RESTRICT` refuses a schema that is not empty, and Rebase drops only the helpers it can tell are its own.";
6641
+ const lines = objects.slice(0, MAX_LEFTOVERS_NAMED).map((o) => o.helper === "ours" ? ` • ${o.object} — Rebase's old helper, which could not be dropped` : o.helper === "lookalike" ? ` • ${o.object} — named and typed like Rebase's old helper, but no release wrote this body, so it was left alone; drop it by hand if nothing of yours calls it` : ` • ${o.object}`);
6642
+ if (objects.length > MAX_LEFTOVERS_NAMED) lines.push(` • …and ${objects.length - MAX_LEFTOVERS_NAMED} more`);
6643
+ return `The pre-1.0 \`${schema}\` schema is still here: ${why} Still in it:\n` + lines.join("\n") + `
6644
+ Anything of yours can stay where it is — Rebase no longer uses this schema — or move out with \`ALTER … SET SCHEMA\`. Once the schema is empty, \`DROP SCHEMA ${schema};\` removes it.`;
6645
+ }
6646
+ //#endregion
6647
+ //#region src/cli-flags.ts
6648
+ var import_arg = /* @__PURE__ */ __toESM(require_arg(), 1);
6649
+ /**
6650
+ * Flags that reach this process on a line it did not compose.
6651
+ *
6652
+ * `rebase db …` hands the driver the user's whole line: the CLI *reads*
6653
+ * `--database-url` and `--docker` to resolve the database and then relays them
6654
+ * verbatim, and `--debug` is what `bin/rebase.js` prints after every failure as
6655
+ * the thing to re-run with. Rejecting any of the three would make the driver
6656
+ * refuse commands the CLI documents.
6657
+ */
6658
+ var RELAYED_FLAGS = {
6659
+ "--database-url": String,
6660
+ "--docker": Boolean,
6661
+ "--debug": Boolean,
6662
+ "--help": Boolean,
6663
+ "-h": "--help"
6664
+ };
6665
+ /**
6666
+ * Parse a driver command's line — `["db", "<subcommand>", …]` — with the
6667
+ * relayed flags declared alongside the command's own.
6668
+ *
6669
+ * Every driver parser is permissive, and an undeclared flag in a permissive
6670
+ * parse is a positional. The positionals are what `db migrate` hands
6671
+ * `atlas migrate apply`, what `db generate` names its migration after and
6672
+ * which backup `db restore` restores, so a relayed flag left undeclared was
6673
+ * read as one of those: `rebase db migrate --docker` died on Atlas's
6674
+ * `unknown flag: --docker` after it had already written the auth schema.
6675
+ */
6676
+ function parseDriverLine(spec, args) {
6677
+ return (0, import_arg.default)({
6678
+ ...RELAYED_FLAGS,
6679
+ ...spec
6680
+ }, {
6681
+ argv: args.slice(2),
6682
+ permissive: true
6683
+ });
6684
+ }
6685
+ /** Keyed by `"<domain> <subcommand>"`, the way the user types it. */
6686
+ var DRIVER_FLAG_SPECS = {
6687
+ "db push": {
6688
+ "--collections": String,
6689
+ "-c": "--collections",
6690
+ "--dry-run": Boolean,
6691
+ "--allow-destructive": Boolean,
6692
+ "--yes": Boolean,
6693
+ "-y": "--yes"
6694
+ },
6695
+ "db generate": {
6696
+ "--collections": String,
6697
+ "-c": "--collections"
6698
+ },
6699
+ "db migrate": {
6700
+ "--baseline": String,
6701
+ "--refuse-destructive": Boolean
6702
+ },
6703
+ "schema generate": {
6704
+ "--collections": String,
6705
+ "-c": "--collections",
6706
+ "--output": String,
6707
+ "-o": "--output",
6708
+ "--out": "--output",
6709
+ "--watch": Boolean,
6710
+ "-w": "--watch"
6711
+ },
6712
+ "schema introspect": {
6713
+ "--output": String,
6714
+ "-o": "--output",
6715
+ "--out": "--output",
6716
+ "--collections": String,
6717
+ "-c": "--collections",
6718
+ "--force": Boolean,
6719
+ "-f": "--force",
6720
+ "--schema": String,
6721
+ "--data-inference": Boolean,
6722
+ "--no-data-inference": Boolean
6723
+ },
6724
+ "schema stale": {
6725
+ "--collections": String,
6726
+ "-c": "--collections",
6727
+ "--output": String,
6728
+ "-o": "--output",
6729
+ "--out": "--output",
6730
+ "--fix": Boolean
6731
+ }
6732
+ };
6733
+ /**
6734
+ * The `--collections` path the user typed, or `null` if they typed none.
6735
+ *
6736
+ * Read once, at the entry point, because every command that takes it re-enters
6737
+ * the generators with the same line and each of them resolved it again — which
6738
+ * is how "Collections path not found" came to be printed four times before the
6739
+ * real output, and how a path that does not exist got as far as *writing* an
6740
+ * empty schema.
6741
+ *
6742
+ * Permissive, and it has to be: {@link assertKnownFlags} has already judged the
6743
+ * line, and `db push` carries flags the `--collections` spec does not name.
6744
+ */
6745
+ function collectionsPathIn(args) {
6746
+ try {
6747
+ return (0, import_arg.default)({
6748
+ "--collections": String,
6749
+ "-c": "--collections"
6750
+ }, {
6751
+ argv: args.slice(2),
6752
+ permissive: true
6753
+ })["--collections"] ?? null;
6754
+ } catch {
6755
+ return null;
6756
+ }
6757
+ }
6758
+ /**
6759
+ * Reject a flag the named command does not take.
6760
+ *
6761
+ * `args` is the driver's whole line — `["db", "push", …]` — so the flags are
6762
+ * read from `args.slice(2)`, past the two command words.
6763
+ *
6764
+ * Throws rather than exiting: `runPluginCommand`'s caller already turns a
6765
+ * thrown error into one red line and exit 1, and a thrown error is what the
6766
+ * tests can see.
6767
+ */
6768
+ function assertKnownFlags(domain, subcommand, args) {
6769
+ if (!subcommand) return;
6770
+ const spec = DRIVER_FLAG_SPECS[`${domain} ${subcommand}`];
6771
+ if (!spec) return;
6772
+ const argv = resolveSwitchValues({
6773
+ ...RELAYED_FLAGS,
6774
+ ...spec
6775
+ }, args.slice(2), `${domain} ${subcommand}`, domain);
6776
+ try {
6777
+ (0, import_arg.default)({
6778
+ ...RELAYED_FLAGS,
6779
+ ...spec
6780
+ }, { argv });
6781
+ } catch (err) {
6782
+ const unknown = unknownFlagIn(err, args.slice(2));
6783
+ if (unknown === null) throw err;
6784
+ throw new Error(unknownOptionSentence(`${domain} ${subcommand}`, domain, unknown, spec));
6785
+ }
6786
+ }
6787
+ /**
6788
+ * The sentence an undeclared flag is refused with, in the words the CLI's own
6789
+ * parser uses: "`rebase db push` has no option --dry-rn — did you mean
6790
+ * --dry-run? Nothing was run."
6791
+ *
6792
+ * `token` is the whole argument `arg` refused, which is not always a flag: one
6793
+ * argument holding a flag and its value, quoted together
6794
+ * (`"--from postgres://u:pw@h/db"`), is refused whole. That one is named as
6795
+ * what it is, and what it quotes is password-masked; no other part of an
6796
+ * argument is quoted at all.
6797
+ *
6798
+ * @param command the command as typed: `"db push"`.
6799
+ * @param help the command whose `--help` the sentence names.
6800
+ * @param spec the command's own flags; the relayed ones are offered too.
6801
+ */
6802
+ function unknownOptionSentence(command, help, token, spec) {
6803
+ const full = {
6804
+ ...RELAYED_FLAGS,
6805
+ ...spec
6806
+ };
6807
+ const name = token.split(/[=\s]/)[0];
6808
+ const suggestion = closestWord(name, Object.keys(full).filter((flag) => flag.startsWith("--")));
6809
+ const tail = ` Nothing was run. See \`rebase ${help} --help\`.`;
6810
+ if (/\s/.test(token.trim())) {
6811
+ const flag = name in full ? name : suggestion;
6812
+ const how = flag ? ` Write them as two arguments, ${flag} <value>, or as ${flag}=<value>.` : " Write a flag and its value as two arguments.";
6813
+ return `\`rebase ${command}\` has no option "${redactConnectionPasswords(token)}": that is a flag and a value in one argument, quoted together.${how}${tail}`;
6814
+ }
6815
+ const own = Object.keys(spec).filter((flag) => flag.startsWith("--"));
6816
+ const takes = own.length > 0 ? `it takes ${own.join(", ")}` : "it takes no options of its own";
6817
+ return `\`rebase ${command}\` has no option ${name}` + (suggestion ? ` — did you mean ${suggestion}?` : ` — ${takes}.`) + tail;
6818
+ }
6819
+ /** What a flag resolves to, through its alias (`"-y": "--yes"`). */
6820
+ function handlerOf(spec, flag) {
6821
+ const declared = spec[flag];
6822
+ return typeof declared === "string" ? spec[declared] : declared;
6823
+ }
6824
+ /** A flag that takes no value: on when given, off when left out. */
6825
+ function isSwitch(handler) {
6826
+ return handler === Boolean || Array.isArray(handler) && handler[0] === Boolean;
6827
+ }
6828
+ /** The values a switch reads as on, and as off, after its `=`. */
6829
+ var SWITCH_ON = /^(?:true|1)$/i;
6830
+ var SWITCH_OFF = /^(?:false|0)$/i;
6831
+ /**
6832
+ * A line with every `--switch=value` resolved, before `arg` reads it: `=true`
6833
+ * or `=1` is the switch alone, `=false` or `=0` drops it, and any other value
6834
+ * is refused.
6835
+ *
6836
+ * `arg` drops the value of a flag that takes none and turns the flag on, so
6837
+ * `db push --dry-run=false` was a dry run and `--allow-destructive=false`
6838
+ * allowed the destructive statements it was typed to forbid. `=true` kept
6839
+ * working by the same accident, and scripts write it, so both readings a value
6840
+ * can have are honoured and nothing else is guessed at. Past `--` every word
6841
+ * is a positional, so nothing there is read.
6842
+ *
6843
+ * @param command the command as typed, for the message: `"db push"`.
6844
+ * @param help the command whose `--help` the message names.
6845
+ */
6846
+ function resolveSwitchValues(spec, argv, command, help = command) {
6847
+ const resolved = [];
6848
+ for (let index = 0; index < argv.length; index += 1) {
6849
+ const word = argv[index];
6850
+ if (word === "--") {
6851
+ resolved.push(...argv.slice(index));
6852
+ break;
6853
+ }
6854
+ const flag = word.startsWith("--") && word.includes("=") ? word.slice(0, word.indexOf("=")) : null;
6855
+ if (flag === null || !isSwitch(handlerOf(spec, flag))) {
6856
+ resolved.push(word);
6857
+ continue;
6858
+ }
6859
+ const value = word.slice(flag.length + 1);
6860
+ if (SWITCH_ON.test(value)) resolved.push(flag);
6861
+ else if (!SWITCH_OFF.test(value)) throw new Error(`\`rebase ${command}\` cannot read ${redactConnectionPasswords(word)}: ${flag} is a switch, and after its = it takes true or 1 to turn it on, false or 0 to turn it off. Nothing was run. See \`rebase ${help} --help\`.`);
6862
+ }
6863
+ return resolved;
6864
+ }
6865
+ /**
6866
+ * A driver command's line, `["<domain>", "<subcommand>", …]`, with its
6867
+ * switches' values resolved by {@link resolveSwitchValues} against the
6868
+ * command's spec: what `runPluginCommand` hands every parser after it, so a
6869
+ * permissive one never reads `--allow-destructive=false` as on. A command with
6870
+ * no spec here comes back as it was.
6871
+ */
6872
+ function resolveDriverSwitches(domain, subcommand, args) {
6873
+ const spec = subcommand ? DRIVER_FLAG_SPECS[`${domain} ${subcommand}`] : void 0;
6874
+ if (!spec) return [...args];
6875
+ return [...args.slice(0, 2), ...resolveSwitchValues({
6876
+ ...RELAYED_FLAGS,
6877
+ ...spec
6878
+ }, args.slice(2), `${domain} ${subcommand}`, domain)];
6879
+ }
6880
+ /**
6881
+ * The argument `arg` refused as undeclared, whole, or `null` for any other
6882
+ * failure.
6883
+ *
6884
+ * arg's wording is `unknown or unexpected option: --targt-db`, naming the
6885
+ * argument up to its first `=`. An argument with a space in it is named past
6886
+ * the space too, so the match runs to the end of the message; and the word
6887
+ * in `argv` it came from is returned, `=` and all, so a refusal that quotes it
6888
+ * quotes what was typed.
6889
+ */
6890
+ function unknownFlagIn(err, argv) {
6891
+ if (!(err instanceof Error) || !("code" in err) || err.code !== "ARG_UNKNOWN_OPTION") return null;
6892
+ const named = /option: ([\s\S]+)$/.exec(err.message)?.[1];
6893
+ if (named === void 0) return null;
6894
+ return argv.find((word) => word === named || word.startsWith(`${named}=`)) ?? named;
6895
+ }
6896
+ /**
6897
+ * The word in `known` that `typed` was probably meant to be: one edit away
6898
+ * (`--targt-db`), or two neighbouring letters swapped (`--clena`, `--froce`),
6899
+ * which `suggestNearMiss` counts as two edits and so never offers.
6900
+ */
6901
+ function closestWord(typed, known) {
6902
+ return suggestNearMiss(known, typed) ?? transposedFrom(known, typed);
6903
+ }
6904
+ function transposedFrom(known, typed) {
6905
+ return known.find((candidate) => {
6906
+ if (candidate.length !== typed.length || candidate === typed) return false;
6907
+ const differ = [...candidate].flatMap((letter, index) => letter === typed[index] ? [] : [index]);
6908
+ return differ.length === 2 && differ[1] === differ[0] + 1 && candidate[differ[0]] === typed[differ[1]] && candidate[differ[1]] === typed[differ[0]];
6909
+ });
6910
+ }
6911
+ /**
6912
+ * Parse a command that owns its argument handling, refusing what it does not
6913
+ * declare.
6914
+ *
6915
+ * `db backup`, `db restore` and `db backups list` parsed permissively, so an
6916
+ * undeclared flag became a positional and was ignored: `db restore <dump>
6917
+ * --targt-db x --create-db --yes` dropped the misspelled target and restored
6918
+ * into the live database. A typo is refused here before anything runs, naming
6919
+ * the declared flag it is one edit from, or every flag the command takes when
6920
+ * none is that close.
6921
+ *
6922
+ * @param argv the words after the command, e.g. `rawArgs.slice(2)`.
6923
+ * @param command the command as typed, for the message: `"db restore"`.
6924
+ */
6925
+ function parseCommandFlags(spec, argv, command) {
6926
+ const full = {
6927
+ ...RELAYED_FLAGS,
6928
+ ...spec
6929
+ };
6930
+ try {
6931
+ return (0, import_arg.default)(full, { argv: resolveSwitchValues(full, argv, command) });
6932
+ } catch (err) {
6933
+ const unknown = unknownFlagIn(err, argv);
6934
+ if (unknown === null) throw err;
6935
+ throw new Error(unknownOptionSentence(command, command, unknown, spec));
6936
+ }
6937
+ }
6938
+ //#endregion
6939
+ //#region src/branch-argv.ts
6940
+ /**
6941
+ * Argument shapes for `rebase db branch`, with no runtime dependencies.
6942
+ *
6943
+ * Its own module for the same reason `schema/atlas-argv.ts` is: `cli.ts` pulls
6944
+ * in chalk, execa and the driver runtime, so a unit test that imports it cannot
6945
+ * run. Argv parsing is the part most worth testing and the part least in need
6946
+ * of any of that.
6947
+ */
6948
+ /**
6949
+ * The flags each driver-side `db branch` action takes, besides the ones the CLI
6950
+ * relays on every line (`--database-url`, `--docker`, `--debug`).
6951
+ *
6952
+ * `switch` is absent: the CLI answers it before the driver is ever spawned.
6953
+ * An action not listed here is not checked, so the dispatch can say it does
6954
+ * not exist in its own words.
6955
+ */
6956
+ var BRANCH_ACTION_FLAGS = {
6957
+ create: {
6958
+ "--from": String,
6959
+ "--force": Boolean
6960
+ },
6961
+ list: {},
6962
+ info: {},
6963
+ diff: {
6964
+ "--json": Boolean,
6965
+ "--sample-limit": Number
6966
+ },
6967
+ delete: {
6968
+ "--force": Boolean,
6969
+ "--yes": Boolean,
6970
+ "-y": "--yes"
6971
+ },
6972
+ prune: {
6973
+ "--older-than": String,
6974
+ "--include-dev-diff": Boolean,
6975
+ "--yes": Boolean,
6976
+ "-y": "--yes"
6977
+ }
6978
+ };
6979
+ /**
6980
+ * Refuse a flag `action` does not take, naming the one it was probably meant
6981
+ * to be — the same sentence `db backup` and `db restore` say.
6982
+ *
6983
+ * Throws: the driver's entry turns a thrown error into one red line and exit 1,
6984
+ * and this runs before anything connects.
6985
+ */
6986
+ function assertKnownBranchFlags(action, rest) {
6987
+ const spec = BRANCH_ACTION_FLAGS[action];
6988
+ if (!spec) return;
6989
+ const full = {
6990
+ ...RELAYED_FLAGS,
6991
+ ...spec
6992
+ };
6993
+ const argv = resolveSwitchValues(full, rest, `db branch ${action}`, "db branch");
6994
+ try {
6995
+ (0, import_arg.default)(full, { argv });
6996
+ } catch (error) {
6997
+ const unknown = unknownFlagIn(error, rest);
6998
+ if (unknown === null) throw error;
6999
+ throw new Error(unknownOptionSentence(`db branch ${action}`, "db branch", unknown, spec));
6518
7000
  }
6519
7001
  }
7002
+ /**
7003
+ * A `db branch` line, `["db", "branch", <action>, …]`, with its switches'
7004
+ * values resolved against the action's flags (see `resolveSwitchValues`):
7005
+ * what the branch dispatch reads, so `prune --yes=false` is not a yes. An
7006
+ * action the table does not list comes back as it was.
7007
+ */
7008
+ function resolveBranchSwitches(rawArgs) {
7009
+ const action = rawArgs[2];
7010
+ const spec = action === void 0 ? void 0 : BRANCH_ACTION_FLAGS[action];
7011
+ if (!spec) return [...rawArgs];
7012
+ return [...rawArgs.slice(0, 3), ...resolveSwitchValues({
7013
+ ...RELAYED_FLAGS,
7014
+ ...spec
7015
+ }, rawArgs.slice(3), `db branch ${action}`, "db branch")];
7016
+ }
7017
+ /**
7018
+ * Words on a `db branch` command line that no argument accounts for.
7019
+ *
7020
+ * `rebase db branch create alpha beta` created a branch called `alpha` and
7021
+ * threw `beta` away without a word. The shapes that produces are all quiet and
7022
+ * all wrong: an unquoted name (`create my feature` → `my`), a flag written
7023
+ * without its dashes (`create feat from main` → `feat`), a shell that split
7024
+ * something you thought was one token. In each case the command succeeds and
7025
+ * the branch is not the one asked for — and branch names are the thing you
7026
+ * later type to switch, delete, or point a deploy at.
7027
+ *
7028
+ * A flag the action does not take is refused outright, by throwing — see
7029
+ * {@link BRANCH_ACTION_FLAGS}. It was let through on the reasoning that a
7030
+ * subcommand added later might take flags this file had never heard of; what
7031
+ * that bought was every typo running without the flag it meant:
7032
+ * `prune --older-then 1d --yes` pruned by no age, `create x --froce` ran
7033
+ * without --force. A new action gets its line in the table. The value after a
7034
+ * flag that takes one is skipped: it is that flag's argument, not a stray word.
7035
+ *
7036
+ * @param words The command's own arguments: action, name, then the rest.
7037
+ */
7038
+ function unexpectedBranchArgs(words) {
7039
+ if (words[0]) assertKnownBranchFlags(words[0], words.slice(1));
7040
+ const extras = [];
7041
+ let seenName = false;
7042
+ for (let index = 1; index < words.length; index += 1) {
7043
+ const word = words[index];
7044
+ if (word.startsWith("-")) {
7045
+ if (TAKES_A_VALUE.has(word.split("=")[0])) index += 1;
7046
+ continue;
7047
+ }
7048
+ if (!seenName) {
7049
+ seenName = true;
7050
+ continue;
7051
+ }
7052
+ extras.push(word);
7053
+ }
7054
+ return extras;
7055
+ }
7056
+ /**
7057
+ * Branch-line flags whose next word belongs to them.
7058
+ *
7059
+ * `--from` was the only one listed, and `--database-url` is the one that bit:
7060
+ * `rebase db branch list --database-url postgres://…` — which the CLI itself
7061
+ * appends when the checkout is on a branch, so `rebase.branches` is read in the
7062
+ * parent where it lives — answered `✗ Unexpected argument: postgres://…`. The
7063
+ * `=` form carries its value in the same word and needs no skip.
7064
+ */
7065
+ var TAKES_A_VALUE = /* @__PURE__ */ new Set([
7066
+ "--from",
7067
+ "--older-than",
7068
+ "--sample-limit",
7069
+ "--database-url"
7070
+ ]);
7071
+ //#endregion
7072
+ //#region src/backup-argv.ts
7073
+ /**
7074
+ * @param rawArgs the driver's whole line, `["db", "backup", …]`.
7075
+ */
7076
+ function backupActionOf(rawArgs) {
7077
+ return rawArgs[2] === "list" ? "list" : "create";
7078
+ }
7079
+ //#endregion
7080
+ //#region src/db-subcommands.ts
7081
+ /**
7082
+ * The `db` subcommands this driver dispatches, with no runtime dependencies.
7083
+ *
7084
+ * Its own module so the dispatch in `cli.ts` can name them without loading
7085
+ * what judges their lines (`db-command-line.ts` reaches the backup module,
7086
+ * which `cli.ts` imports only when a backup command runs).
7087
+ */
7088
+ var DRIVER_DB_SUBCOMMANDS = [
7089
+ "push",
7090
+ "generate",
7091
+ "migrate",
7092
+ "branch",
7093
+ "backup",
7094
+ "restore",
7095
+ "backups"
7096
+ ];
7097
+ /** Is `word` a `db` subcommand this driver answers? */
7098
+ function isDriverDbSubcommand(word) {
7099
+ return DRIVER_DB_SUBCOMMANDS.some((subcommand) => subcommand === word);
7100
+ }
7101
+ //#endregion
7102
+ //#region src/schema/migrate-baseline.ts
7103
+ /**
7104
+ * What `rebase db migrate --baseline <version>` does, given the history the
7105
+ * database already has.
7106
+ *
7107
+ * The flag promises to record `<version>` as applied and migrate from the next
7108
+ * one. Atlas's own `migrate apply --baseline` keeps that promise only on a
7109
+ * database with no revision history: once one row exists it ignores the flag
7110
+ * without a word, applies the migration anyway, and the CLI had already printed
7111
+ * "Recording … as already applied". That is the normal second time: a project
7112
+ * baselines once, then `rebase dev` adds a column at boot, `db generate` writes
7113
+ * the migration for it, and `db migrate` fails on `column … already exists
7114
+ * (42701)` however the baseline is spelled.
7115
+ *
7116
+ * So the history is read first. With none, Atlas's baseline does the work.
7117
+ * Behind `<version>`, `atlas migrate set` marks every migration up to it as
7118
+ * applied, which is what a baseline means. At or past it, there is nothing to
7119
+ * record, and the command says so instead of claiming it recorded something.
7120
+ */
7121
+ /**
7122
+ * Atlas's version order for the versions Rebase writes: numeric timestamps,
7123
+ * compared as numbers so a shorter one is not read as later; anything else as
7124
+ * text.
7125
+ */
7126
+ function compareMigrationVersions(a, b) {
7127
+ if (/^\d+$/.test(a) && /^\d+$/.test(b)) {
7128
+ const x = BigInt(a);
7129
+ const y = BigInt(b);
7130
+ return x === y ? 0 : x < y ? -1 : 1;
7131
+ }
7132
+ return a.localeCompare(b);
7133
+ }
7134
+ /**
7135
+ * The version Atlas records for a migration file: its numeric prefix
7136
+ * (`20260906101530_init.sql` → `20260906101530`), or the name without `.sql`.
7137
+ */
7138
+ function migrationVersionOf(file) {
7139
+ return /^(\d+)/.exec(file)?.[1] ?? file.replace(/\.sql$/, "");
7140
+ }
7141
+ /** How many versions a refused baseline names, newest last. */
7142
+ var VERSIONS_NAMED = 5;
7143
+ /**
7144
+ * Why `--baseline <version>` cannot run, or `null` when it names a migration
7145
+ * file in `drizzle/migrations`.
7146
+ *
7147
+ * Asked before anything else, the database included. A version no file has
7148
+ * was refused only by Atlas, after the command had said "Marking every
7149
+ * migration up to abc as applied…"; and one older than every file read
7150
+ * "Nothing to record" and migrated on, though it named nothing to baseline.
7151
+ *
7152
+ * @param files the migration files, sorted oldest first.
7153
+ * @returns the refusal, its first line the error and the rest the detail.
7154
+ */
7155
+ function baselineVersionProblem(version, files) {
7156
+ const versions = files.map(migrationVersionOf);
7157
+ 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.`;
7158
+ if (versions.includes(version)) return null;
7159
+ const named = versions.slice(-5).join(", ");
7160
+ 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.`;
7161
+ }
7162
+ /** How to keep `--baseline <version>`'s promise, given the newest recorded version. */
7163
+ function planBaseline(version, latestRecorded) {
7164
+ if (latestRecorded === null) return { kind: "atlas-baseline" };
7165
+ if (compareMigrationVersions(version, latestRecorded) > 0) return {
7166
+ kind: "set",
7167
+ latest: latestRecorded
7168
+ };
7169
+ return {
7170
+ kind: "already-recorded",
7171
+ latest: latestRecorded
7172
+ };
7173
+ }
7174
+ /** What the command says before it migrates, for each plan. */
7175
+ function describeBaselinePlan(version, plan) {
7176
+ switch (plan.kind) {
7177
+ case "atlas-baseline": return `Recording ${version} as already applied, then migrating from the next one.`;
7178
+ 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.`;
7179
+ 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.`;
7180
+ }
7181
+ }
7182
+ /**
7183
+ * The newest version Atlas has recorded on this database, or `null` when it
7184
+ * has recorded none (no revisions table yet included).
7185
+ *
7186
+ * The table is `rebase.atlas_schema_revisions`: the CLI passes
7187
+ * `--revisions-schema rebase` to every `migrate apply`.
7188
+ */
7189
+ async function latestRecordedMigration(databaseUrl) {
7190
+ return withAppClient(databaseUrl, async (client) => {
7191
+ if (!(await client.query("SELECT to_regclass('rebase.atlas_schema_revisions') IS NOT NULL AS present")).rows[0]?.present) return null;
7192
+ const versions = (await client.query("SELECT version FROM rebase.atlas_schema_revisions WHERE version NOT LIKE '.%'")).rows.map((row) => row.version);
7193
+ if (versions.length === 0) return null;
7194
+ return versions.reduce((latest, version) => compareMigrationVersions(version, latest) > 0 ? version : latest);
7195
+ });
7196
+ }
6520
7197
  //#endregion
6521
- export { rls_bootstrap_sql_exports as a, rlsRuntimeIsCurrent as i, RLS_BOOTSTRAP_STATEMENTS as n, execa as o, dropLegacyAuthSchema as r, RLS_BOOTSTRAP_SQL as t };
7198
+ export { rlsRuntimeIsCurrent as C, dropLegacyAuthSchema as S, execa as T, parseCommandFlags as _, migrationVersionOf as a, RLS_BOOTSTRAP_SQL as b, isDriverDbSubcommand as c, resolveBranchSwitches as d, unexpectedBranchArgs as f, collectionsPathIn as g, closestWord as h, latestRecordedMigration as i, backupActionOf as l, assertKnownFlags as m, compareMigrationVersions as n, planBaseline as o, RELAYED_FLAGS as p, describeBaselinePlan as r, DRIVER_DB_SUBCOMMANDS as s, baselineVersionProblem as t, assertKnownBranchFlags as u, parseDriverLine as v, rls_bootstrap_sql_exports as w, RLS_BOOTSTRAP_STATEMENTS as x, resolveDriverSwitches as y };
6522
7199
 
6523
- //# sourceMappingURL=rls-bootstrap-sql-Bd-hJk08.js.map
7200
+ //# sourceMappingURL=migrate-baseline-B-2Ef4Mv.js.map