@rebasepro/server-postgres 0.23.0 → 0.24.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 (108) hide show
  1. package/dist/{BranchService-DGPL6G_C.js → BranchService-BRt78gfa.js} +35 -18
  2. package/dist/BranchService-BRt78gfa.js.map +1 -0
  3. package/dist/PostgresBackendDriver.d.ts +84 -12
  4. package/dist/auth/services.d.ts +72 -14
  5. package/dist/{auth-users-columns-D2LBFrMH.js → auth-users-columns-C72EMoDJ.js} +17 -1
  6. package/dist/{auth-users-columns-D2LBFrMH.js.map → auth-users-columns-C72EMoDJ.js.map} +1 -1
  7. package/dist/{backup-cli-24v4OlSp.js → backup-cli-DW5p9_zv.js} +2 -2
  8. package/dist/{backup-cli-24v4OlSp.js.map → backup-cli-DW5p9_zv.js.map} +1 -1
  9. package/dist/{backup-service-HQ9GC4tN.js → backup-service-EwcDVG-8.js} +7 -9
  10. package/dist/{backup-service-HQ9GC4tN.js.map → backup-service-EwcDVG-8.js.map} +1 -1
  11. package/dist/{cli-errors-B8qHg02P.js → cli-errors-DsA-K9uP.js} +96 -1
  12. package/dist/{cli-errors-B8qHg02P.js.map → cli-errors-DsA-K9uP.js.map} +1 -1
  13. package/dist/cli-errors.d.ts +42 -0
  14. package/dist/cli-helpers.d.ts +12 -12
  15. package/dist/cli.js +91 -37
  16. package/dist/cli.js.map +1 -1
  17. package/dist/{column-plan-helpers-DF-8dTVa.js → column-plan-helpers-1-LQD0yI.js} +34 -24
  18. package/dist/column-plan-helpers-1-LQD0yI.js.map +1 -0
  19. package/dist/data-transformer.d.ts +0 -8
  20. package/dist/{doctor-Cb2thZ8s.js → doctor-C-sYWbmt.js} +224 -43
  21. package/dist/doctor-C-sYWbmt.js.map +1 -0
  22. package/dist/{ensure-collection-policies-RHUcEp4v.js → ensure-collection-policies-B1ureSIV.js} +73 -18
  23. package/dist/ensure-collection-policies-B1ureSIV.js.map +1 -0
  24. package/dist/{ensure-collection-tables-BEEjn5cn.js → ensure-collection-tables-kkkHk8oo.js} +76 -20
  25. package/dist/ensure-collection-tables-kkkHk8oo.js.map +1 -0
  26. package/dist/{ensure-tables-Dhn9KM3B.js → ensure-tables-BmI_tRxc.js} +10 -3
  27. package/dist/ensure-tables-BmI_tRxc.js.map +1 -0
  28. package/dist/{generate-drizzle-schema-yzY_BLhr.js → generate-drizzle-schema-93M0lUxK.js} +2 -2
  29. package/dist/{generate-drizzle-schema-yzY_BLhr.js.map → generate-drizzle-schema-93M0lUxK.js.map} +1 -1
  30. package/dist/{generate-drizzle-schema-logic-BfzK7UQd.js → generate-drizzle-schema-logic-sSFDp6LR.js} +26 -8
  31. package/dist/generate-drizzle-schema-logic-sSFDp6LR.js.map +1 -0
  32. package/dist/generate-postgres-ddl-logic-BJsLaVNX.js +152 -0
  33. package/dist/generate-postgres-ddl-logic-BJsLaVNX.js.map +1 -0
  34. package/dist/generated-sql.d.ts +28 -0
  35. package/dist/index.es.js +3415 -917
  36. package/dist/index.es.js.map +1 -1
  37. package/dist/{introspect-db-logic-WMuAfxvw.js → introspect-db-logic-kCETE8TY.js} +523 -349
  38. package/dist/introspect-db-logic-kCETE8TY.js.map +1 -0
  39. package/dist/introspect-db-queries-C_Q5VgQw.js +317 -0
  40. package/dist/introspect-db-queries-C_Q5VgQw.js.map +1 -0
  41. package/dist/{plan-schema-C0fxM8dY.js → plan-schema-DU9exq6C.js} +139 -504
  42. package/dist/plan-schema-DU9exq6C.js.map +1 -0
  43. package/dist/{policy-drift-DljYdrpW.js → policy-drift-B-J2hhm0.js} +3 -3
  44. package/dist/policy-drift-B-J2hhm0.js.map +1 -0
  45. package/dist/{generate-postgres-ddl-logic-Bt2d2mRH.js → render-ddl-Ds2t_d9V.js} +11 -149
  46. package/dist/render-ddl-Ds2t_d9V.js.map +1 -0
  47. package/dist/{rls-bootstrap-sql-B8EclDyM.js → rls-bootstrap-sql-_KNnjanK.js} +263 -39
  48. package/dist/rls-bootstrap-sql-_KNnjanK.js.map +1 -0
  49. package/dist/{rls-enforcement-C6Xk0lA6.js → rls-enforcement-CfXOJJaW.js} +10 -2
  50. package/dist/rls-enforcement-CfXOJJaW.js.map +1 -0
  51. package/dist/schema/auth-schema.d.ts +170 -0
  52. package/dist/schema/classify-change.d.ts +29 -1
  53. package/dist/schema/column-plan-helpers.d.ts +33 -21
  54. package/dist/schema/destructive-sql.d.ts +20 -1
  55. package/dist/schema/doctor-cli.js +4 -4
  56. package/dist/schema/doctor.d.ts +34 -1
  57. package/dist/schema/ensure-collection-policies.d.ts +22 -0
  58. package/dist/schema/generate-drizzle-schema.js +1 -1
  59. package/dist/schema/generate-postgres-ddl-logic.d.ts +5 -5
  60. package/dist/schema/generate-postgres-ddl.js +1 -1
  61. package/dist/schema/generate-schema-commit.d.ts +12 -0
  62. package/dist/schema/introspect-db-logic.d.ts +31 -0
  63. package/dist/schema/introspect-db-queries.d.ts +1 -1
  64. package/dist/schema/introspect-db-search.d.ts +22 -0
  65. package/dist/schema/introspect-db-storage.d.ts +83 -0
  66. package/dist/schema/introspect-db.js +15 -3
  67. package/dist/schema/introspect-db.js.map +1 -1
  68. package/dist/schema/plan/diff-plan.d.ts +1 -1
  69. package/dist/schema/plan/plan-schema.d.ts +16 -8
  70. package/dist/schema/plan/render-ddl.d.ts +6 -0
  71. package/dist/schema/plan/types.d.ts +37 -11
  72. package/dist/search-column-BM-GV6vH.js +442 -0
  73. package/dist/search-column-BM-GV6vH.js.map +1 -0
  74. package/dist/security/policy-drift.d.ts +1 -1
  75. package/dist/security/rls-enforcement.d.ts +7 -0
  76. package/dist/services/BranchService.d.ts +22 -1
  77. package/dist/services/FetchService.d.ts +5 -4
  78. package/dist/services/RelationService.d.ts +18 -0
  79. package/dist/services/cdc/CdcListener.d.ts +9 -4
  80. package/dist/services/cdc/identity-columns.d.ts +19 -0
  81. package/dist/services/cdc/trigger-cdc.d.ts +45 -7
  82. package/dist/services/channel-bus/PostgresChannelBus.d.ts +12 -7
  83. package/dist/services/channel-history.d.ts +17 -1
  84. package/dist/services/collection-helpers.d.ts +11 -1
  85. package/dist/services/pg-notify-listener.d.ts +88 -6
  86. package/dist/services/realtimeService.d.ts +159 -45
  87. package/dist/services/socket-liveness.d.ts +45 -0
  88. package/dist/services/soft-delete.d.ts +12 -0
  89. package/dist/services/sql-script.d.ts +71 -0
  90. package/dist/services/write-depth.d.ts +16 -0
  91. package/dist/services/write-transaction-scope.d.ts +6 -3
  92. package/dist/utils/drizzle-conditions.d.ts +42 -2
  93. package/dist/utils/sql-redaction.d.ts +21 -0
  94. package/dist/websocket.d.ts +50 -18
  95. package/package.json +7 -7
  96. package/dist/BranchService-DGPL6G_C.js.map +0 -1
  97. package/dist/column-plan-helpers-DF-8dTVa.js.map +0 -1
  98. package/dist/doctor-Cb2thZ8s.js.map +0 -1
  99. package/dist/ensure-collection-policies-RHUcEp4v.js.map +0 -1
  100. package/dist/ensure-collection-tables-BEEjn5cn.js.map +0 -1
  101. package/dist/ensure-tables-Dhn9KM3B.js.map +0 -1
  102. package/dist/generate-drizzle-schema-logic-BfzK7UQd.js.map +0 -1
  103. package/dist/generate-postgres-ddl-logic-Bt2d2mRH.js.map +0 -1
  104. package/dist/introspect-db-logic-WMuAfxvw.js.map +0 -1
  105. package/dist/plan-schema-C0fxM8dY.js.map +0 -1
  106. package/dist/policy-drift-DljYdrpW.js.map +0 -1
  107. package/dist/rls-bootstrap-sql-B8EclDyM.js.map +0 -1
  108. package/dist/rls-enforcement-C6Xk0lA6.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-ddl-Ds2t_d9V.js","names":[],"sources":["../src/schema/plan/render-ddl.ts"],"sourcesContent":["/**\n * The SQL files, rendered from a {@link SchemaPlan}.\n *\n * Four files, because Atlas owns exactly one of them:\n *\n * - `schema.sql` — the desired state Atlas diffs the database against.\n * - `policies.sql` — the RLS policies. Atlas has no model for a policy.\n * - `search.sql` — the generated `tsvector` column, its GIN index and the\n * IMMUTABLE helpers its expression calls. Atlas's free tier refuses to\n * *parse* a file containing a function, and it wipes the dev database it\n * diffs against, so a helper seeded there is gone by the time the plan runs.\n * - `vector.sql` — the `vector` columns and their ANN indexes. Atlas cannot\n * *execute* these: it materialises `schema.sql` in a dev database that is\n * created empty and emptied again on every run, so `VECTOR(384)` resolves\n * against a database that structurally cannot have pgvector.\n * - `triggers.sql` — `autoValue: \"on_update\"`, which is a function plus a\n * binding, so it is out for the same reason search is.\n *\n * Everything not in `schema.sql` is excluded from Atlas's view by pattern\n * (`…ExcludePatterns` below) and applied by Rebase — at push time by the CLI,\n * at boot by the schema ensure. Without the excludes a desired state that omits\n * an object reads to Atlas as an instruction to drop it.\n */\nimport {\n fuzzyColumnDefinition,\n searchColumnDefinition,\n searchColumnStamps,\n searchExtensionStatements,\n searchHelperFunctions,\n searchIndexNames,\n searchIndexStatements,\n searchStampGuards\n} from \"../search-column\";\nimport {\n VECTOR_EXTENSION_OPT_IN,\n vectorColumnDefinition,\n vectorDimensionGuard,\n vectorExtensionDeclared,\n vectorExtensionStatement,\n vectorIndexNames,\n vectorIndexStatements\n} from \"../vector-index\";\nimport { collectionIndexStatements } from \"../collection-index\";\nimport { quoteSqlLiteral } from \"./plan-schema\";\nimport { triggerStatements } from \"./updated-at-trigger\";\nimport type { ColumnPlan, PgType, PolicyPlan, SchemaPlan, TablePlan } from \"./types\";\n\n/** What `schema.sql` may leave out, because another file owns it. */\nexport interface DdlRenderOptions {\n /** RLS policies. `false` puts them in `policies.sql` alone. */\n includePolicies?: boolean;\n /** The search column, its helpers and its index. See `search.sql`. */\n includeSearch?: boolean;\n /** The vector columns and their ANN indexes. See `vector.sql`. */\n includeVector?: boolean;\n}\n\n/**\n * A {@link PgType} as Postgres spells it.\n *\n * The one place. `getSqlColumnType` and `getDrizzleColumn` each carried their\n * own ladder and disagreed about `VARCHAR` widths, about `geopoint`, and about\n * whether an `increment` id honours `columnType`.\n */\nexport const renderPgType = (type: PgType): string => {\n switch (type.kind) {\n case \"text\": return \"TEXT\";\n case \"varchar\": return `VARCHAR(${type.length})`;\n case \"char\": return `CHAR(${type.length})`;\n case \"uuid\": return \"UUID\";\n case \"enum\": return `\"${type.schema}\".\"${type.name}\"`;\n case \"smallint\": return \"SMALLINT\";\n case \"integer\": return \"INTEGER\";\n case \"bigint\": return \"BIGINT\";\n case \"smallserial\": return \"SMALLSERIAL\";\n case \"serial\": return \"SERIAL\";\n case \"bigserial\": return \"BIGSERIAL\";\n case \"real\": return \"REAL\";\n case \"doublePrecision\": return \"DOUBLE PRECISION\";\n case \"numeric\":\n if (type.precision === undefined) return \"NUMERIC\";\n return type.scale === undefined\n ? `NUMERIC(${type.precision})`\n : `NUMERIC(${type.precision}, ${type.scale})`;\n case \"boolean\": return \"BOOLEAN\";\n case \"timestamptz\": return \"TIMESTAMP WITH TIME ZONE\";\n case \"date\": return \"DATE\";\n case \"time\": return \"TIME\";\n case \"json\": return \"JSON\";\n case \"jsonb\": return \"JSONB\";\n case \"vector\": return `VECTOR(${type.dimensions})`;\n case \"bytea\": return \"BYTEA\";\n case \"tsvector\": return \"tsvector\";\n case \"array\": return `${renderPgType(type.of)}[]`;\n }\n};\n\n/**\n * Everything after the column name, in one order for every SQL emitter.\n *\n * `CREATE TABLE` and `ADD COLUMN` used to assemble this separately, which is\n * how a required relation column came out `NOT NULL` after `db push` and\n * nullable after a boot-ensure. `identity` is part of the type rather than a\n * DEFAULT, which is why it is rendered here and not in the DEFAULT slot.\n */\nexport const renderColumnDefinition = (column: ColumnPlan): string => {\n // `auth-users-columns` is the single description of what an auth user\n // table's columns must be, and it hands over a rendered definition.\n if (column.sqlDefinition) return column.sqlDefinition;\n\n if (column.generated) {\n return `${renderPgType(column.type)} GENERATED ALWAYS AS (${column.generated.expression}) STORED`;\n }\n\n let definition = renderPgType(column.type);\n if (column.default?.kind === \"identity\") definition += \" GENERATED BY DEFAULT AS IDENTITY\";\n if (column.primaryKey) definition += \" PRIMARY KEY\";\n if (column.unique) definition += \" UNIQUE\";\n if (column.default && column.default.kind !== \"identity\") {\n definition += ` DEFAULT ${column.default.kind === \"sql\" ? column.default.expression : column.default.sql}`;\n }\n if (!column.nullable && !column.primaryKey) definition += \" NOT NULL\";\n return definition;\n};\n\n/**\n * `PRIMARY KEY (\"a\", \"b\")`, the clause a composite key takes inside a\n * `CREATE TABLE`. Shared by `schema.sql` and boot-ensure's `CREATE TABLE`, so\n * the two cannot list the columns differently.\n */\nexport const renderPrimaryKeyConstraint = (table: Pick<TablePlan, \"primaryKey\">): string =>\n `PRIMARY KEY (${table.primaryKey.map(c => `\"${c}\"`).join(\", \")})`;\n\n/** `\"col\" TYPE …`, as it appears inside a `CREATE TABLE`. */\nconst renderColumn = (column: ColumnPlan): string =>\n `\"${column.column}\" ${renderColumnDefinition(column)}`;\n\n// ── Policies ─────────────────────────────────────────────────────────────────\n\n/**\n * One policy as its `DROP` / `CREATE` pair — each a complete statement.\n *\n * This is the primitive the boot-time RLS applier runs one statement at a time\n * (the runtime's DB handle speaks the extended query protocol, which forbids\n * multiple commands in one execute), while `db push` writes the joined string.\n */\nexport const renderPolicyStatements = (\n table: Pick<TablePlan, \"schema\" | \"table\">,\n policy: PolicyPlan\n): string[] => {\n const target = `\"${table.schema}\".\"${table.table}\"`;\n let create = `CREATE POLICY \"${policy.name}\" ON ${target} AS ${policy.mode.toUpperCase()} ` +\n `FOR ${policy.operation.toUpperCase()} TO ${policy.roles.map(r => `\"${r}\"`).join(\", \")}`;\n if (policy.using) create += ` USING (${policy.using})`;\n if (policy.withCheck) create += ` WITH CHECK (${policy.withCheck})`;\n return [`DROP POLICY IF EXISTS \"${policy.name}\" ON ${target};`, `${create};`];\n};\n\n/** `ALTER TABLE … ENABLE ROW LEVEL SECURITY;` — every generated table is locked. */\nexport const renderEnableRls = (table: Pick<TablePlan, \"schema\" | \"table\">): string =>\n `ALTER TABLE \"${table.schema}\".\"${table.table}\" ENABLE ROW LEVEL SECURITY;`;\n\nconst statementsToDdl = (statements: string[]): string => statements.map(s => `${s}\\n`).join(\"\");\n\n// ── schema.sql ───────────────────────────────────────────────────────────────\n\nexport function renderPostgresDdl(plan: SchemaPlan, options: DdlRenderOptions = {}): string {\n const includePolicies = options.includePolicies !== false;\n const includeSearch = options.includeSearch !== false;\n const includeVector = options.includeVector !== false;\n\n let ddl = \"-- This file is auto-generated by the Rebase DDL generator. Do not edit manually.\\n\\n\";\n\n // 1. Custom schemas.\n //\n // `rebase` is unconditional and load-bearing. The RLS helper functions live\n // in it, so the migration preamble creates it — which puts it in Atlas's\n // replayed state, and anything in that state but absent from this desired\n // schema gets a `DROP SCHEMA … CASCADE` planned against it. That would take\n // the auth tables.\n plan.schemas.forEach(schema => { ddl += `CREATE SCHEMA IF NOT EXISTS \"${schema}\";\\n`; });\n if (plan.schemas.length > 0) ddl += \"\\n\";\n\n // 1b. Search support, for collections that opted in.\n //\n // Extensions and helper functions come before every CREATE TABLE because a\n // generated column's expression is resolved at creation time: a table whose\n // search column calls `rebase_search_text` cannot be created before that\n // function exists. Both are `IF NOT EXISTS` / `OR REPLACE`, so a file\n // replayed against a live database is a no-op here.\n const searchTables = plan.tables.filter(t => t.search);\n if (searchTables.length > 0) {\n if (!includeSearch) {\n ddl += \"-- Full-text search support lives in `search.sql`, applied separately.\\n\\n\";\n } else {\n const extensions = Array.from(new Set(searchTables.flatMap(t => searchExtensionStatements(t.search!))));\n const helpers = Array.from(new Set(searchTables.flatMap(t => searchHelperFunctions(t.search!))));\n ddl += \"-- Full-text search support (collections declaring a `search` block)\\n\";\n extensions.forEach(s => { ddl += `${s}\\n`; });\n if (extensions.length > 0) ddl += \"\\n\";\n helpers.forEach(s => { ddl += `${s}\\n\\n`; });\n }\n }\n\n // 1c. Vector support. Only ever a note, never a statement — unlike 1b.\n // `CREATE EXTENSION vector` is the one thing this file must never carry\n // whichever way the flag goes: Atlas rejects an extension in a desired\n // state as a paid feature.\n if (!includeVector && plan.tables.some(t => t.vectorColumns.length > 0)) {\n ddl += \"-- Vector columns and their ANN indexes live in `vector.sql`, applied separately.\\n\\n\";\n }\n\n // 1d. `autoValue: \"on_update\"`. A trigger is a function plus a binding, so\n // it is out of Atlas's sight for the reason search is — and out\n // unconditionally, because `schema.sql` has no flag that would make Atlas\n // able to read one.\n if (plan.tables.some(t => t.triggers.length > 0)) {\n ddl += \"-- `autoValue: \\\"on_update\\\"` triggers live in `triggers.sql`, applied separately.\\n\\n\";\n }\n\n // 2. Enum types.\n plan.enums.forEach(enumPlan => {\n ddl += `CREATE TYPE \"${enumPlan.schema}\".\"${enumPlan.name}\" AS ENUM (${enumPlan.labels.map(quoteSqlLiteral).join(\", \")});\\n`;\n });\n if (ddl.endsWith(\";\\n\")) ddl += \"\\n\";\n\n // 3. Tables, in the plan's order — each collection, then any junction its\n // relations imply.\n const fkStatements: string[] = [];\n // Indexes follow the tables for the same reason the FK constraints do: the\n // table has to exist first.\n const indexStatements: string[] = [];\n // Policies are emitted after every CREATE TABLE, like the FK constraints: a\n // policy may reference other tables (a junction's derived policies always\n // reference both endpoints; `policy.existsIn` references a join table), and\n // CREATE POLICY validates those relations at creation time.\n const policyStatements: string[] = [];\n\n for (const table of plan.tables) {\n const emitted = table.columns.filter(column => {\n // A vector column is not Atlas's — `vector.sql` adds it. Filtered\n // here rather than upstream so the column keeps its place in the\n // generator's order when it *is* included, which is what the\n // derived-names contract renders.\n if (!includeVector && column.type.kind === \"vector\") return false;\n if (!includeSearch && column.source.kind === \"search\") return false;\n // A declared property already emits this column (an explicit\n // `postId` beside the `belongsTo` that uses it); the relation\n // contributes only the constraint.\n if (column.columnOwnedByProperty) return false;\n return true;\n });\n\n ddl += `CREATE TABLE \"${table.schema}\".\"${table.table}\" (\\n`;\n const lines = emitted.map(column => ` ${renderColumn(column)}`);\n // A key over several columns — a junction's two endpoints, or a\n // collection with several `isId` properties — is one table constraint;\n // a single-column key is already inline on its column.\n if (table.primaryKey.length > 1) {\n lines.push(` ${renderPrimaryKeyConstraint(table)}`);\n }\n ddl += lines.join(\",\\n\");\n ddl += \"\\n);\\n\\n\";\n\n for (const column of table.columns) {\n if (column.foreignKey) fkStatements.push(`${column.foreignKey.sql};`);\n }\n\n if (includeSearch && table.search) {\n indexStatements.push(...searchIndexStatements(table.search));\n }\n // ANN indexes are emitted with the other indexes rather than inline,\n // because `CREATE INDEX` is a statement and a column definition is not\n // — and because a column too wide for pgvector to index still needs its\n // column. Absent alongside their column when vector is carved out.\n if (includeVector && table.vector) {\n indexStatements.push(...vectorIndexStatements(table.vector));\n for (const skip of table.vector.skipped) {\n indexStatements.push(`-- No ANN index on \"${skip.schema}\".\"${skip.table}\".\"${skip.column}\": ${skip.reason}`);\n }\n }\n // The collection's own `indexes:` block. Last of the three index\n // producers, and the only one the developer wrote deliberately rather\n // than getting as a side effect of another feature.\n indexStatements.push(...collectionIndexStatements(table.indexes));\n\n if (includePolicies) {\n // No FORCE: authenticated requests run as the non-owner\n // `rebase_user` role, which plain ENABLE already binds. The owner\n // (server context) must bypass — it is the trusted plane.\n ddl += `${renderEnableRls(table)}\\n`;\n ddl += \"\\n\";\n for (const policy of table.policies) {\n policyStatements.push(statementsToDdl(renderPolicyStatements(table, policy)));\n }\n }\n }\n\n // Indexes before foreign keys. Today every FK targets a primary key created\n // inline, so the order is safe by accident; the moment a declared\n // `unique: true` index can back an FK target, a constraint emitted first\n // would reference an index that does not exist yet.\n if (indexStatements.length > 0) {\n ddl += \"-- Indexes\\n\";\n ddl += indexStatements.join(\"\\n\") + \"\\n\\n\";\n }\n\n if (fkStatements.length > 0) {\n ddl += \"-- Foreign Key Constraints\\n\";\n ddl += fkStatements.join(\"\\n\") + \"\\n\\n\";\n }\n\n if (policyStatements.length > 0) {\n ddl += \"-- Row Level Security Policies\\n\";\n ddl += policyStatements.join(\"\");\n ddl += \"\\n\";\n }\n\n return ddl;\n}\n\n// ── policies.sql ─────────────────────────────────────────────────────────────\n\nexport function renderPoliciesDdl(plan: SchemaPlan): string {\n let ddl = \"-- This file contains RLS policies generated by Rebase. Applied separately from migrations.\\n\\n\";\n\n // Collections first, then the junctions no collection declares — the order\n // the boot-time applier plans them in.\n for (const table of plan.tables.filter(t => t.kind === \"collection\")) {\n ddl += `${renderEnableRls(table)}\\n`;\n ddl += \"\\n\";\n if (table.policies.length === 0) continue;\n let announced: string | undefined;\n for (const policy of table.policies) {\n // Say which policies the author did not write. They are permissive,\n // so they OR with the declared rules and widen the final ACL beyond\n // what `securityRules` reads like — and re-appear after any manual\n // DROP, because a push asserts the declared state. Once per *rule*:\n // one rule with four operations is one thing the author did not\n // write, not four.\n if (policy.injected && policy.ruleKey !== announced) {\n ddl += \"-- Injected by Rebase (not from this collection's securityRules).\\n\";\n ddl += `-- Set \\`disableDefaultPolicies: true\\` on \"${table.slug}\" to drop these and own its RLS outright.\\n`;\n }\n announced = policy.ruleKey;\n ddl += statementsToDdl(renderPolicyStatements(table, policy));\n }\n ddl += \"\\n\";\n }\n\n for (const table of plan.tables.filter(t => t.kind === \"junction\")) {\n ddl += `${renderEnableRls(table)}\\n`;\n ddl += \"\\n\";\n if (table.policies.length === 0) continue;\n const declaringSlugs = (table.declaringSlugs ?? []).join('\", \"');\n ddl += `-- Derived by Rebase for the junction \"${table.table}\" (no collection declares it).\\n`;\n ddl += \"-- Reads require both endpoint rows to be visible; writes follow the update\\n\";\n ddl += `-- rules of \"${declaringSlugs}\". Set \\`disableDefaultPolicies: true\\` on the\\n`;\n ddl += \"-- declaring collection(s) to drop these and police the junction yourself.\\n\";\n for (const policy of table.policies) {\n ddl += statementsToDdl(renderPolicyStatements(table, policy));\n }\n ddl += \"\\n\";\n }\n\n return ddl;\n}\n\n// ── search.sql ───────────────────────────────────────────────────────────────\n\nexport function renderSearchDdl(plan: SchemaPlan): string {\n const tables = plan.tables.filter(t => t.search);\n if (tables.length === 0) return \"\";\n\n const extensions = Array.from(new Set(tables.flatMap(t => searchExtensionStatements(t.search!))));\n const helpers = Array.from(new Set(tables.flatMap(t => searchHelperFunctions(t.search!))));\n\n let ddl = \"-- This file is auto-generated by the Rebase DDL generator. Do not edit manually.\\n\";\n ddl += \"--\\n\";\n ddl += \"-- Full-text search for the collections declaring a `search` block.\\n\";\n ddl += \"-- Applied by Rebase, not by Atlas — see generatePostgresSearchDdl.\\n\\n\";\n\n extensions.forEach(s => { ddl += `${s}\\n`; });\n if (extensions.length > 0) ddl += \"\\n\";\n helpers.forEach(s => { ddl += `${s}\\n\\n`; });\n\n for (const table of tables) {\n const spec = table.search!;\n const target = `\"${table.schema}\".\"${table.table}\"`;\n // ADD COLUMN IF NOT EXISTS rather than the inline definition the\n // CREATE TABLE would use: by the time this runs the table exists,\n // whether Atlas just created it or it has been live for a year. The\n // guard first, because `ADD COLUMN IF NOT EXISTS` is a no-op against an\n // existing column — without it the file would report success, leave the\n // old expression in place, and then stamp it as current.\n searchStampGuards(spec).forEach(s => { ddl += `${s}\\n`; });\n ddl += `ALTER TABLE ${target} ADD COLUMN IF NOT EXISTS ${searchColumnDefinition(spec)};\\n`;\n const fuzzy = fuzzyColumnDefinition(spec);\n if (fuzzy) ddl += `ALTER TABLE ${target} ADD COLUMN IF NOT EXISTS ${fuzzy};\\n`;\n searchColumnStamps(spec).forEach(s => { ddl += `${s.sql}\\n`; });\n searchIndexStatements(spec).forEach(s => { ddl += `${s}\\n`; });\n ddl += \"\\n\";\n }\n\n return ddl;\n}\n\n/**\n * Glob patterns telling Atlas to leave an object alone.\n *\n * Fully qualified, `schema.table.object`, matching the include list. The\n * two-part form is what Atlas wants when the connection URL scopes it to one\n * schema and is *silently ignored* otherwise: it reads `posts.search_vector` as\n * a table named `search_vector` in a schema named `posts`, matches nothing, and\n * reports no error for the pattern that never fired.\n */\nexport const searchExcludePatternsOf = (plan: SchemaPlan): string[] => {\n const patterns: string[] = [];\n for (const table of plan.tables) {\n if (!table.search) continue;\n for (const column of table.columns) {\n if (column.source.kind !== \"search\") continue;\n patterns.push(`${table.schema}.${table.table}.${column.column}`);\n }\n for (const index of searchIndexNames(table.search)) {\n patterns.push(`${table.schema}.${table.table}.${index}`);\n }\n }\n return patterns;\n};\n\n// ── vector.sql ───────────────────────────────────────────────────────────────\n\nexport function renderVectorDdl(plan: SchemaPlan): string {\n const tables = plan.tables.filter(t => t.vectorColumns.length > 0);\n if (tables.length === 0) return \"\";\n\n let ddl = \"-- This file is auto-generated by the Rebase DDL generator. Do not edit manually.\\n\";\n ddl += \"--\\n\";\n ddl += \"-- Vector columns and their ANN indexes, for the collections declaring a\\n\";\n ddl += \"-- `vector` property. Applied by Rebase, not by Atlas — see\\n\";\n ddl += \"-- generatePostgresVectorDdl.\\n\\n\";\n // The note is not decoration. Someone reading this file after a failed push\n // is looking for the reason the type does not exist, and \"no CREATE\n // EXTENSION here\" is invisible unless it is written down.\n ddl += vectorExtensionDeclared(plan.options.databaseExtensions)\n ? `${vectorExtensionStatement()}\\n\\n`\n : \"-- pgvector is not installed from here: no database declared it. To let Rebase\\n\" +\n `-- install it, add ${VECTOR_EXTENSION_OPT_IN} in config/resources.ts.\\n` +\n \"-- Otherwise install it once by hand; the column below needs the type either way.\\n\\n\";\n\n for (const table of tables) {\n const target = `\"${table.schema}\".\"${table.table}\"`;\n for (const spec of table.vectorColumns) {\n // Guard before the ADD, so a changed `dimensions` is reported\n // rather than skipped over — see `vectorDimensionGuard`.\n ddl += `${vectorDimensionGuard(spec)}\\n`;\n ddl += `ALTER TABLE ${target} ADD COLUMN IF NOT EXISTS ${vectorColumnDefinition(spec)};\\n`;\n }\n if (table.vector) {\n vectorIndexStatements(table.vector).forEach(s => { ddl += `${s}\\n`; });\n for (const skip of table.vector.skipped) {\n ddl += `-- No ANN index on \"${skip.schema}\".\"${skip.table}\".\"${skip.column}\": ${skip.reason}\\n`;\n }\n }\n ddl += \"\\n\";\n }\n\n return ddl;\n}\n\n/** @see searchExcludePatternsOf */\nexport const vectorExcludePatternsOf = (plan: SchemaPlan): string[] => {\n const patterns: string[] = [];\n for (const table of plan.tables) {\n const names = [\n ...table.vectorColumns.map(spec => spec.column),\n ...(table.vector ? vectorIndexNames(table.vector) : [])\n ];\n for (const name of names) patterns.push(`${table.schema}.${table.table}.${name}`);\n }\n return patterns;\n};\n\n// ── triggers.sql ─────────────────────────────────────────────────────────────\n\n/**\n * `autoValue: \"on_update\"`, as the file Rebase applies itself.\n *\n * Empty when nothing declares one — the caller writes no file then, and removes\n * a stale one.\n */\nexport function renderTriggersDdl(plan: SchemaPlan): string {\n const tables = plan.tables.filter(t => t.triggers.length > 0);\n if (tables.length === 0) return \"\";\n\n let ddl = \"-- This file is auto-generated by the Rebase DDL generator. Do not edit manually.\\n\";\n ddl += \"--\\n\";\n ddl += \"-- `updated_at` triggers, for the properties declaring `autoValue: \\\"on_update\\\"`.\\n\";\n ddl += \"-- Applied by Rebase, not by Atlas — a trigger is a function plus a binding,\\n\";\n ddl += \"-- and Atlas's free tier refuses to parse a desired state containing one.\\n\\n\";\n\n for (const statement of plan.functions.filter(isTriggerFunction)) {\n ddl += `${statement}\\n\\n`;\n }\n for (const table of tables) {\n for (const trigger of table.triggers) {\n triggerStatements(trigger).forEach(s => { ddl += `${s}\\n`; });\n }\n ddl += \"\\n\";\n }\n return ddl;\n}\n\nconst isTriggerFunction = (statement: string): boolean => statement.includes(\"RETURNS trigger\");\n\n/** @see searchExcludePatternsOf */\nexport const triggerExcludePatternsOf = (plan: SchemaPlan): string[] =>\n plan.tables.flatMap(table => table.triggers.map(t => `${table.schema}.${table.table}.${t.name}`));\n"],"mappings":"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"}
