@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
@@ -1,8 +1,8 @@
1
1
  import { createRequire as __createRequire } from "module";
2
2
  __createRequire(import.meta.url);
3
3
  import { r as __exportAll } from "./__vite-browser-external-BnuHet1e.js";
4
- import { n as planSchema } from "./plan-schema-C0fxM8dY.js";
5
4
  import { s as renderPredicate } from "./collection-index-DdnsxiJ_.js";
5
+ import { n as planSchema } from "./plan-schema-DU9exq6C.js";
6
6
  import { relationalCollections, sortCollectionsBySlug } from "@rebasepro/common";
7
7
  //#region src/schema/plan/render-drizzle.ts
8
8
  var JS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
@@ -171,8 +171,11 @@ function renderDrizzleSchema(plan, options = {}) {
171
171
  const lines = [...table.columns.filter((c) => c.source.kind !== "implicit-id"), ...table.columns.filter((c) => c.source.kind === "implicit-id")].filter((c) => !c.columnOwnedByProperty && c.source.kind !== "auth").map((column) => ` ${propKey(column.key)}: ${renderColumn(column, table, plan, uses)}`);
172
172
  body += table.kind === "junction" ? `${lines.join(",\n")},\n` : `${lines.join(",\n")}\n`;
173
173
  const extras = [];
174
- if (table.kind === "junction") extras.push(` ${needs(uses, "primaryKey")}({ columns: [${table.primaryKey.map((c) => member("table", c)).join(", ")}] }),`);
175
- else extras.push(...indexExtras(table, uses));
174
+ if (table.primaryKey.length > 1) {
175
+ const keyMember = (name) => member("table", table.columns.find((c) => c.column === name && !c.columnOwnedByProperty)?.key ?? name);
176
+ extras.push(` ${needs(uses, "primaryKey")}({ columns: [${table.primaryKey.map(keyMember).join(", ")}] }),`);
177
+ }
178
+ if (table.kind === "collection") extras.push(...indexExtras(table, uses));
176
179
  if (withPolicies) extras.push(...table.policies.map((policy) => policyExtra(policy, uses)));
177
180
  if (extras.length > 0) {
178
181
  body += "}, (table) => ([\n";
@@ -185,7 +188,7 @@ function renderDrizzleSchema(plan, options = {}) {
185
188
  for (const table of plan.tables) {
186
189
  const entries = plan.relations.filter((r) => r.tableVar === table.varName);
187
190
  if (entries.length === 0) continue;
188
- const rendered = entries.map((relation) => renderRelation(table, relation));
191
+ const rendered = entries.map((relation) => renderRelation(table, relation, uses));
189
192
  const varName = `${table.varName}Relations`;
190
193
  body += `export const ${varName} = drizzleRelations(${table.varName}, ({ one, many }) => ({\n${rendered.join(",\n")}\n}));\n\n`;
191
194
  if (!relationVars.includes(varName)) relationVars.push(varName);
@@ -201,10 +204,25 @@ function renderDrizzleSchema(plan, options = {}) {
201
204
  out += `export const relations = { ${relationVars.join(", ")} };\n\n`;
202
205
  return out;
203
206
  }
204
- var renderRelation = (table, relation) => {
207
+ var renderRelation = (table, relation, uses) => {
205
208
  if (relation.kind === "many") return ` ${quote(relation.key)}: many(${relation.targetVar}, { relationName: ${quote(relation.relationName)} })`;
206
- if (!relation.fields) return ` ${quote(relation.key)}: one(${relation.targetVar})`;
207
- return ` ${quote(relation.key)}: one(${relation.targetVar}, {\n fields: [${relation.fields.map((f) => member(table.varName, f)).join(", ")}],\n references: [${relation.references.map((r) => member(relation.targetVar, r)).join(", ")}],\n relationName: ${quote(relation.relationName)}\n })`;
209
+ return ` ${quote(relation.key)}: one${oneTypeArguments(table, relation, uses)}(${relation.targetVar}, {\n fields: [${relation.fields.map((f) => member(table.varName, f)).join(", ")}],\n references: [${relation.references.map((r) => member(relation.targetVar, r)).join(", ")}],\n relationName: ${quote(relation.relationName)}\n })`;
210
+ };
211
+ /**
212
+ * Explicit type arguments for a `one()` whose target row may not exist.
213
+ *
214
+ * Drizzle types a `one()` as never null when every column in `fields` is NOT
215
+ * NULL — right for a foreign key, wrong for a `hasOne`, whose `fields` are this
216
+ * table's own key. Giving `TColumns` as a column of this table with no declared
217
+ * nullability makes the related row `T | null` in a relational query's result,
218
+ * which is what the database returns. Empty for every other `one()`, whose
219
+ * inferred type is already right.
220
+ */
221
+ var oneTypeArguments = (table, relation, uses) => {
222
+ if (!relation.nullable) return "";
223
+ needs(uses, "type AnyPgColumn");
224
+ const column = `AnyPgColumn<{ tableName: ${quote(table.table)} }>`;
225
+ return `<typeof ${relation.targetVar}, [${relation.fields.map(() => column).join(", ")}]>`;
208
226
  };
209
227
  //#endregion
210
228
  //#region src/schema/generate-drizzle-schema-logic.ts
@@ -227,4 +245,4 @@ var generateSchema = (allCollections, options = {}) => {
227
245
  //#endregion
228
246
  export { generate_drizzle_schema_logic_exports as n, generateSchema as t };
229
247
 
230
- //# sourceMappingURL=generate-drizzle-schema-logic-BfzK7UQd.js.map
248
+ //# sourceMappingURL=generate-drizzle-schema-logic-sSFDp6LR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-drizzle-schema-logic-sSFDp6LR.js","names":[],"sources":["../src/schema/plan/render-drizzle.ts","../src/schema/generate-drizzle-schema-logic.ts"],"sourcesContent":["/**\n * `schema.generated.ts`, rendered from a {@link SchemaPlan}.\n *\n * This file is what a developer running drizzle-kit themselves diffs against,\n * and what the runtime's query builder is typed by. It has to *compile*, which\n * nothing in this repo checked until `generated-schema-compiles.test.ts`: six\n * ordinary inputs used to produce a file TypeScript rejects, and the class the\n * bigint incident already taught is that a file which will not build gets\n * hand-patched and then sits stale until a security fix misses production.\n *\n * Two structural rules keep that from coming back:\n *\n * - **The import list is derived from what was emitted.** Every builder is\n * recorded as it is used ({@link BuilderUses}), and the header is written\n * last, from that set. It used to be a fixed roster plus three\n * property-scanning heuristics, so `columnType: \"uuid\"` on a plain string\n * emitted `uuid(…)` and imported nothing.\n * - **Nothing here reads a `Property`.** Types, defaults, keys and constraints\n * all arrive decided. The Drizzle generator disagreeing with the DDL one is\n * what left `geopoint` with a database column and no Drizzle key.\n */\nimport type { ColumnPlan, ForeignKeyPlan, OneRelationPlan, PgType, PolicyPlan, RelationPlan, SchemaPlan, TablePlan } from \"./types\";\nimport { renderPredicate } from \"../collection-index\";\n\n/** What the generated file may leave out. */\nexport interface DrizzleRenderOptions {\n /**\n * `false` strips the `pgPolicy(...)` calls. RLS is still enabled on every\n * table regardless — a bare table must default-deny, not fail open.\n */\n policies?: boolean;\n}\n\nconst JS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\n/**\n * A string literal for the generated file.\n *\n * Column names, table names and enum values are all written in as literals and\n * none of them is constrained to be quote-free: a Postgres identifier only has\n * to be quoted, and `O'Brien` is an ordinary enum value. Interpolating them raw\n * ended the literal early.\n */\nconst quote = (value: string): string => JSON.stringify(value);\n\n/** An object key: verbatim when it is an identifier, quoted otherwise. */\nconst propKey = (name: string): string => (JS_IDENTIFIER.test(name) ? name : quote(name));\n\n/**\n * A property access on a generated table variable.\n *\n * `users.full name` is not an expression; `users[\"full name\"]` is, and Drizzle\n * treats the two identically.\n */\nconst member = (object: string, key: string): string =>\n (JS_IDENTIFIER.test(key) ? `${object}.${key}` : `${object}[${quote(key)}]`);\n\n/**\n * The drizzle-orm builders this file emitted, collected as they are written.\n * The only way to use a builder is to record it here.\n */\ntype BuilderUses = Set<string>;\n\n/** Record a builder and hand back its name, so a call site cannot use one silently. */\nconst needs = (uses: BuilderUses, builder: string): string => {\n uses.add(builder);\n return builder;\n};\n\n/**\n * Wraps a compiled SQL clause in a Drizzle `sql\\`...\\`` template literal.\n *\n * The clause is SQL being written into a TypeScript file, so it has to survive\n * being read back as a template literal. Three characters do not:\n *\n * - `` ` `` closes the template early, and the rest of the clause becomes code.\n * - `${` opens an interpolation — the file stops compiling, or worse, compiles\n * against whatever identifier happens to be in scope.\n * - `\\` is an escape, and Drizzle's `sql` tag reads the *cooked* strings, not\n * `.raw`. So a policy written as `email ~ '^admin\\.user@corp\\.com$'` reached\n * the database as `^admin.user@corp.com$`, where every `\\.` now matches any\n * character. A `USING` clause is a security boundary and that one silently\n * widened it.\n */\nconst wrapSql = (clause: string): string =>\n `sql\\`${clause.replace(/\\\\/g, \"\\\\\\\\\").replace(/`/g, \"\\\\`\").replace(/\\$\\{/g, \"\\\\${\")}\\``;\n\n/** The pg-core builder call for a tagged type, minus the column name. */\nconst builderFor = (type: PgType, column: string, uses: BuilderUses): string => {\n const name = quote(column);\n switch (type.kind) {\n case \"text\": return `${needs(uses, \"text\")}(${name})`;\n case \"varchar\": return `${needs(uses, \"varchar\")}(${name}, { length: ${type.length} })`;\n case \"char\": return `${needs(uses, \"char\")}(${name}, { length: ${type.length} })`;\n case \"uuid\": return `${needs(uses, \"uuid\")}(${name})`;\n // The enum variable is declared at the top of the same file; it is not\n // a pg-core import.\n case \"enum\": return `${type.varName}(${name})`;\n case \"smallint\": return `${needs(uses, \"smallint\")}(${name})`;\n case \"integer\": return `${needs(uses, \"integer\")}(${name})`;\n // `bigint` and `bigserial` are the only pg-core builders that *require*\n // a config argument: without `mode`, drizzle cannot know whether to hand\n // back a `number` or a `bigint`, and the emitted call does not\n // typecheck. This is why `schema.generated.ts` drifted — regenerating it\n // produced a file that would not compile, so the bigint lines were\n // hand-patched, and every regeneration after that looked like a large,\n // alarming diff nobody wanted to ship.\n //\n // `number` rather than `bigint`: these are counters and byte totals that\n // every caller already treats as numbers.\n case \"bigint\": return `${needs(uses, \"bigint\")}(${name}, { mode: \"number\" })`;\n case \"bigserial\": return `${needs(uses, \"bigserial\")}(${name}, { mode: \"number\" })`;\n case \"smallserial\": return `${needs(uses, \"smallserial\")}(${name})`;\n case \"serial\": return `${needs(uses, \"serial\")}(${name})`;\n case \"real\": return `${needs(uses, \"real\")}(${name})`;\n case \"doublePrecision\": return `${needs(uses, \"doublePrecision\")}(${name})`;\n case \"numeric\":\n if (type.precision === undefined) return `${needs(uses, \"numeric\")}(${name})`;\n return type.scale === undefined\n ? `${needs(uses, \"numeric\")}(${name}, { precision: ${type.precision} })`\n : `${needs(uses, \"numeric\")}(${name}, { precision: ${type.precision}, scale: ${type.scale} })`;\n case \"boolean\": return `${needs(uses, \"boolean\")}(${name})`;\n case \"timestamptz\": return `${needs(uses, \"timestamp\")}(${name}, { withTimezone: true, mode: 'string' })`;\n case \"date\": return `${needs(uses, \"date\")}(${name}, { mode: 'string' })`;\n case \"time\": return `${needs(uses, \"time\")}(${name})`;\n case \"json\": return `${needs(uses, \"json\")}(${name})`;\n case \"jsonb\": return `${needs(uses, \"jsonb\")}(${name})`;\n case \"vector\": return `${needs(uses, \"vector\")}(${name}, { dimensions: ${type.dimensions} })`;\n case \"bytea\": return `${needs(uses, \"customType\")}({ dataType() { return 'bytea'; } })(${name})`;\n case \"tsvector\": return `${needs(uses, \"customType\")}({ dataType() { return 'tsvector'; } })(${name})`;\n // pg-core has no array *type*; `.array()` is a modifier on the element.\n case \"array\": return `${builderFor(type.of, column, uses)}.array()`;\n }\n};\n\n/**\n * The `.references(...)` clause a foreign key column carries.\n *\n * The `(): AnyPgColumn =>` annotation is not optional decoration and it is\n * emitted on every reference rather than only the self-referential ones. A\n * `belongsTo` pointing at its own table — a comment thread, a category tree —\n * produces `.references(() => posts.id)` inside the initializer of `posts`, and\n * TypeScript cannot infer a type for a `const` that appears in its own\n * initializer (TS7022). Drizzle documents the annotation as the fix; applying\n * it everywhere means the generated line does not depend on whether the author\n * happened to point the relation at another table.\n */\nconst referencesClause = (\n fk: ForeignKeyPlan,\n targetVar: string,\n targetKey: string,\n uses: BuilderUses\n): string => {\n needs(uses, \"type AnyPgColumn\");\n const parts = [\n fk.onUpdate ? `onUpdate: \"${fk.onUpdate.toLowerCase()}\"` : \"\",\n `onDelete: \"${fk.onDelete.toLowerCase()}\"`\n ].filter(Boolean);\n return `.references((): AnyPgColumn => ${member(targetVar, targetKey)}, { ${parts.join(\", \")} })`;\n};\n\n/** The declaration a column compiles to, without its object key. */\nconst renderColumn = (column: ColumnPlan, table: TablePlan, plan: SchemaPlan, uses: BuilderUses): string => {\n let out = builderFor(column.type, column.column, uses);\n\n if (column.generated) {\n return `${out}.generatedAlwaysAs(${wrapSql(column.generated.expression)})`;\n }\n\n // A junction endpoint spells `.notNull()` before its reference; every other\n // column spells it last. Both are the same constraint — this keeps the\n // generated file byte-stable across the move to one planner.\n const junctionKey = column.source.kind === \"junction-key\";\n if (junctionKey) out += \".notNull()\";\n\n if (column.default?.kind === \"identity\") out += \".generatedByDefaultAsIdentity()\";\n if (column.primaryKey) out += \".primaryKey()\";\n if (column.default && column.default.kind !== \"identity\") {\n const expression = column.default.kind === \"sql\" ? column.default.expression : column.default.sql;\n // `.defaultRandom()` is drizzle's own spelling of `gen_random_uuid()`,\n // and it exists **only on the uuid builder** — a `text` id whose\n // strategy happens to be spelled ``isId: \"sql`gen_random_uuid()`\"`` has\n // to take the generic form, which is what the file has always emitted\n // for it.\n out += column.type.kind === \"uuid\" && expression === \"gen_random_uuid()\"\n ? \".defaultRandom()\"\n : `.default(${wrapSql(expression)})`;\n }\n // A plain property that carries a relation's foreign key column gets the\n // constraint the relation would have emitted. `postId: { columnName:\n // \"post_id\" }` beside a `belongsTo` on `post_id` used to emit an integer\n // with no `.references()` at all, so that project got no foreign key on the\n // Drizzle side while `db push` created one.\n const foreignKey = column.foreignKey\n ?? table.columns.find(c => c.columnOwnedByProperty && c.column === column.column)?.foreignKey;\n if (foreignKey) {\n const target = plan.tables.find(t =>\n t.schema === foreignKey.targetSchema && t.table === foreignKey.targetTable);\n if (target) {\n const targetColumn = target.columns.find(c => c.column === foreignKey.targetColumn);\n out += referencesClause(foreignKey, target.varName, targetColumn?.key ?? foreignKey.targetColumn, uses);\n }\n }\n if (column.unique) out += \".unique()\";\n if (!column.nullable && !column.primaryKey && !junctionKey) out += \".notNull()\";\n return out;\n};\n\n/** The declared `indexes:` block, as Drizzle table extras. */\nconst indexExtras = (table: TablePlan, uses: BuilderUses): string[] =>\n table.indexes.map(spec => {\n const builder = spec.unique ? needs(uses, \"uniqueIndex\") : needs(uses, \"index\");\n // The spec holds COLUMN names; the generated table is keyed by field\n // name (`authorId` for `author_id`).\n const column = (name: string): string =>\n member(\"table\", table.columns.find(c => c.column === name)?.key ?? name);\n const keys = spec.keys.map(key => {\n if (spec.method !== \"btree\") return column(key.column);\n const direction = key.direction === \"desc\" ? \".desc()\" : \".asc()\";\n const nulls = key.nulls === \"first\" ? \".nullsFirst()\" : \".nullsLast()\";\n return `${column(key.column)}${direction}${nulls}`;\n });\n const on = spec.method === \"btree\"\n ? `.on(${keys.join(\", \")})`\n : `.using(${quote(spec.method)}, ${keys.join(\", \")})`;\n const where = spec.predicate ? `.where(sql\\`${renderPredicate(spec.predicate)}\\`)` : \"\";\n // drizzle-orm has no INCLUDE in its index builder (0.45), so a covering\n // index is emitted here as its key columns alone. The database still\n // gets the INCLUDE — `schema.sql` and boot-ensure both emit it.\n const covering = spec.include.length > 0\n ? ` // INCLUDE (${spec.include.join(\", \")}) — drizzle cannot express it; schema.sql does`\n : \"\";\n return ` ${builder}(${quote(spec.indexName)})${on}${where},${covering}`;\n });\n\nconst policyExtra = (policy: PolicyPlan, uses: BuilderUses): string => {\n needs(uses, \"pgPolicy\");\n const parts = [\n `as: \"${policy.mode}\"`,\n `for: \"${policy.operation}\"`,\n `to: [${policy.roles.map(r => `\"${r}\"`).join(\", \")}]`\n ];\n if (policy.using) parts.push(`using: ${wrapSql(policy.using)}`);\n if (policy.withCheck) parts.push(`withCheck: ${wrapSql(policy.withCheck)}`);\n return ` pgPolicy(${quote(policy.name)}, { ${parts.join(\", \")} }),`;\n};\n\nexport function renderDrizzleSchema(plan: SchemaPlan, options: DrizzleRenderOptions = {}): string {\n const withPolicies = options.policies !== false;\n const uses: BuilderUses = new Set();\n // The body is assembled first and the header composed around it, because\n // the header cannot be written until the body has said what it needs.\n let body = \"\";\n\n let schemaDeclarations = \"\";\n if (plan.declaredSchemas.length > 0) {\n needs(uses, \"pgSchema\");\n plan.declaredSchemas.forEach(schema => {\n schemaDeclarations += `export const ${schema}Schema = pgSchema(\"${schema}\");\\n`;\n });\n schemaDeclarations += \"\\n\";\n }\n\n const enumVars: string[] = [];\n for (const enumPlan of plan.enums) {\n // A collection with `schema: \"app\"` gets `CREATE TYPE \"app\".\"…\"` from\n // the DDL renderer, so the type has to be declared in the same schema\n // here — `pgEnum` is `public` and nothing else. Unqualified, the runtime\n // and drizzle-kit looked for a type that does not exist in `public`.\n const labels = enumPlan.labels.map(quote).join(\", \");\n const declaration = enumPlan.declaredSchema\n ? `${enumPlan.declaredSchema}Schema.enum(${quote(enumPlan.name)}, [${labels}])`\n : `${needs(uses, \"pgEnum\")}(${quote(enumPlan.name)}, [${labels}])`;\n body += `export const ${enumPlan.varName} = ${declaration};\\n`;\n if (!enumVars.includes(enumPlan.varName)) enumVars.push(enumPlan.varName);\n }\n body += \"\\n\";\n\n const tableVars: string[] = [];\n for (const table of plan.tables) {\n const creator = table.declaredSchema\n ? `${table.declaredSchema}Schema.table`\n : needs(uses, \"pgTable\");\n body += `export const ${table.varName} = ${creator}(\"${table.table}\", {\\n`;\n\n // The implicit `id` goes last here and first in the SQL file: the DDL\n // generator unshifts it so the key leads the `CREATE TABLE`, and this\n // one appends it after the generated search columns. Both describe the\n // same table; only the reading order differs.\n const ordered = [\n ...table.columns.filter(c => c.source.kind !== \"implicit-id\"),\n ...table.columns.filter(c => c.source.kind === \"implicit-id\")\n ].filter(c =>\n !c.columnOwnedByProperty\n // The auth columns a users collection does not itself declare —\n // `is_anonymous`, `tokens_valid_after`. `db push` emits them because\n // Atlas diffs the database against exactly what it is shown and\n // would otherwise plan a DROP; drizzle-kit creates no auth table, so\n // this file describes the collection as written and leaves auth's\n // own contract to the SQL side that owns it.\n && c.source.kind !== \"auth\");\n\n const lines = ordered.map(column => ` ${propKey(column.key)}: ${renderColumn(column, table, plan, uses)}`);\n // A junction's column list carries a trailing comma; a collection's does\n // not. Kept as the two files have always spelled them.\n body += table.kind === \"junction\"\n ? `${lines.join(\",\\n\")},\\n`\n : `${lines.join(\",\\n\")}\\n`;\n\n const extras: string[] = [];\n // A key over several columns is one `primaryKey({ columns })` extra —\n // `.primaryKey()` on each column would declare several primary keys.\n // The plan lists COLUMN names and the table is keyed by field name\n // (`authorId` for `author_id`), so each is looked up.\n if (table.primaryKey.length > 1) {\n const keyMember = (name: string): string =>\n member(\"table\", table.columns.find(c => c.column === name && !c.columnOwnedByProperty)?.key ?? name);\n extras.push(` ${needs(uses, \"primaryKey\")}({ columns: [${table.primaryKey.map(keyMember).join(\", \")}] }),`);\n }\n if (table.kind === \"collection\") {\n extras.push(...indexExtras(table, uses));\n }\n if (withPolicies) {\n extras.push(...table.policies.map(policy => policyExtra(policy, uses)));\n }\n\n if (extras.length > 0) {\n body += \"}, (table) => ([\\n\";\n body += `${extras.join(\"\\n\")}\\n`;\n body += \"])).enableRLS();\\n\\n\";\n } else {\n // No explicit policies and no declared indexes — RLS enabled with a\n // deny-all default (Postgres denies everything when RLS is on and no\n // permissive policies exist).\n body += \"}).enableRLS();\\n\\n\";\n }\n if (!tableVars.includes(table.varName)) tableVars.push(table.varName);\n }\n\n const relationVars: string[] = [];\n for (const table of plan.tables) {\n const entries = plan.relations.filter(r => r.tableVar === table.varName);\n if (entries.length === 0) continue;\n const rendered = entries.map(relation => renderRelation(table, relation, uses));\n const varName = `${table.varName}Relations`;\n body += `export const ${varName} = drizzleRelations(${table.varName}, ({ one, many }) => ({\\n${rendered.join(\",\\n\")}\\n}));\\n\\n`;\n if (!relationVars.includes(varName)) relationVars.push(varName);\n }\n\n // ── The header, written last because the body decides what is in it ──────\n const imports = Array.from(uses).sort((a, b) =>\n a.replace(/^type /, \"\").localeCompare(b.replace(/^type /, \"\")));\n\n let out = \"// This file is auto-generated by the Rebase Drizzle generator. Do not edit manually.\\n\\n\";\n out += `import { ${imports.join(\", \")} } from 'drizzle-orm/pg-core';\\n`;\n out += \"import { relations as drizzleRelations, sql } from 'drizzle-orm';\\n\\n\";\n out += schemaDeclarations;\n out += body;\n out += `export const tables = { ${tableVars.join(\", \")} };\\n`;\n out += `export const enums = { ${enumVars.join(\", \")} };\\n`;\n out += `export const relations = { ${relationVars.join(\", \")} };\\n\\n`;\n return out;\n}\n\nconst renderRelation = (table: TablePlan, relation: RelationPlan, uses: BuilderUses): string => {\n if (relation.kind === \"many\") {\n return ` ${quote(relation.key)}: many(${relation.targetVar}, { relationName: ${quote(relation.relationName)} })`;\n }\n return ` ${quote(relation.key)}: one${oneTypeArguments(table, relation, uses)}(${relation.targetVar}, {\\n` +\n ` fields: [${relation.fields.map(f => member(table.varName, f)).join(\", \")}],\\n` +\n ` references: [${relation.references.map(r => member(relation.targetVar, r)).join(\", \")}],\\n` +\n ` relationName: ${quote(relation.relationName)}\\n` +\n \" })\";\n};\n\n/**\n * Explicit type arguments for a `one()` whose target row may not exist.\n *\n * Drizzle types a `one()` as never null when every column in `fields` is NOT\n * NULL — right for a foreign key, wrong for a `hasOne`, whose `fields` are this\n * table's own key. Giving `TColumns` as a column of this table with no declared\n * nullability makes the related row `T | null` in a relational query's result,\n * which is what the database returns. Empty for every other `one()`, whose\n * inferred type is already right.\n */\nconst oneTypeArguments = (table: TablePlan, relation: OneRelationPlan, uses: BuilderUses): string => {\n if (!relation.nullable) return \"\";\n needs(uses, \"type AnyPgColumn\");\n const column = `AnyPgColumn<{ tableName: ${quote(table.table)} }>`;\n return `<typeof ${relation.targetVar}, [${relation.fields.map(() => column).join(\", \")}]>`;\n};\n","/**\n * `schema.generated.ts`, the Drizzle half of `rebase db generate`.\n *\n * There is no interpretation left in this file. `schema/plan/plan-schema.ts`\n * reads the collections into a {@link SchemaPlan} and\n * `schema/plan/render-drizzle.ts` writes the TypeScript; this is the entry\n * point they are reached through.\n *\n * It used to hold `getDrizzleColumn` — one of three `switch (prop.type)`\n * statements, each with its own reading of relations, enums, search and\n * `validation.unique`, and each disagreeing with the other two about something.\n * See `schema/plan/types.ts`.\n */\nimport type { CollectionConfig } from \"@rebasepro/types\";\nimport { relationalCollections, sortCollectionsBySlug } from \"@rebasepro/common\";\nimport { planSchema } from \"./plan/plan-schema\";\nimport { renderDrizzleSchema, type DrizzleRenderOptions } from \"./plan/render-drizzle\";\n\n/**\n * The generated Drizzle schema for a set of collections.\n *\n * Synchronous, and takes an options object. It was `async` with nothing to\n * await and a positional `stripPolicies` boolean — `generateSchema(c, true)`,\n * which reads as neither \"with policies\" nor \"without\".\n *\n * Sorted by slug here rather than in the writer script: the output is\n * order-dependent and `rebase doctor` regenerates it in memory to compare\n * against the file on disk, so with the sort in the writer only, a project\n * whose file order differed from its slug order was reported stale forever.\n */\nexport const generateSchema = (\n allCollections: CollectionConfig[],\n options: DrizzleRenderOptions = {}\n): string => {\n // A Firestore or MongoDB collection has no table to generate, and\n // generating one for it is not merely wasted output: `db push` would create\n // it, and `rebase doctor` would then report the store the collection\n // actually reads from as drift.\n const collections = sortCollectionsBySlug(relationalCollections(allCollections));\n return renderDrizzleSchema(planSchema(collections), options);\n};\n"],"mappings":";;;;;;;AAiCA,IAAM,gBAAgB;;;;;;;;;AAUtB,IAAM,SAAS,UAA0B,KAAK,UAAU,KAAK;;AAG7D,IAAM,WAAW,SAA0B,cAAc,KAAK,IAAI,IAAI,OAAO,MAAM,IAAI;;;;;;;AAQvF,IAAM,UAAU,QAAgB,QAC3B,cAAc,KAAK,GAAG,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,EAAE;;AAS5E,IAAM,SAAS,MAAmB,YAA4B;CAC1D,KAAK,IAAI,OAAO;CAChB,OAAO;AACX;;;;;;;;;;;;;;;;AAiBA,IAAM,WAAW,WACb,QAAQ,OAAO,QAAQ,OAAO,MAAM,CAAC,CAAC,QAAQ,MAAM,KAAK,CAAC,CAAC,QAAQ,SAAS,MAAM,EAAE;;AAGxF,IAAM,cAAc,MAAc,QAAgB,SAA8B;CAC5E,MAAM,OAAO,MAAM,MAAM;CACzB,QAAQ,KAAK,MAAb;EACI,KAAK,QAAQ,OAAO,GAAG,MAAM,MAAM,MAAM,EAAE,GAAG,KAAK;EACnD,KAAK,WAAW,OAAO,GAAG,MAAM,MAAM,SAAS,EAAE,GAAG,KAAK,cAAc,KAAK,OAAO;EACnF,KAAK,QAAQ,OAAO,GAAG,MAAM,MAAM,MAAM,EAAE,GAAG,KAAK,cAAc,KAAK,OAAO;EAC7E,KAAK,QAAQ,OAAO,GAAG,MAAM,MAAM,MAAM,EAAE,GAAG,KAAK;EAGnD,KAAK,QAAQ,OAAO,GAAG,KAAK,QAAQ,GAAG,KAAK;EAC5C,KAAK,YAAY,OAAO,GAAG,MAAM,MAAM,UAAU,EAAE,GAAG,KAAK;EAC3D,KAAK,WAAW,OAAO,GAAG,MAAM,MAAM,SAAS,EAAE,GAAG,KAAK;EAWzD,KAAK,UAAU,OAAO,GAAG,MAAM,MAAM,QAAQ,EAAE,GAAG,KAAK;EACvD,KAAK,aAAa,OAAO,GAAG,MAAM,MAAM,WAAW,EAAE,GAAG,KAAK;EAC7D,KAAK,eAAe,OAAO,GAAG,MAAM,MAAM,aAAa,EAAE,GAAG,KAAK;EACjE,KAAK,UAAU,OAAO,GAAG,MAAM,MAAM,QAAQ,EAAE,GAAG,KAAK;EACvD,KAAK,QAAQ,OAAO,GAAG,MAAM,MAAM,MAAM,EAAE,GAAG,KAAK;EACnD,KAAK,mBAAmB,OAAO,GAAG,MAAM,MAAM,iBAAiB,EAAE,GAAG,KAAK;EACzE,KAAK;GACD,IAAI,KAAK,cAAc,KAAA,GAAW,OAAO,GAAG,MAAM,MAAM,SAAS,EAAE,GAAG,KAAK;GAC3E,OAAO,KAAK,UAAU,KAAA,IAChB,GAAG,MAAM,MAAM,SAAS,EAAE,GAAG,KAAK,iBAAiB,KAAK,UAAU,OAClE,GAAG,MAAM,MAAM,SAAS,EAAE,GAAG,KAAK,iBAAiB,KAAK,UAAU,WAAW,KAAK,MAAM;EAClG,KAAK,WAAW,OAAO,GAAG,MAAM,MAAM,SAAS,EAAE,GAAG,KAAK;EACzD,KAAK,eAAe,OAAO,GAAG,MAAM,MAAM,WAAW,EAAE,GAAG,KAAK;EAC/D,KAAK,QAAQ,OAAO,GAAG,MAAM,MAAM,MAAM,EAAE,GAAG,KAAK;EACnD,KAAK,QAAQ,OAAO,GAAG,MAAM,MAAM,MAAM,EAAE,GAAG,KAAK;EACnD,KAAK,QAAQ,OAAO,GAAG,MAAM,MAAM,MAAM,EAAE,GAAG,KAAK;EACnD,KAAK,SAAS,OAAO,GAAG,MAAM,MAAM,OAAO,EAAE,GAAG,KAAK;EACrD,KAAK,UAAU,OAAO,GAAG,MAAM,MAAM,QAAQ,EAAE,GAAG,KAAK,kBAAkB,KAAK,WAAW;EACzF,KAAK,SAAS,OAAO,GAAG,MAAM,MAAM,YAAY,EAAE,uCAAuC,KAAK;EAC9F,KAAK,YAAY,OAAO,GAAG,MAAM,MAAM,YAAY,EAAE,0CAA0C,KAAK;EAEpG,KAAK,SAAS,OAAO,GAAG,WAAW,KAAK,IAAI,QAAQ,IAAI,EAAE;CAC9D;AACJ;;;;;;;;;;;;;AAcA,IAAM,oBACF,IACA,WACA,WACA,SACS;CACT,MAAM,MAAM,kBAAkB;CAC9B,MAAM,QAAQ,CACV,GAAG,WAAW,cAAc,GAAG,SAAS,YAAY,EAAE,KAAK,IAC3D,cAAc,GAAG,SAAS,YAAY,EAAE,EAC5C,CAAC,CAAC,OAAO,OAAO;CAChB,OAAO,kCAAkC,OAAO,WAAW,SAAS,EAAE,MAAM,MAAM,KAAK,IAAI,EAAE;AACjG;;AAGA,IAAM,gBAAgB,QAAoB,OAAkB,MAAkB,SAA8B;CACxG,IAAI,MAAM,WAAW,OAAO,MAAM,OAAO,QAAQ,IAAI;CAErD,IAAI,OAAO,WACP,OAAO,GAAG,IAAI,qBAAqB,QAAQ,OAAO,UAAU,UAAU,EAAE;CAM5E,MAAM,cAAc,OAAO,OAAO,SAAS;CAC3C,IAAI,aAAa,OAAO;CAExB,IAAI,OAAO,SAAS,SAAS,YAAY,OAAO;CAChD,IAAI,OAAO,YAAY,OAAO;CAC9B,IAAI,OAAO,WAAW,OAAO,QAAQ,SAAS,YAAY;EACtD,MAAM,aAAa,OAAO,QAAQ,SAAS,QAAQ,OAAO,QAAQ,aAAa,OAAO,QAAQ;EAM9F,OAAO,OAAO,KAAK,SAAS,UAAU,eAAe,sBAC/C,qBACA,YAAY,QAAQ,UAAU,EAAE;CAC1C;CAMA,MAAM,aAAa,OAAO,cACnB,MAAM,QAAQ,MAAK,MAAK,EAAE,yBAAyB,EAAE,WAAW,OAAO,MAAM,CAAC,EAAE;CACvF,IAAI,YAAY;EACZ,MAAM,SAAS,KAAK,OAAO,MAAK,MAC5B,EAAE,WAAW,WAAW,gBAAgB,EAAE,UAAU,WAAW,WAAW;EAC9E,IAAI,QAAQ;GACR,MAAM,eAAe,OAAO,QAAQ,MAAK,MAAK,EAAE,WAAW,WAAW,YAAY;GAClF,OAAO,iBAAiB,YAAY,OAAO,SAAS,cAAc,OAAO,WAAW,cAAc,IAAI;EAC1G;CACJ;CACA,IAAI,OAAO,QAAQ,OAAO;CAC1B,IAAI,CAAC,OAAO,YAAY,CAAC,OAAO,cAAc,CAAC,aAAa,OAAO;CACnE,OAAO;AACX;;AAGA,IAAM,eAAe,OAAkB,SACnC,MAAM,QAAQ,KAAI,SAAQ;CACtB,MAAM,UAAU,KAAK,SAAS,MAAM,MAAM,aAAa,IAAI,MAAM,MAAM,OAAO;CAG9E,MAAM,UAAU,SACZ,OAAO,SAAS,MAAM,QAAQ,MAAK,MAAK,EAAE,WAAW,IAAI,CAAC,EAAE,OAAO,IAAI;CAC3E,MAAM,OAAO,KAAK,KAAK,KAAI,QAAO;EAC9B,IAAI,KAAK,WAAW,SAAS,OAAO,OAAO,IAAI,MAAM;EACrD,MAAM,YAAY,IAAI,cAAc,SAAS,YAAY;EACzD,MAAM,QAAQ,IAAI,UAAU,UAAU,kBAAkB;EACxD,OAAO,GAAG,OAAO,IAAI,MAAM,IAAI,YAAY;CAC/C,CAAC;CACD,MAAM,KAAK,KAAK,WAAW,UACrB,OAAO,KAAK,KAAK,IAAI,EAAE,KACvB,UAAU,MAAM,KAAK,MAAM,EAAE,IAAI,KAAK,KAAK,IAAI,EAAE;CACvD,MAAM,QAAQ,KAAK,YAAY,eAAe,gBAAgB,KAAK,SAAS,EAAE,OAAO;CAIrF,MAAM,WAAW,KAAK,QAAQ,SAAS,IACjC,gBAAgB,KAAK,QAAQ,KAAK,IAAI,EAAE,kDACxC;CACN,OAAO,OAAO,QAAQ,GAAG,MAAM,KAAK,SAAS,EAAE,GAAG,KAAK,MAAM,GAAG;AACpE,CAAC;AAEL,IAAM,eAAe,QAAoB,SAA8B;CACnE,MAAM,MAAM,UAAU;CACtB,MAAM,QAAQ;EACV,QAAQ,OAAO,KAAK;EACpB,SAAS,OAAO,UAAU;EAC1B,QAAQ,OAAO,MAAM,KAAI,MAAK,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE;CACvD;CACA,IAAI,OAAO,OAAO,MAAM,KAAK,UAAU,QAAQ,OAAO,KAAK,GAAG;CAC9D,IAAI,OAAO,WAAW,MAAM,KAAK,cAAc,QAAQ,OAAO,SAAS,GAAG;CAC1E,OAAO,gBAAgB,MAAM,OAAO,IAAI,EAAE,MAAM,MAAM,KAAK,IAAI,EAAE;AACrE;AAEA,SAAgB,oBAAoB,MAAkB,UAAgC,CAAC,GAAW;CAC9F,MAAM,eAAe,QAAQ,aAAa;CAC1C,MAAM,uBAAoB,IAAI,IAAI;CAGlC,IAAI,OAAO;CAEX,IAAI,qBAAqB;CACzB,IAAI,KAAK,gBAAgB,SAAS,GAAG;EACjC,MAAM,MAAM,UAAU;EACtB,KAAK,gBAAgB,SAAQ,WAAU;GACnC,sBAAsB,gBAAgB,OAAO,qBAAqB,OAAO;EAC7E,CAAC;EACD,sBAAsB;CAC1B;CAEA,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,YAAY,KAAK,OAAO;EAK/B,MAAM,SAAS,SAAS,OAAO,IAAI,KAAK,CAAC,CAAC,KAAK,IAAI;EACnD,MAAM,cAAc,SAAS,iBACvB,GAAG,SAAS,eAAe,cAAc,MAAM,SAAS,IAAI,EAAE,KAAK,OAAO,MAC1E,GAAG,MAAM,MAAM,QAAQ,EAAE,GAAG,MAAM,SAAS,IAAI,EAAE,KAAK,OAAO;EACnE,QAAQ,gBAAgB,SAAS,QAAQ,KAAK,YAAY;EAC1D,IAAI,CAAC,SAAS,SAAS,SAAS,OAAO,GAAG,SAAS,KAAK,SAAS,OAAO;CAC5E;CACA,QAAQ;CAER,MAAM,YAAsB,CAAC;CAC7B,KAAK,MAAM,SAAS,KAAK,QAAQ;EAC7B,MAAM,UAAU,MAAM,iBAChB,GAAG,MAAM,eAAe,gBACxB,MAAM,MAAM,SAAS;EAC3B,QAAQ,gBAAgB,MAAM,QAAQ,KAAK,QAAQ,IAAI,MAAM,MAAM;EAmBnE,MAAM,QAbU,CACZ,GAAG,MAAM,QAAQ,QAAO,MAAK,EAAE,OAAO,SAAS,aAAa,GAC5D,GAAG,MAAM,QAAQ,QAAO,MAAK,EAAE,OAAO,SAAS,aAAa,CAChE,CAAC,CAAC,QAAO,MACL,CAAC,EAAE,yBAOA,EAAE,OAAO,SAAS,MAEX,CAAA,CAAQ,KAAI,WAAU,OAAO,QAAQ,OAAO,GAAG,EAAE,IAAI,aAAa,QAAQ,OAAO,MAAM,IAAI,GAAG;EAG5G,QAAQ,MAAM,SAAS,aACjB,GAAG,MAAM,KAAK,KAAK,EAAE,OACrB,GAAG,MAAM,KAAK,KAAK,EAAE;EAE3B,MAAM,SAAmB,CAAC;EAK1B,IAAI,MAAM,WAAW,SAAS,GAAG;GAC7B,MAAM,aAAa,SACf,OAAO,SAAS,MAAM,QAAQ,MAAK,MAAK,EAAE,WAAW,QAAQ,CAAC,EAAE,qBAAqB,CAAC,EAAE,OAAO,IAAI;GACvG,OAAO,KAAK,OAAO,MAAM,MAAM,YAAY,EAAE,eAAe,MAAM,WAAW,IAAI,SAAS,CAAC,CAAC,KAAK,IAAI,EAAE,MAAM;EACjH;EACA,IAAI,MAAM,SAAS,cACf,OAAO,KAAK,GAAG,YAAY,OAAO,IAAI,CAAC;EAE3C,IAAI,cACA,OAAO,KAAK,GAAG,MAAM,SAAS,KAAI,WAAU,YAAY,QAAQ,IAAI,CAAC,CAAC;EAG1E,IAAI,OAAO,SAAS,GAAG;GACnB,QAAQ;GACR,QAAQ,GAAG,OAAO,KAAK,IAAI,EAAE;GAC7B,QAAQ;EACZ,OAII,QAAQ;EAEZ,IAAI,CAAC,UAAU,SAAS,MAAM,OAAO,GAAG,UAAU,KAAK,MAAM,OAAO;CACxE;CAEA,MAAM,eAAyB,CAAC;CAChC,KAAK,MAAM,SAAS,KAAK,QAAQ;EAC7B,MAAM,UAAU,KAAK,UAAU,QAAO,MAAK,EAAE,aAAa,MAAM,OAAO;EACvE,IAAI,QAAQ,WAAW,GAAG;EAC1B,MAAM,WAAW,QAAQ,KAAI,aAAY,eAAe,OAAO,UAAU,IAAI,CAAC;EAC9E,MAAM,UAAU,GAAG,MAAM,QAAQ;EACjC,QAAQ,gBAAgB,QAAQ,sBAAsB,MAAM,QAAQ,2BAA2B,SAAS,KAAK,KAAK,EAAE;EACpH,IAAI,CAAC,aAAa,SAAS,OAAO,GAAG,aAAa,KAAK,OAAO;CAClE;CAGA,MAAM,UAAU,MAAM,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG,MACtC,EAAE,QAAQ,UAAU,EAAE,CAAC,CAAC,cAAc,EAAE,QAAQ,UAAU,EAAE,CAAC,CAAC;CAElE,IAAI,MAAM;CACV,OAAO,YAAY,QAAQ,KAAK,IAAI,EAAE;CACtC,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO,2BAA2B,UAAU,KAAK,IAAI,EAAE;CACvD,OAAO,0BAA0B,SAAS,KAAK,IAAI,EAAE;CACrD,OAAO,8BAA8B,aAAa,KAAK,IAAI,EAAE;CAC7D,OAAO;AACX;AAEA,IAAM,kBAAkB,OAAkB,UAAwB,SAA8B;CAC5F,IAAI,SAAS,SAAS,QAClB,OAAO,OAAO,MAAM,SAAS,GAAG,EAAE,SAAS,SAAS,UAAU,oBAAoB,MAAM,SAAS,YAAY,EAAE;CAEnH,OAAO,OAAO,MAAM,SAAS,GAAG,EAAE,OAAO,iBAAiB,OAAO,UAAU,IAAI,EAAE,GAAG,SAAS,UAAU,wBAC/E,SAAS,OAAO,KAAI,MAAK,OAAO,MAAM,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,2BAC1D,SAAS,WAAW,KAAI,MAAK,OAAO,SAAS,WAAW,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,4BACtE,MAAM,SAAS,YAAY,EAAE;AAE9D;;;;;;;;;;;AAYA,IAAM,oBAAoB,OAAkB,UAA2B,SAA8B;CACjG,IAAI,CAAC,SAAS,UAAU,OAAO;CAC/B,MAAM,MAAM,kBAAkB;CAC9B,MAAM,SAAS,4BAA4B,MAAM,MAAM,KAAK,EAAE;CAC9D,OAAO,WAAW,SAAS,UAAU,KAAK,SAAS,OAAO,UAAU,MAAM,CAAC,CAAC,KAAK,IAAI,EAAE;AAC3F;;;;;;;;;;;;;;;;ACxWA,IAAa,kBACT,gBACA,UAAgC,CAAC,MACxB;CAMT,OAAO,oBAAoB,WADP,sBAAsB,sBAAsB,cAAc,CACxC,CAAW,GAAG,OAAO;AAC/D"}
@@ -0,0 +1,152 @@
1
+ import { createRequire as __createRequire } from "module";
2
+ __createRequire(import.meta.url);
3
+ import { r as __exportAll } from "./__vite-browser-external-BnuHet1e.js";
4
+ import { n as planSchema } from "./plan-schema-DU9exq6C.js";
5
+ import { a as renderPolicyStatements, c as renderSearchDdl, d as searchExcludePatternsOf, f as triggerExcludePatternsOf, i as renderPoliciesDdl, l as renderTriggersDdl, o as renderPostgresDdl, p as vectorExcludePatternsOf, r as renderPgType, u as renderVectorDdl } from "./render-ddl-Ds2t_d9V.js";
6
+ import "@rebasepro/types";
7
+ import "@rebasepro/common";
8
+ //#region src/schema/generate-postgres-ddl-logic.ts
9
+ var generate_postgres_ddl_logic_exports = /* @__PURE__ */ __exportAll({
10
+ generatePostgresDdl: () => generatePostgresDdl,
11
+ generatePostgresPoliciesDdl: () => generatePostgresPoliciesDdl,
12
+ generatePostgresSearchDdl: () => generatePostgresSearchDdl,
13
+ generatePostgresTriggersDdl: () => generatePostgresTriggersDdl,
14
+ generatePostgresVectorDdl: () => generatePostgresVectorDdl,
15
+ planCollectionPolicies: () => planCollectionPolicies,
16
+ planJunctionTables: () => planJunctionTables,
17
+ searchColumnDependencies: () => searchColumnDependencies,
18
+ searchExcludePatterns: () => searchExcludePatterns,
19
+ triggerExcludePatterns: () => triggerExcludePatterns,
20
+ vectorExcludePatterns: () => vectorExcludePatterns
21
+ });
22
+ /**
23
+ * `.rebase/sql/schema.sql` — the desired state Atlas diffs the database against.
24
+ *
25
+ * Synchronous. It was `async` with nothing to await, which made every caller
26
+ * (the writer script, the doctor, the live editor, two gates) carry a promise
27
+ * for no reason and made the one place that forgot to await it silently compare
28
+ * `[object Promise]`.
29
+ */
30
+ var generatePostgresDdl = (allCollections, options = {}) => renderPostgresDdl(planSchema(allCollections), options);
31
+ /** `.rebase/sql/policies.sql` — the RLS policies. Atlas has no model for a policy. */
32
+ var generatePostgresPoliciesDdl = (allCollections) => renderPoliciesDdl(planSchema(allCollections));
33
+ /**
34
+ * `.rebase/sql/search.sql` — everything a `search` block needs, as a file Rebase
35
+ * applies itself.
36
+ *
37
+ * Search is one of the parts of the schema Atlas does not own, for two
38
+ * independent reasons and either alone would be enough:
39
+ *
40
+ * 1. Its free tier refuses to *parse* a desired-state file that so much as
41
+ * contains a function — "functions and procedures are available to
42
+ * logged-in users only". A generated `tsvector` column cannot avoid one:
43
+ * `unaccent` is STABLE and jsonb flattening needs a set-returning function,
44
+ * so both have to be wrapped in an IMMUTABLE helper to be legal in a
45
+ * generated column at all.
46
+ * 2. Even with the file accepted, Atlas *wipes* the dev database it diffs
47
+ * against, so a helper seeded there beforehand is gone by the time the plan
48
+ * is analysed. There is no hook to reinstate it.
49
+ *
50
+ * Empty when nothing opted in — the caller writes no file then.
51
+ */
52
+ var generatePostgresSearchDdl = (allCollections) => renderSearchDdl(planSchema(allCollections));
53
+ /**
54
+ * Glob patterns telling Atlas to leave the search column and its index alone.
55
+ *
56
+ * Without these, a desired state that omits search reads to Atlas as an
57
+ * instruction to drop the column — taking the index and the whole search
58
+ * feature with it on the next push.
59
+ */
60
+ var searchExcludePatterns = (allCollections) => searchExcludePatternsOf(planSchema(allCollections));
61
+ /**
62
+ * `.rebase/sql/vector.sql` — the `vector` columns, their ANN indexes and, when a
63
+ * database declared it, the extension.
64
+ *
65
+ * Vector is out of Atlas's hands for a narrower reason than search: not that
66
+ * Atlas refuses to parse the file, but that it cannot *execute* it. To compute
67
+ * a desired state Atlas materialises `schema.sql` in a dev database, that
68
+ * database is created empty, and Atlas empties it again at the start of every
69
+ * run — so `VECTOR(384)` resolves against a database that structurally cannot
70
+ * have pgvector, and every push dies with `type "vector" does not exist`.
71
+ */
72
+ var generatePostgresVectorDdl = (allCollections, options = {}) => renderVectorDdl(planSchema(allCollections, { databaseExtensions: options.extensions }));
73
+ /** @see searchExcludePatterns */
74
+ var vectorExcludePatterns = (allCollections) => vectorExcludePatternsOf(planSchema(allCollections));
75
+ /**
76
+ * `.rebase/sql/triggers.sql` — the `BEFORE UPDATE` triggers behind
77
+ * `autoValue: "on_update"`, and the one function they share.
78
+ *
79
+ * A trigger is a function plus a binding, so it is carved out of Atlas's view
80
+ * for the first of the two reasons search is. Empty when no property declares
81
+ * `on_update` — the caller writes no file then, and removes a stale one.
82
+ */
83
+ var generatePostgresTriggersDdl = (allCollections) => renderTriggersDdl(planSchema(allCollections));
84
+ /** @see searchExcludePatterns */
85
+ var triggerExcludePatterns = (allCollections) => triggerExcludePatternsOf(planSchema(allCollections));
86
+ var planOf = (collections) => planSchema(collections);
87
+ /**
88
+ * The junction tables a bundle's many-to-many relations imply.
89
+ *
90
+ * Derived from the same plan the derived RLS comes from, so a table created
91
+ * here always has policies planned for it — a junction with row-level security
92
+ * left off is readable and writable by every signed-in user.
93
+ */
94
+ var planJunctionTables = (allCollections) => planOf(allCollections).tables.filter((table) => table.kind === "junction").map((table) => ({
95
+ schema: table.schema,
96
+ table: table.table,
97
+ columns: table.columns.map((c) => ({
98
+ name: c.column,
99
+ type: renderPgType(c.type),
100
+ legacyName: c.legacyColumn
101
+ })),
102
+ createTable: `CREATE TABLE IF NOT EXISTS "${table.schema}"."${table.table}" (` + table.columns.map((c) => `"${c.column}" ${renderPgType(c.type)} NOT NULL`).join(", ") + `, PRIMARY KEY (${table.primaryKey.map((c) => `"${c}"`).join(", ")}));`,
103
+ foreignKeys: table.columns.map((c) => c.foreignKey).filter((fk) => fk !== void 0)
104
+ }));
105
+ /**
106
+ * The per-table RLS plan for the declared collections and their junctions, as
107
+ * executable statements — what the managed runtime applies at boot so a freshly
108
+ * provisioned tenant database serves data instead of 401ing every read.
109
+ *
110
+ * The same policies `policies.sql` carries, from the same plan, so boot and
111
+ * `db push` produce identical RLS from identical collections.
112
+ */
113
+ var planCollectionPolicies = (allCollections) => {
114
+ const plan = planOf(allCollections);
115
+ return [...plan.tables.filter((t) => t.kind === "collection"), ...plan.tables.filter((t) => t.kind === "junction")].map((table) => ({
116
+ schema: table.schema,
117
+ table: table.table,
118
+ qualified: table.qualified,
119
+ enableRls: `ALTER TABLE "${table.schema}"."${table.table}" ENABLE ROW LEVEL SECURITY;`,
120
+ policyStatements: table.policies.flatMap((policy) => renderPolicyStatements(table, policy))
121
+ }));
122
+ };
123
+ /**
124
+ * The `generated column → column it reads` edges a project's `search` blocks
125
+ * imply, derived from the collections alone.
126
+ *
127
+ * The same edges `queryGeneratedColumnDependencies` reads out of `pg_depend`,
128
+ * but available where no database is: `rebase db generate` writes a migration
129
+ * that will run somewhere else, later, against a database it cannot inspect.
130
+ * A spec's `fields[].column` is the physical column each search field starts
131
+ * at, which is exactly what the generated expression depends on.
132
+ */
133
+ var searchColumnDependencies = (allCollections) => {
134
+ const dependencies = [];
135
+ for (const table of planOf(allCollections).tables) {
136
+ const spec = table.search;
137
+ if (!spec) continue;
138
+ const generated = [spec.column, ...spec.fuzzy ? [spec.fuzzy.column] : []];
139
+ const sources = new Set(spec.fields.map((field) => field.column));
140
+ for (const column of generated) for (const dependsOn of sources) dependencies.push({
141
+ schema: table.schema,
142
+ table: table.table,
143
+ column,
144
+ dependsOn
145
+ });
146
+ }
147
+ return dependencies;
148
+ };
149
+ //#endregion
150
+ export { generatePostgresVectorDdl as a, planJunctionTables as c, generatePostgresTriggersDdl as i, generatePostgresPoliciesDdl as n, generate_postgres_ddl_logic_exports as o, generatePostgresSearchDdl as r, planCollectionPolicies as s, generatePostgresDdl as t };
151
+
152
+ //# sourceMappingURL=generate-postgres-ddl-logic-BJsLaVNX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-postgres-ddl-logic-BJsLaVNX.js","names":[],"sources":["../src/schema/generate-postgres-ddl-logic.ts"],"sourcesContent":["/**\n * The SQL half of `rebase db generate`, as a plan and a renderer.\n *\n * There is no interpretation left in this file. `schema/plan/plan-schema.ts`\n * reads the collections into a {@link SchemaPlan} and `schema/plan/render-ddl.ts`\n * writes the files; what remains here is the public entry points those two are\n * reached through, plus the smaller plans (`planRelationalColumns`,\n * `planJunctionTables`, `planCollectionPolicies`) that other modules ask for as\n * slices of the same plan.\n *\n * It used to hold `getSqlColumnType` and a `CREATE TABLE` walk with its own\n * reading of relations, junctions, enums and search — one of three such\n * readings. See `schema/plan/types.ts` for what that cost.\n */\nimport { CollectionConfig, SecurityRule, isPostgresCollectionConfig } from \"@rebasepro/types\";\nimport { getTableName } from \"@rebasepro/common\";\nimport { planSchema, compileSecurityRule, quoteSqlLiteral, defaultBelongsToOnDelete } from \"./plan/plan-schema\";\nimport {\n renderPgType,\n renderPoliciesDdl,\n renderPolicyStatements,\n renderPostgresDdl,\n renderSearchDdl,\n renderTriggersDdl,\n renderVectorDdl,\n searchExcludePatternsOf,\n triggerExcludePatternsOf,\n vectorExcludePatternsOf,\n type DdlRenderOptions\n} from \"./plan/render-ddl\";\nimport type { ForeignKeyPlan, SchemaPlan } from \"./plan/types\";\nimport type { GeneratedColumnDependency } from \"./generated-column-conflicts\";\n\nexport { quoteSqlLiteral, defaultBelongsToOnDelete };\nexport type { ForeignKeyPlan };\n\ntype ResolveCollection = (slug: string) => CollectionConfig | undefined;\n\n/**\n * The individual SQL statements a single security rule compiles to: a\n * `DROP POLICY IF EXISTS` / `CREATE POLICY` pair per operation, each a complete\n * statement (terminated by `;`, no trailing newline).\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 generatePolicyStatements = (\n collection: CollectionConfig,\n rule: SecurityRule,\n resolveCollection: ResolveCollection\n): string[] => {\n const table = {\n schema: isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\",\n table: getTableName(collection)\n };\n return compileSecurityRule(collection, rule, resolveCollection, false)\n .flatMap(policy => renderPolicyStatements(table, policy));\n};\n\n/**\n * `.rebase/sql/schema.sql` — the desired state Atlas diffs the database against.\n *\n * Synchronous. It was `async` with nothing to await, which made every caller\n * (the writer script, the doctor, the live editor, two gates) carry a promise\n * for no reason and made the one place that forgot to await it silently compare\n * `[object Promise]`.\n */\nexport const generatePostgresDdl = (\n allCollections: CollectionConfig[],\n options: DdlRenderOptions = {}\n): string => renderPostgresDdl(planSchema(allCollections), options);\n\n/** `.rebase/sql/policies.sql` — the RLS policies. Atlas has no model for a policy. */\nexport const generatePostgresPoliciesDdl = (allCollections: CollectionConfig[]): string =>\n renderPoliciesDdl(planSchema(allCollections));\n\n/**\n * `.rebase/sql/search.sql` — everything a `search` block needs, as a file Rebase\n * applies itself.\n *\n * Search is one of the parts of the schema Atlas does not own, for two\n * independent reasons and either alone would be enough:\n *\n * 1. Its free tier refuses to *parse* a desired-state file that so much as\n * contains a function — \"functions and procedures are available to\n * logged-in users only\". A generated `tsvector` column cannot avoid one:\n * `unaccent` is STABLE and jsonb flattening needs a set-returning function,\n * so both have to be wrapped in an IMMUTABLE helper to be legal in a\n * generated column at all.\n * 2. Even with the file accepted, Atlas *wipes* the dev database it diffs\n * against, so a helper seeded there beforehand is gone by the time the plan\n * is analysed. There is no hook to reinstate it.\n *\n * Empty when nothing opted in — the caller writes no file then.\n */\nexport const generatePostgresSearchDdl = (allCollections: CollectionConfig[]): string =>\n renderSearchDdl(planSchema(allCollections));\n\n/**\n * Glob patterns telling Atlas to leave the search column and its index alone.\n *\n * Without these, a desired state that omits search reads to Atlas as an\n * instruction to drop the column — taking the index and the whole search\n * feature with it on the next push.\n */\nexport const searchExcludePatterns = (allCollections: CollectionConfig[]): string[] =>\n searchExcludePatternsOf(planSchema(allCollections));\n\n/**\n * `.rebase/sql/vector.sql` — the `vector` columns, their ANN indexes and, when a\n * database declared it, the extension.\n *\n * Vector is out of Atlas's hands for a narrower reason than search: not that\n * Atlas refuses to parse the file, but that it cannot *execute* it. To compute\n * a desired state Atlas materialises `schema.sql` in a dev database, that\n * database is created empty, and Atlas empties it again at the start of every\n * run — so `VECTOR(384)` resolves against a database that structurally cannot\n * have pgvector, and every push dies with `type \"vector\" does not exist`.\n */\nexport const generatePostgresVectorDdl = (\n allCollections: CollectionConfig[],\n options: { extensions?: readonly string[] } = {}\n): string => renderVectorDdl(planSchema(allCollections, { databaseExtensions: options.extensions }));\n\n/** @see searchExcludePatterns */\nexport const vectorExcludePatterns = (allCollections: CollectionConfig[]): string[] =>\n vectorExcludePatternsOf(planSchema(allCollections));\n\n/**\n * `.rebase/sql/triggers.sql` — the `BEFORE UPDATE` triggers behind\n * `autoValue: \"on_update\"`, and the one function they share.\n *\n * A trigger is a function plus a binding, so it is carved out of Atlas's view\n * for the first of the two reasons search is. Empty when no property declares\n * `on_update` — the caller writes no file then, and removes a stale one.\n */\nexport const generatePostgresTriggersDdl = (allCollections: CollectionConfig[]): string =>\n renderTriggersDdl(planSchema(allCollections));\n\n/** @see searchExcludePatterns */\nexport const triggerExcludePatterns = (allCollections: CollectionConfig[]): string[] =>\n triggerExcludePatternsOf(planSchema(allCollections));\n\n// ── Slices of the plan other modules ask for ─────────────────────────────────\n\n/** A column a `relation` or `reference` property owns on its own table. */\nexport interface RelationalColumnPlan {\n schema: string;\n /** Bare table name, no schema prefix. */\n table: string;\n column: string;\n /** Postgres type, exactly as the DDL renderer declares it. */\n type: string;\n /** `validation.required` on the property that owns the link. */\n required: boolean;\n /**\n * True when a declared property emits this column and the relation\n * contributes only the constraint — `postId` with `columnName: \"post_id\"`\n * beside a `belongsTo` on `post_id`.\n */\n columnOwnedByProperty?: boolean;\n /** Absent when the target collection is not part of this bundle. */\n foreignKey?: ForeignKeyPlan;\n /**\n * What this column would have been called before `generateForeignKeyName`\n * learned to singularize. Carried so the boot-time ensure can notice a\n * database provisioned under the old rule; never used to name anything.\n */\n legacyColumn?: string;\n}\n\n/** The table behind a many-to-many `through` relation. */\nexport interface JunctionTablePlan {\n schema: string;\n /** Bare table name, no schema prefix. */\n table: string;\n columns: { name: string; type: string; legacyName?: string }[];\n /** Both endpoint columns plus the composite primary key. */\n createTable: string;\n foreignKeys: ForeignKeyPlan[];\n}\n\nconst planOf = (collections: CollectionConfig[]): SchemaPlan => planSchema(collections);\n\n/**\n * The FK columns the declared collections own — one entry per `relation`\n * (`belongsTo` side) or `reference` property.\n *\n * A view over the schema plan. It was a second walk of the collections beside\n * the `CREATE TABLE` one, which is how boot-ensure came to add every relation\n * column bare while `db push` made the same column `NOT NULL`.\n */\nexport const planRelationalColumns = (allCollections: CollectionConfig[]): RelationalColumnPlan[] => {\n const plan = planOf(allCollections);\n const out: RelationalColumnPlan[] = [];\n for (const table of plan.tables) {\n if (table.kind !== \"collection\") continue;\n for (const column of table.columns) {\n if (column.source.kind !== \"relation\" && column.source.kind !== \"reference\") continue;\n out.push({\n schema: table.schema,\n table: table.table,\n column: column.column,\n type: renderPgType(column.type),\n required: !column.nullable,\n columnOwnedByProperty: column.columnOwnedByProperty,\n legacyColumn: column.legacyColumn,\n foreignKey: column.foreignKey\n });\n }\n }\n return out;\n};\n\n/**\n * The junction tables a bundle's many-to-many relations imply.\n *\n * Derived from the same plan the derived RLS comes from, so a table created\n * here always has policies planned for it — a junction with row-level security\n * left off is readable and writable by every signed-in user.\n */\nexport const planJunctionTables = (allCollections: CollectionConfig[]): JunctionTablePlan[] =>\n planOf(allCollections).tables\n .filter(table => table.kind === \"junction\")\n .map(table => ({\n schema: table.schema,\n table: table.table,\n columns: table.columns.map(c => ({\n name: c.column,\n type: renderPgType(c.type),\n legacyName: c.legacyColumn\n })),\n createTable:\n `CREATE TABLE IF NOT EXISTS \"${table.schema}\".\"${table.table}\" (` +\n table.columns.map(c => `\"${c.column}\" ${renderPgType(c.type)} NOT NULL`).join(\", \") +\n `, PRIMARY KEY (${table.primaryKey.map(c => `\"${c}\"`).join(\", \")}));`,\n foreignKeys: table.columns.map(c => c.foreignKey).filter((fk): fk is ForeignKeyPlan => fk !== undefined)\n }));\n\nexport interface CollectionPolicyPlan {\n /** The table's schema (e.g. `public`, `rebase`). */\n schema: string;\n /** The bare table name, no schema prefix. */\n table: string;\n /** `schema.table` — matches the keys `readExistingSchema` returns. */\n qualified: string;\n /** `ALTER TABLE … ENABLE ROW LEVEL SECURITY;` — locked by default. */\n enableRls: string;\n /** `DROP POLICY IF EXISTS` / `CREATE POLICY` statements, in order. */\n policyStatements: string[];\n}\n\n/**\n * The per-table RLS plan for the declared collections and their junctions, as\n * executable statements — what the managed runtime applies at boot so a freshly\n * provisioned tenant database serves data instead of 401ing every read.\n *\n * The same policies `policies.sql` carries, from the same plan, so boot and\n * `db push` produce identical RLS from identical collections.\n */\nexport const planCollectionPolicies = (allCollections: CollectionConfig[]): CollectionPolicyPlan[] => {\n const plan = planOf(allCollections);\n // Collections first, then the junctions no collection declares.\n const ordered = [\n ...plan.tables.filter(t => t.kind === \"collection\"),\n ...plan.tables.filter(t => t.kind === \"junction\")\n ];\n return ordered.map(table => ({\n schema: table.schema,\n table: table.table,\n qualified: table.qualified,\n enableRls: `ALTER TABLE \"${table.schema}\".\"${table.table}\" ENABLE ROW LEVEL SECURITY;`,\n policyStatements: table.policies.flatMap(policy => renderPolicyStatements(table, policy))\n }));\n};\n\n/**\n * The `generated column → column it reads` edges a project's `search` blocks\n * imply, derived from the collections alone.\n *\n * The same edges `queryGeneratedColumnDependencies` reads out of `pg_depend`,\n * but available where no database is: `rebase db generate` writes a migration\n * that will run somewhere else, later, against a database it cannot inspect.\n * A spec's `fields[].column` is the physical column each search field starts\n * at, which is exactly what the generated expression depends on.\n */\nexport const searchColumnDependencies = (\n allCollections: CollectionConfig[]\n): GeneratedColumnDependency[] => {\n const dependencies: GeneratedColumnDependency[] = [];\n for (const table of planOf(allCollections).tables) {\n const spec = table.search;\n if (!spec) continue;\n const generated = [spec.column, ...(spec.fuzzy ? [spec.fuzzy.column] : [])];\n const sources = new Set(spec.fields.map(field => field.column));\n for (const column of generated) {\n for (const dependsOn of sources) {\n dependencies.push({ schema: table.schema, table: table.table, column, dependsOn });\n }\n }\n }\n return dependencies;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEA,IAAa,uBACT,gBACA,UAA4B,CAAC,MACpB,kBAAkB,WAAW,cAAc,GAAG,OAAO;;AAGlE,IAAa,+BAA+B,mBACxC,kBAAkB,WAAW,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;AAqBhD,IAAa,6BAA6B,mBACtC,gBAAgB,WAAW,cAAc,CAAC;;;;;;;;AAS9C,IAAa,yBAAyB,mBAClC,wBAAwB,WAAW,cAAc,CAAC;;;;;;;;;;;;AAatD,IAAa,6BACT,gBACA,UAA8C,CAAC,MACtC,gBAAgB,WAAW,gBAAgB,EAAE,oBAAoB,QAAQ,WAAW,CAAC,CAAC;;AAGnG,IAAa,yBAAyB,mBAClC,wBAAwB,WAAW,cAAc,CAAC;;;;;;;;;AAUtD,IAAa,+BAA+B,mBACxC,kBAAkB,WAAW,cAAc,CAAC;;AAGhD,IAAa,0BAA0B,mBACnC,yBAAyB,WAAW,cAAc,CAAC;AAyCvD,IAAM,UAAU,gBAAgD,WAAW,WAAW;;;;;;;;AAuCtF,IAAa,sBAAsB,mBAC/B,OAAO,cAAc,CAAC,CAAC,OAClB,QAAO,UAAS,MAAM,SAAS,UAAU,CAAC,CAC1C,KAAI,WAAU;CACX,QAAQ,MAAM;CACd,OAAO,MAAM;CACb,SAAS,MAAM,QAAQ,KAAI,OAAM;EAC7B,MAAM,EAAE;EACR,MAAM,aAAa,EAAE,IAAI;EACzB,YAAY,EAAE;CAClB,EAAE;CACF,aACI,+BAA+B,MAAM,OAAO,KAAK,MAAM,MAAM,OAC7D,MAAM,QAAQ,KAAI,MAAK,IAAI,EAAE,OAAO,IAAI,aAAa,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,KAAK,IAAI,IAClF,kBAAkB,MAAM,WAAW,KAAI,MAAK,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE;CACrE,aAAa,MAAM,QAAQ,KAAI,MAAK,EAAE,UAAU,CAAC,CAAC,QAAQ,OAA6B,OAAO,KAAA,CAAS;AAC3G,EAAE;;;;;;;;;AAuBV,IAAa,0BAA0B,mBAA+D;CAClG,MAAM,OAAO,OAAO,cAAc;CAMlC,OAAO,CAHH,GAAG,KAAK,OAAO,QAAO,MAAK,EAAE,SAAS,YAAY,GAClD,GAAG,KAAK,OAAO,QAAO,MAAK,EAAE,SAAS,UAAU,CAE7C,CAAA,CAAQ,KAAI,WAAU;EACzB,QAAQ,MAAM;EACd,OAAO,MAAM;EACb,WAAW,MAAM;EACjB,WAAW,gBAAgB,MAAM,OAAO,KAAK,MAAM,MAAM;EACzD,kBAAkB,MAAM,SAAS,SAAQ,WAAU,uBAAuB,OAAO,MAAM,CAAC;CAC5F,EAAE;AACN;;;;;;;;;;;AAYA,IAAa,4BACT,mBAC8B;CAC9B,MAAM,eAA4C,CAAC;CACnD,KAAK,MAAM,SAAS,OAAO,cAAc,CAAC,CAAC,QAAQ;EAC/C,MAAM,OAAO,MAAM;EACnB,IAAI,CAAC,MAAM;EACX,MAAM,YAAY,CAAC,KAAK,QAAQ,GAAI,KAAK,QAAQ,CAAC,KAAK,MAAM,MAAM,IAAI,CAAC,CAAE;EAC1E,MAAM,UAAU,IAAI,IAAI,KAAK,OAAO,KAAI,UAAS,MAAM,MAAM,CAAC;EAC9D,KAAK,MAAM,UAAU,WACjB,KAAK,MAAM,aAAa,SACpB,aAAa,KAAK;GAAE,QAAQ,MAAM;GAAQ,OAAO,MAAM;GAAO;GAAQ;EAAU,CAAC;CAG7F;CACA,OAAO;AACX"}
@@ -0,0 +1,28 @@
1
+ /** The directory, relative to the working directory. POSIX, because it is also a URL path. */
2
+ export declare const GENERATED_SQL_DIR = ".rebase/sql";
3
+ /**
4
+ * `schema.sql` as Atlas takes it. Relative on purpose, like the
5
+ * `file://drizzle/migrations` beside it: Atlas joins the URL's host and path, so
6
+ * `.rebase` is read as the first path segment, measured against the pinned 1.2.3.
7
+ */
8
+ export declare const DESIRED_STATE_URL = "file://.rebase/sql/schema.sql";
9
+ /** The files the DDL generator writes, all of them derived from the collections. */
10
+ export declare const GENERATED_SQL_FILES: readonly ["schema.sql", "policies.sql", "search.sql", "vector.sql", "triggers.sql"];
11
+ /** The absolute directory, for the working directory the command runs in. */
12
+ export declare function generatedSqlDir(cwd?: string): string;
13
+ /**
14
+ * Get the directory ready to be written, and retire the copies an older release
15
+ * committed.
16
+ *
17
+ * The directory ignores itself, rather than relying on the project's
18
+ * `.gitignore`: a scaffold ignores `.rebase/`, but a project created before it
19
+ * did would otherwise start committing these files from their new home.
20
+ *
21
+ * The old copies in `drizzle/` are deleted, not left behind. Nothing writes them
22
+ * any more, so from the first push after an upgrade they only fall further
23
+ * behind — and a stale `policies.sql` in the repository is exactly what someone
24
+ * reviewing the project's RLS would read.
25
+ *
26
+ * @returns the old files it removed, relative to the working directory.
27
+ */
28
+ export declare function prepareGeneratedSqlDir(cwd?: string): string[];