@@ -6265,13 +6265,17 @@ var import___vite_browser_external = /* @__PURE__ */ __toESM(require___vite_brow
6265
6265
  * here, and refuse to auto-approve anything destructive without an explicit
6266
6266
  * opt-in.
6267
6267
  *
6268
- * A type change is the one destructive plan that names no DROP. `ALTER COLUMN
6268
+ * A type change is a destructive plan that names no DROP. `ALTER COLUMN
6269
6269
  * "price" TYPE integer` rounds every 19.99 to 20, and `TYPE date` throws away
6270
6270
  * every timestamp's time of day, in a statement that reads like a routine
6271
6271
  * alter. So a type change is gated too, unless it is a widening every value
6272
6272
  * survives unchanged — which needs the column's current type, read from the
6273
6273
  * catalogue by the caller.
6274
6274
  *
6275
+ * A primary key change names no DROP of data either: `DROP CONSTRAINT
6276
+ * "<table>_pkey", ADD PRIMARY KEY ("id", "locale")` keeps every value and
6277
+ * gives every row a new address. It is gated like a drop.
6278
+ *
6275
6279
  * Everything in this file is side-effect free so it can be unit-tested
6276
6280
  * without Atlas or a database.
6277
6281
  */
@@ -6315,6 +6319,96 @@ var DESTRUCTIVE_PATTERNS = [
6315
6319
  function splitSqlStatements(sql) {
6316
6320
  return sql.split("\n").filter((line) => !line.trim().startsWith("--")).join("\n").split(";").map((s) => s.trim()).filter((s) => s.length > 0);
6317
6321
  }
6322
+ /** `->`, the marker Atlas prints in front of the first line of every statement. */
6323
+ var PLAN_MARKER_RE = /^\s*->\s?/;
6324
+ /** `-------`, both the rule between sections and the one above a summary. */
6325
+ var PLAN_RULE_RE = /^\s*-{5,}\s*$/;
6326
+ /** `Planning migration statements (3 in total):` — and the count it announces. */
6327
+ var PLAN_HEADING_RE = /^Planning migration statements \((\d+) in total\):/m;
6328
+ /** What Atlas prints, and nothing else, when there is nothing to plan. */
6329
+ var PLAN_SYNCED_RE = /^\s*Schema is synced, no changes to be made\s*$/;
6330
+ /**
6331
+ * Read the statements out of whatever Atlas printed.
6332
+ *
6333
+ * `atlas schema apply --dry-run` does not print SQL. It prints a report, and
6334
+ * the same statements appear in it twice:
6335
+ *
6336
+ * Planning migration statements (2 in total):
6337
+ *
6338
+ * -- modify "people" table:
6339
+ * -> ALTER TABLE "public"."people" ALTER COLUMN "rating" TYPE numeric(4,1);
6340
+ * -- create "notes" table:
6341
+ * -> CREATE TABLE "public"."notes" (
6342
+ * "body" text NULL DEFAULT 'a;b',
6343
+ * …
6344
+ * );
6345
+ *
6346
+ * -------------------------------------------
6347
+ *
6348
+ * Analyzing planned statements (2 in total):
6349
+ * …
6350
+ * Running dry-run migration (2 statements in total):
6351
+ * -- modify "people" table
6352
+ * -> ALTER TABLE …; ← the same statement, again
6353
+ * -- ok (19µs)
6354
+ *
6355
+ * Splitting that on `;` is how the push gate went blind: the heading has no
6356
+ * `;` and does not start with `--`, so it became the first line of the first
6357
+ * statement, and an `ALTER TABLE` parse anchored at the start of a statement
6358
+ * read nothing — every narrowing type change applied without a prompt. Reading
6359
+ * both sections was a second bug, counting every destructive statement twice.
6360
+ *
6361
+ * So the report is read as a report: a statement is a `->` line and the lines
6362
+ * indented under it, up to the next `--` heading, `->`, blank line or rule;
6363
+ * only the first section that holds a `->` is read; and `;` plays no part,
6364
+ * because a default like `'a;b'` contains one.
6365
+ *
6366
+ * Plain SQL — a migration file, or an Atlas that printed SQL — has no `->`
6367
+ * and is split the old way.
6368
+ */
6369
+ function readPlan(plan) {
6370
+ if (PLAN_SYNCED_RE.test(plan)) return { statements: [] };
6371
+ const lines = plan.split(/\r?\n/);
6372
+ if (!lines.some((line) => PLAN_MARKER_RE.test(line))) return { statements: splitSqlStatements(plan) };
6373
+ const heading = PLAN_HEADING_RE.exec(plan);
6374
+ const statements = [];
6375
+ let current = null;
6376
+ const flush = () => {
6377
+ if (current) statements.push(current.join("\n").trim().replace(/;\s*$/, "").trimEnd());
6378
+ current = null;
6379
+ };
6380
+ for (const line of lines) {
6381
+ if (PLAN_MARKER_RE.test(line)) {
6382
+ flush();
6383
+ current = [line.replace(PLAN_MARKER_RE, "")];
6384
+ continue;
6385
+ }
6386
+ if (PLAN_RULE_RE.test(line) || /^\S/.test(line) && !line.startsWith("--")) {
6387
+ flush();
6388
+ if (statements.length > 0) break;
6389
+ continue;
6390
+ }
6391
+ if (!current) continue;
6392
+ if (line.trim() === "" || line.trim().startsWith("--")) {
6393
+ flush();
6394
+ continue;
6395
+ }
6396
+ current.push(line);
6397
+ }
6398
+ flush();
6399
+ return {
6400
+ statements,
6401
+ announced: heading ? Number(heading[1]) : void 0
6402
+ };
6403
+ }
6404
+ /**
6405
+ * The statements of an Atlas plan, each exactly once, without Atlas's
6406
+ * rendering and without the trailing `;`. See {@link readPlan} for the shape
6407
+ * it reads; `test/fixtures/atlas-dry-run/` holds real captures of it.
6408
+ */
6409
+ function extractPlanStatements(plan) {
6410
+ return readPlan(plan).statements;
6411
+ }
6318
6412
  /**
6319
6413
  * Every column of every ordinary table outside the system schemas, with its
6320
6414
  * type. What {@link detectDestructiveStatements} compares a planned type
@@ -6346,23 +6440,29 @@ var COLUMN_TYPES_SQL = `
6346
6440
  */
6347
6441
  function detectDestructiveStatements(planSql, columnTypes = []) {
6348
6442
  const found = [];
6349
- for (const statement of splitSqlStatements(planSql)) {
6443
+ const { statements, announced } = readPlan(planSql);
6444
+ if (announced !== void 0 && announced !== statements.length) found.push({
6445
+ statement: `Planning migration statements (${announced} in total)`,
6446
+ kind: "UNREADABLE PLAN",
6447
+ detail: `Atlas announced ${announced} statement(s) and ${statements.length} could be read — read the plan yourself before allowing it`
6448
+ });
6449
+ for (const statement of statements) {
6350
6450
  const dropped = DESTRUCTIVE_PATTERNS.find(({ re }) => re.test(statement));
6351
- if (dropped) {
6352
- found.push({
6353
- statement,
6354
- kind: dropped.label
6355
- });
6356
- continue;
6357
- }
6451
+ const rekeyed = describePrimaryKeyChange(statement);
6358
6452
  const lossy = parseColumnTypeChanges(statement).map((change) => ({
6359
6453
  change,
6360
6454
  from: currentTypeOf(change, columnTypes)
6361
- })).filter(({ change, from }) => from === void 0 || !isLosslessTypeChange(from, change.to));
6362
- if (lossy.length > 0) found.push({
6455
+ })).filter(({ change, from }) => from === void 0 || !isLosslessTypeChange(from, change.to)).map(({ change, from }) => `"${change.column}" ${from ?? "(current type unknown)"} → ${change.to}`).join("; ");
6456
+ const kind = dropped?.label ?? (rekeyed ? "PRIMARY KEY CHANGE" : lossy ? "ALTER COLUMN TYPE" : void 0);
6457
+ if (!kind) continue;
6458
+ const detail = [rekeyed, lossy].filter(Boolean).join("; ");
6459
+ found.push(detail ? {
6363
6460
  statement,
6364
- kind: "ALTER COLUMN TYPE",
6365
- detail: lossy.map(({ change, from }) => `"${change.column}" ${from ?? "(current type unknown)"} → ${change.to}`).join("; ")
6461
+ kind,
6462
+ detail
6463
+ } : {
6464
+ statement,
6465
+ kind
6366
6466
  });
6367
6467
  }
6368
6468
  return found;
@@ -6396,12 +6496,24 @@ function splitClauses(body) {
6396
6496
  return clauses.filter((clause) => clause.length > 0);
6397
6497
  }
6398
6498
  /**
6499
+ * The statement from its `ALTER TABLE` on, wherever on a line that starts.
6500
+ *
6501
+ * {@link readPlan} hands over clean statements. This is the second line of
6502
+ * defence for text it could not clean — a rendering it does not know yet —
6503
+ * where a line of prose above the statement must not hide the statement, which
6504
+ * is exactly how an anchored match once read Atlas's whole plan as safe.
6505
+ */
6506
+ function fromAlterTable(statement) {
6507
+ const start = /(?:^|\n)[ \t]*(?:->[ \t]*)?(ALTER\s+TABLE\b)/i.exec(statement);
6508
+ return start ? statement.slice(start.index + start[0].length - start[1].length).trim() : statement.trim();
6509
+ }
6510
+ /**
6399
6511
  * Every `ALTER COLUMN … [SET DATA] TYPE` in one statement. Atlas prints its
6400
6512
  * dry-run statements under a `-> ` marker and puts several clauses in one
6401
6513
  * `ALTER TABLE`, so both are handled.
6402
6514
  */
6403
6515
  function parseColumnTypeChanges(statement) {
6404
- const table = ALTER_TABLE_RE.exec(statement.replace(/^\s*->\s*/, "").trim());
6516
+ const table = ALTER_TABLE_RE.exec(fromAlterTable(statement));
6405
6517
  if (!table) return [];
6406
6518
  const [schema, name] = table[2] ? [unquote(table[1]), unquote(table[2])] : [void 0, unquote(table[1])];
6407
6519
  const changes = [];
@@ -6418,6 +6530,44 @@ function parseColumnTypeChanges(statement) {
6418
6530
  }
6419
6531
  return changes;
6420
6532
  }
6533
+ /** `ADD [CONSTRAINT "x"] PRIMARY KEY ("a", "b")`, one clause of an `ALTER TABLE`. */
6534
+ var ADD_PRIMARY_KEY_RE = new RegExp(String.raw`^ADD\s+(?:CONSTRAINT\s+${IDENT}\s+)?PRIMARY\s+KEY\b\s*(?:\(([^)]*)\))?`, "i");
6535
+ /** `DROP CONSTRAINT [IF EXISTS] "x"`, one clause of an `ALTER TABLE`. */
6536
+ var DROP_CONSTRAINT_RE = new RegExp(String.raw`^DROP\s+CONSTRAINT\s+(?:IF\s+EXISTS\s+)?(${IDENT})`, "i");
6537
+ /** What a row loses when the columns that address it change. */
6538
+ var ROW_ADDRESS_LOST = "every row's id changes, so anything that refers to a row by its id — a URL, a foreign key, an id stored elsewhere — stops finding it";
6539
+ /**
6540
+ * What an `ALTER TABLE` does to the table's primary key, in a sentence — or
6541
+ * `undefined` when it leaves the key alone.
6542
+ *
6543
+ * The key is a row's address: Rebase reads, links and routes every row by it.
6544
+ * Atlas plans a key change as `DROP CONSTRAINT "<table>_pkey", ADD PRIMARY KEY
6545
+ * (…)` — one routine-looking statement that names no DROP of data — so moving
6546
+ * a collection from `(id)` to `(id, locale)` re-keyed a populated table with
6547
+ * no prompt. A `CREATE TABLE`'s own `PRIMARY KEY` is not a change and is never
6548
+ * read here: only an `ALTER TABLE`'s clauses are. A dropped constraint counts
6549
+ * as the key when it carries Postgres's default name for one, `<table>_pkey`;
6550
+ * a key under another name is caught by the `ADD PRIMARY KEY` that replaces it.
6551
+ */
6552
+ function describePrimaryKeyChange(statement) {
6553
+ const alter = ALTER_TABLE_RE.exec(fromAlterTable(statement));
6554
+ if (!alter) return void 0;
6555
+ const table = alter[2] ? `${alter[1]}.${alter[2]}` : alter[1];
6556
+ let added;
6557
+ let dropped;
6558
+ for (const clause of splitClauses(alter[3])) {
6559
+ const add = ADD_PRIMARY_KEY_RE.exec(clause);
6560
+ if (add) {
6561
+ added = add[1] === void 0 ? "a new key" : `(${add[1].split(",").map((c) => c.trim()).join(", ")})`;
6562
+ continue;
6563
+ }
6564
+ const drop = DROP_CONSTRAINT_RE.exec(clause);
6565
+ if (drop && unquote(drop[1]).endsWith("_pkey")) dropped = drop[1];
6566
+ }
6567
+ if (added && dropped) return `${table} is re-keyed on ${added}: ${ROW_ADDRESS_LOST}`;
6568
+ if (added) return `${table} gets the primary key ${added}: rows are addressed by it from now on, and the push fails if two rows share a value`;
6569
+ if (dropped) return `${table} loses its primary key ${dropped}: ${ROW_ADDRESS_LOST}`;
6570
+ }
6421
6571
  /**
6422
6572
  * The column's current type. A table named without its schema matches in any
6423
6573
  * schema, but only when every match agrees — two same-named tables with
@@ -6551,6 +6701,80 @@ function decidePushSafety(opts) {
6551
6701
  //#region src/module-dir.ts
6552
6702
  var moduleDir = path.dirname(fileURLToPath$1(import.meta.url));
6553
6703
  //#endregion
6704
+ //#region src/generated-sql.ts
6705
+ /**
6706
+ * Where `db push` and `db generate` put the SQL they render from the collections.
6707
+ *
6708
+ * Five files — `schema.sql` (Atlas's desired state), `policies.sql`,
6709
+ * `search.sql`, `vector.sql` and `triggers.sql` — and every one of them is
6710
+ * scratch. Both commands regenerate all five before reading any of them, and
6711
+ * nothing else reads them at all. They used to be written into `drizzle/`, beside
6712
+ * the migrations, and committed: a copy nobody read, which every collection
6713
+ * change rewrote in the diff and every two branches touching collections
6714
+ * conflicted on.
6715
+ *
6716
+ * Relative to the directory the command runs in (the backend), like the
6717
+ * migrations are.
6718
+ */
6719
+ /** The directory, relative to the working directory. POSIX, because it is also a URL path. */
6720
+ var GENERATED_SQL_DIR = ".rebase/sql";
6721
+ /**
6722
+ * `schema.sql` as Atlas takes it. Relative on purpose, like the
6723
+ * `file://drizzle/migrations` beside it: Atlas joins the URL's host and path, so
6724
+ * `.rebase` is read as the first path segment, measured against the pinned 1.2.3.
6725
+ */
6726
+ var DESIRED_STATE_URL = `file://${GENERATED_SQL_DIR}/schema.sql`;
6727
+ /** The files the DDL generator writes, all of them derived from the collections. */
6728
+ var GENERATED_SQL_FILES = [
6729
+ "schema.sql",
6730
+ "policies.sql",
6731
+ "search.sql",
6732
+ "vector.sql",
6733
+ "triggers.sql"
6734
+ ];
6735
+ /** The absolute directory, for the working directory the command runs in. */
6736
+ function generatedSqlDir(cwd = process.cwd()) {
6737
+ return path.resolve(cwd, GENERATED_SQL_DIR);
6738
+ }
6739
+ /**
6740
+ * The first line of every file the generator writes, in every release that
6741
+ * wrote them into `drizzle/`. Matching it is what makes removing the old copy
6742
+ * safe: a file there under one of these names that does not open this way was
6743
+ * written by a person, and is left alone.
6744
+ */
6745
+ var GENERATED_HEADER = /^-- This file (is auto-generated by the Rebase DDL generator|contains RLS policies generated by Rebase)\./;
6746
+ /**
6747
+ * Get the directory ready to be written, and retire the copies an older release
6748
+ * committed.
6749
+ *
6750
+ * The directory ignores itself, rather than relying on the project's
6751
+ * `.gitignore`: a scaffold ignores `.rebase/`, but a project created before it
6752
+ * did would otherwise start committing these files from their new home.
6753
+ *
6754
+ * The old copies in `drizzle/` are deleted, not left behind. Nothing writes them
6755
+ * any more, so from the first push after an upgrade they only fall further
6756
+ * behind — and a stale `policies.sql` in the repository is exactly what someone
6757
+ * reviewing the project's RLS would read.
6758
+ *
6759
+ * @returns the old files it removed, relative to the working directory.
6760
+ */
6761
+ function prepareGeneratedSqlDir(cwd = process.cwd()) {
6762
+ const dir = generatedSqlDir(cwd);
6763
+ fs.mkdirSync(dir, { recursive: true });
6764
+ const ignore = path.join(dir, ".gitignore");
6765
+ if (!fs.existsSync(ignore)) fs.writeFileSync(ignore, "# Generated by `rebase db push` / `rebase db generate` from the collections.\n*\n");
6766
+ const removed = [];
6767
+ for (const name of GENERATED_SQL_FILES) {
6768
+ const legacy = path.join(cwd, "drizzle", name);
6769
+ if (!fs.existsSync(legacy)) continue;
6770
+ const firstLine = fs.readFileSync(legacy, "utf-8").split("\n", 1)[0];
6771
+ if (!GENERATED_HEADER.test(firstLine)) continue;
6772
+ fs.rmSync(legacy);
6773
+ removed.push(path.posix.join("drizzle", name));
6774
+ }
6775
+ return removed;
6776
+ }
6777
+ //#endregion
6554
6778
  //#region src/cli-helpers.ts
6555
6779
  /**
6556
6780
  * Which package manager installed this project, read off its lockfile.
@@ -6858,11 +7082,11 @@ async function dropDevDatabase(databaseUrl, devDatabaseUrl) {
6858
7082
  /**
6859
7083
  * The generated SQL for the project's `search` blocks, if it has any.
6860
7084
  *
6861
- * @param drizzleDir directory holding the generated SQL. Defaults to `drizzle`
6862
- * under the working directory.
7085
+ * @param sqlDir directory holding the generated SQL. Defaults to
7086
+ * `.rebase/sql` under the working directory.
6863
7087
  */
6864
- function readSearchDdl(drizzleDir = path.resolve(process.cwd(), "drizzle")) {
6865
- const searchFile = path.join(drizzleDir, "search.sql");
7088
+ function readSearchDdl(sqlDir = generatedSqlDir()) {
7089
+ const searchFile = path.join(sqlDir, "search.sql");
6866
7090
  if (!fs.existsSync(searchFile)) return "";
6867
7091
  return fs.readFileSync(searchFile, "utf-8").trim();
6868
7092
  }
@@ -6879,8 +7103,8 @@ function readSearchDdl(drizzleDir = path.resolve(process.cwd(), "drizzle")) {
6879
7103
  * silently pushing a schema whose search column never appeared is how a
6880
7104
  * collection ends up with search configured, no error anywhere, and no results.
6881
7105
  */
6882
- async function applySearchDdl(databaseUrl, drizzleDir = path.resolve(process.cwd(), "drizzle")) {
6883
- const sql = readSearchDdl(drizzleDir);
7106
+ async function applySearchDdl(databaseUrl, sqlDir = generatedSqlDir()) {
7107
+ const sql = readSearchDdl(sqlDir);
6884
7108
  if (!sql) return;
6885
7109
  const { Client } = await import("pg");
6886
7110
  const client = new Client({ connectionString: databaseUrl });
@@ -6899,7 +7123,7 @@ async function applySearchDdl(databaseUrl, drizzleDir = path.resolve(process.cwd
6899
7123
  * `search` block, which is every project that has not opted in.
6900
7124
  */
6901
7125
  async function getSearchExcludes(collectionsPath) {
6902
- const { searchExcludePatterns } = await import("./generate-postgres-ddl-logic-Bt2d2mRH.js").then((n) => n.o);
7126
+ const { searchExcludePatterns } = await import("./generate-postgres-ddl-logic-BJsLaVNX.js").then((n) => n.o);
6903
7127
  return searchExcludePatterns(await loadCollectionsForCli(collectionsPath));
6904
7128
  }
6905
7129
  /**
@@ -6977,11 +7201,11 @@ async function dropGeneratedColumns(databaseUrl, statements) {
6977
7201
  /**
6978
7202
  * The generated SQL for the project's `vector` properties, if it has any.
6979
7203
  *
6980
- * @param drizzleDir directory holding the generated SQL. Defaults to `drizzle`
6981
- * under the working directory.
7204
+ * @param sqlDir directory holding the generated SQL. Defaults to
7205
+ * `.rebase/sql` under the working directory.
6982
7206
  */
6983
- function readVectorDdl(drizzleDir = path.resolve(process.cwd(), "drizzle")) {
6984
- const vectorFile = path.join(drizzleDir, "vector.sql");
7207
+ function readVectorDdl(sqlDir = generatedSqlDir()) {
7208
+ const vectorFile = path.join(sqlDir, "vector.sql");
6985
7209
  if (!fs.existsSync(vectorFile)) return "";
6986
7210
  return fs.readFileSync(vectorFile, "utf-8").trim();
6987
7211
  }
@@ -7000,8 +7224,8 @@ function readVectorDdl(drizzleDir = path.resolve(process.cwd(), "drizzle")) {
7000
7224
  * appeared is how a project ends up with vector search configured, no error
7001
7225
  * anywhere, and no results.
7002
7226
  */
7003
- async function applyVectorDdl(databaseUrl, drizzleDir = path.resolve(process.cwd(), "drizzle")) {
7004
- const sql = readVectorDdl(drizzleDir);
7227
+ async function applyVectorDdl(databaseUrl, sqlDir = generatedSqlDir()) {
7228
+ const sql = readVectorDdl(sqlDir);
7005
7229
  if (!sql) return;
7006
7230
  const { vectorExtensionHint } = await import("./vector-index-CMJKb3C9.js").then((n) => n.f);
7007
7231
  const { Client } = await import("pg");
@@ -7026,18 +7250,18 @@ async function applyVectorDdl(databaseUrl, drizzleDir = path.resolve(process.cwd
7026
7250
  * `vector` property, which is every project that has not opted in.
7027
7251
  */
7028
7252
  async function getVectorExcludes(collectionsPath) {
7029
- const { vectorExcludePatterns } = await import("./generate-postgres-ddl-logic-Bt2d2mRH.js").then((n) => n.o);
7253
+ const { vectorExcludePatterns } = await import("./generate-postgres-ddl-logic-BJsLaVNX.js").then((n) => n.o);
7030
7254
  return vectorExcludePatterns(await loadCollectionsForCli(collectionsPath));
7031
7255
  }
7032
7256
  /**
7033
7257
  * The generated SQL for the project's `autoValue: "on_update"` properties, if
7034
7258
  * it has any.
7035
7259
  *
7036
- * @param drizzleDir directory holding the generated SQL. Defaults to `drizzle`
7037
- * under the working directory.
7260
+ * @param sqlDir directory holding the generated SQL. Defaults to
7261
+ * `.rebase/sql` under the working directory.
7038
7262
  */
7039
- function readTriggersDdl(drizzleDir = path.resolve(process.cwd(), "drizzle")) {
7040
- const triggersFile = path.join(drizzleDir, "triggers.sql");
7263
+ function readTriggersDdl(sqlDir = generatedSqlDir()) {
7264
+ const triggersFile = path.join(sqlDir, "triggers.sql");
7041
7265
  if (!fs.existsSync(triggersFile)) return "";
7042
7266
  return fs.readFileSync(triggersFile, "utf-8").trim();
7043
7267
  }
@@ -7055,8 +7279,8 @@ function readTriggersDdl(drizzleDir = path.resolve(process.cwd(), "drizzle")) {
7055
7279
  * appeared is how `updated_at` ends up silently stale for every writer that is
7056
7280
  * not the driver, which is the whole reason the trigger exists.
7057
7281
  */
7058
- async function applyTriggersDdl(databaseUrl, drizzleDir = path.resolve(process.cwd(), "drizzle")) {
7059
- const sql = readTriggersDdl(drizzleDir);
7282
+ async function applyTriggersDdl(databaseUrl, sqlDir = generatedSqlDir()) {
7283
+ const sql = readTriggersDdl(sqlDir);
7060
7284
  if (!sql) return;
7061
7285
  const { Client } = await import("pg");
7062
7286
  const client = new Client({ connectionString: databaseUrl });
@@ -7070,7 +7294,7 @@ async function applyTriggersDdl(databaseUrl, drizzleDir = path.resolve(process.c
7070
7294
  }
7071
7295
  /** @see getSearchExcludes */
7072
7296
  async function getTriggerExcludes(collectionsPath) {
7073
- const { triggerExcludePatterns } = await import("./generate-postgres-ddl-logic-Bt2d2mRH.js").then((n) => n.o);
7297
+ const { triggerExcludePatterns } = await import("./generate-postgres-ddl-logic-BJsLaVNX.js").then((n) => n.o);
7074
7298
  return triggerExcludePatterns(await loadCollectionsForCli(collectionsPath));
7075
7299
  }
7076
7300
  /**
@@ -7156,8 +7380,8 @@ async function getForeignIndexExcludes(databaseUrl, collectionsPath, deps = {})
7156
7380
  */
7157
7381
  async function managedIndexNames(collectionsPath) {
7158
7382
  const collections = await loadCollectionsForCli(collectionsPath);
7159
- const { searchExcludePatterns, vectorExcludePatterns } = await import("./generate-postgres-ddl-logic-Bt2d2mRH.js").then((n) => n.o);
7160
- const { resolveColumnName } = await import("./column-plan-helpers-DF-8dTVa.js").then((n) => n.n);
7383
+ const { searchExcludePatterns, vectorExcludePatterns } = await import("./generate-postgres-ddl-logic-BJsLaVNX.js").then((n) => n.o);
7384
+ const { resolveColumnName } = await import("./column-plan-helpers-1-LQD0yI.js").then((n) => n.t);
7161
7385
  const { buildCollectionIndexPlan } = await import("./collection-index-DdnsxiJ_.js").then((n) => n.i);
7162
7386
  const names = new Set(buildCollectionIndexPlan(collections, resolveColumnName).map((spec) => spec.indexName));
7163
7387
  for (const pattern of [...searchExcludePatterns(collections), ...vectorExcludePatterns(collections)]) names.add(pattern.slice(pattern.lastIndexOf(".") + 1));
@@ -7494,6 +7718,6 @@ async function dropLegacyAuthSchema(run, report) {
7494
7718
  }
7495
7719
  }
7496
7720
  //#endregion
7497
- export { resolveLocalBin as A, loadCollectionsForCli as C, readSearchDdl as D, queryGeneratedColumnDependencies as E, forLibpq as F, detectDestructiveStatements as M, splitSqlStatements as N, readTriggersDdl as O, execa as P, getVectorExcludes as S, queryColumnTypes as T, getDevDatabaseUrl as _, ExcludeIntrospectionError as a, getTableExcludes as b, applyVectorDdl as c, describeReinstallCommand as d, detectProjectPackageManager as f, ensureDevDatabaseExists as g, dropGeneratedColumns as h, rls_bootstrap_sql_exports as i, decidePushSafety as j, readVectorDdl as k, describeBuildScriptRemedy as l, dropDevDatabase as m, RLS_BOOTSTRAP_STATEMENTS as n, applySearchDdl as o, diagnoseMissingBin as p, dropLegacyAuthSchema as r, applyTriggersDdl as s, RLS_BOOTSTRAP_SQL as t, describeDevAddCommand as u, getForeignIndexExcludes as v, promptConfirm as w, getTriggerExcludes as x, getSearchExcludes as y };
7721
+ export { resolveLocalBin as A, forLibpq as B, loadCollectionsForCli as C, readSearchDdl as D, queryGeneratedColumnDependencies as E, decidePushSafety as F, detectDestructiveStatements as I, extractPlanStatements as L, GENERATED_SQL_DIR as M, generatedSqlDir as N, readTriggersDdl as O, prepareGeneratedSqlDir as P, fromAlterTable as R, getVectorExcludes as S, queryColumnTypes as T, getDevDatabaseUrl as _, ExcludeIntrospectionError as a, getTableExcludes as b, applyVectorDdl as c, describeReinstallCommand as d, detectProjectPackageManager as f, ensureDevDatabaseExists as g, dropGeneratedColumns as h, rls_bootstrap_sql_exports as i, DESIRED_STATE_URL as j, readVectorDdl as k, describeBuildScriptRemedy as l, dropDevDatabase as m, RLS_BOOTSTRAP_STATEMENTS as n, applySearchDdl as o, diagnoseMissingBin as p, dropLegacyAuthSchema as r, applyTriggersDdl as s, RLS_BOOTSTRAP_SQL as t, describeDevAddCommand as u, getForeignIndexExcludes as v, promptConfirm as w, getTriggerExcludes as x, getSearchExcludes as y, execa as z };
7498
7722
 
7499
- //# sourceMappingURL=rls-bootstrap-sql-B8EclDyM.js.map
7723
+ //# sourceMappingURL=rls-bootstrap-sql-_KNnjanK.js.map