@rebasepro/server-postgres 0.13.0 → 0.13.1-canary.g18cfeb7

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 (63) hide show
  1. package/dist/PostgresBackendDriver.d.ts +48 -1
  2. package/dist/{src-DlPBctw_.js → auth-users-columns-Do9mw5Y5.js} +318 -42
  3. package/dist/auth-users-columns-Do9mw5Y5.js.map +1 -0
  4. package/dist/{backup-service-CD8o_1Sl.js → backup-service-Bww-Lg0s.js} +2 -2
  5. package/dist/{backup-service-CD8o_1Sl.js.map → backup-service-Bww-Lg0s.js.map} +1 -1
  6. package/dist/cli-helpers.d.ts +1 -1
  7. package/dist/{ensure-collection-policies-ViG8XiPn.js → ensure-collection-policies-DMUdRQdM.js} +2 -2
  8. package/dist/{ensure-collection-policies-ViG8XiPn.js.map → ensure-collection-policies-DMUdRQdM.js.map} +1 -1
  9. package/dist/{ensure-collection-tables-CBQdOETu.js → ensure-collection-tables-DSIxvLLD.js} +59 -15
  10. package/dist/ensure-collection-tables-DSIxvLLD.js.map +1 -0
  11. package/dist/index.es.js +534 -200
  12. package/dist/index.es.js.map +1 -1
  13. package/dist/{policy-CeA1JcxP.js → policy-CPkCqVTz.js} +4 -4
  14. package/dist/policy-CPkCqVTz.js.map +1 -0
  15. package/dist/rls-bootstrap-sql-Bpv3nUZo.js +244 -0
  16. package/dist/rls-bootstrap-sql-Bpv3nUZo.js.map +1 -0
  17. package/dist/schema/auth-users-columns.d.ts +97 -0
  18. package/dist/schema/ensure-collection-tables.d.ts +1 -1
  19. package/dist/schema/generate-drizzle-schema-logic.d.ts +1 -1
  20. package/dist/schema/generate-postgres-ddl-logic.d.ts +5 -5
  21. package/dist/schema/generated-schema-staleness.d.ts +39 -0
  22. package/dist/schema/rls-bootstrap-sql.d.ts +135 -0
  23. package/dist/security/rls-enforcement.d.ts +53 -2
  24. package/dist/services/FetchService.d.ts +10 -7
  25. package/dist/services/dataService.d.ts +2 -0
  26. package/dist/services/realtimeService.d.ts +25 -21
  27. package/dist/{src-DoU9yPqq.js → src-C_wvdMnl.js} +91 -2
  28. package/dist/src-C_wvdMnl.js.map +1 -0
  29. package/dist/{websocket-B2LsrINK.js → websocket-D0TBU3ia.js} +3 -3
  30. package/dist/{websocket-B2LsrINK.js.map → websocket-D0TBU3ia.js.map} +1 -1
  31. package/package.json +9 -8
  32. package/src/PostgresBackendDriver.ts +165 -3
  33. package/src/PostgresBootstrapper.ts +41 -2
  34. package/src/auth/ensure-tables.ts +185 -86
  35. package/src/cli-helpers.ts +22 -9
  36. package/src/cli.ts +175 -30
  37. package/src/collections/validate-relations.ts +124 -17
  38. package/src/data-transformer.ts +13 -3
  39. package/src/history/ensure-history-table.ts +7 -0
  40. package/src/schema/auth-users-columns.ts +131 -0
  41. package/src/schema/doctor.ts +7 -5
  42. package/src/schema/ensure-collection-tables.ts +88 -19
  43. package/src/schema/generate-drizzle-schema-logic.ts +8 -2
  44. package/src/schema/generate-postgres-ddl-logic.ts +97 -13
  45. package/src/schema/generated-schema-staleness.ts +169 -0
  46. package/src/schema/introspect-db-logic.ts +1 -1
  47. package/src/schema/non-sql-collections.test.ts +131 -0
  48. package/src/schema/rls-bootstrap-sql.ts +288 -0
  49. package/src/security/anonymous-grants.test.ts +4 -2
  50. package/src/security/rls-enforcement.ts +141 -3
  51. package/src/services/BranchService.ts +5 -0
  52. package/src/services/FetchService.ts +10 -93
  53. package/src/services/PersistService.ts +14 -1
  54. package/src/services/channel-history.ts +8 -0
  55. package/src/services/channel-presence.ts +6 -0
  56. package/src/services/dataService.ts +2 -0
  57. package/src/services/realtimeService.ts +36 -33
  58. package/dist/ensure-collection-tables-CBQdOETu.js.map +0 -1
  59. package/dist/policy-CeA1JcxP.js.map +0 -1
  60. package/dist/schema/auth-bootstrap-sql.d.ts +0 -24
  61. package/dist/src-DlPBctw_.js.map +0 -1
  62. package/dist/src-DoU9yPqq.js.map +0 -1
  63. package/src/schema/auth-bootstrap-sql.ts +0 -47
@@ -24,7 +24,7 @@ import type { CollectionConfig } from "@rebasepro/types";
24
24
  */
25
25
  export declare function diagnoseMissingBin(packageName: string): "not-installed" | "build-script-blocked";
26
26
  export declare function resolveLocalBin(binName: string): string | null;
27
- export declare function getTableIncludesFromCollections(collections: CollectionConfig[]): Promise<string[]>;
27
+ export declare function getTableIncludesFromCollections(allCollections: CollectionConfig[]): Promise<string[]>;
28
28
  export declare function getTableIncludes(collectionsPath: string): Promise<string[]>;
29
29
  export declare function getDevDatabaseUrl(databaseUrl: string): string;
30
30
  export declare function ensureDevDatabaseExists(databaseUrl: string, devDatabaseUrl: string): Promise<void>;
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __createRequire } from "module";
2
2
  import "process";
3
3
  __createRequire(import.meta.url);
4
- import { readExistingSchema, t as planCollectionPolicies } from "./ensure-collection-tables-CBQdOETu.js";
4
+ import { readExistingSchema, t as planCollectionPolicies } from "./ensure-collection-tables-DSIxvLLD.js";
5
5
  //#region src/schema/ensure-collection-policies.ts
6
6
  var isCreatePolicy = (statement) => /^\s*CREATE POLICY/i.test(statement);
7
7
  /**
@@ -54,4 +54,4 @@ async function ensureCollectionPolicies(client, collections, log) {
54
54
  //#endregion
55
55
  export { ensureCollectionPolicies };
56
56
 
57
- //# sourceMappingURL=ensure-collection-policies-ViG8XiPn.js.map
57
+ //# sourceMappingURL=ensure-collection-policies-DMUdRQdM.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ensure-collection-policies-ViG8XiPn.js","names":[],"sources":["../src/schema/ensure-collection-policies.ts"],"sourcesContent":["/**\n * Applying a bundle's RLS policies to a database at boot, idempotently.\n *\n * ## Why this exists\n *\n * {@link ensureCollectionTables} creates the collection *tables* a managed\n * runtime boots against, but a table with row-level security disabled and no\n * policies is not servable: authenticated requests run as the restricted\n * `rebase_user` role, so a read with no `SELECT` policy returns nothing (a\n * public collection answered 401) and a write with no `INSERT`/`UPDATE` policy\n * is denied. The policies live in the collections' `securityRules`; nothing at\n * boot applied them. `rebase db push` does — but it drives Atlas against a\n * local `DATABASE_URL`, and a managed tenant's database is reachable only from\n * inside the cluster, by the runtime that is already connected to it. So the\n * runtime is the only thing that *can* apply them, and this is where it does.\n *\n * ## Why this is safe to run on every boot\n *\n * Every statement is idempotent: `ENABLE ROW LEVEL SECURITY` is a no-op once\n * enabled, and each policy is a `DROP POLICY IF EXISTS` immediately followed by\n * a `CREATE POLICY`, so re-applying asserts exactly the declared state. It adds\n * and replaces; it never drops data. (It does not *reconcile* — a policy a\n * previous push left behind under an old name is not removed here; that stays a\n * `db push` / `db migrate` concern, alongside destructive schema changes.)\n *\n * Unlike table creation, a failure here is not fatal: RLS stays enabled, so a\n * table whose policies could not be applied fails **closed** (denies) rather\n * than leaking rows. One collection's policy failing (e.g. a rule that\n * references a table a real migration has not created yet) must not crash-loop\n * the whole deployment and take the other collections' working routes down with\n * it. Failures are reported loudly and per-table so the operator can see\n * exactly which collection is not yet servable and why.\n */\nimport { type CollectionConfig } from \"@rebasepro/types\";\nimport { planCollectionPolicies } from \"./generate-postgres-ddl-logic\";\nimport { readExistingSchema, type Queryable } from \"./ensure-collection-tables\";\n\nexport interface PolicyEnsureResult {\n /** `CREATE POLICY` statements that ran successfully. */\n policiesApplied: number;\n /** Tables that had RLS enabled. */\n tablesSecured: number;\n /** Declared tables absent from the database — left to a real migration. */\n skipped: { table: string; reason: string }[];\n /** Tables whose RLS could not be fully applied (fail closed). */\n failures: { table: string; error: string }[];\n}\n\nconst isCreatePolicy = (statement: string): boolean => /^\\s*CREATE POLICY/i.test(statement);\n\n/**\n * Bring the declared collections' RLS policies up to date. Returns what it did.\n *\n * Only tables that already exist are touched: the boot-time table creator runs\n * first, so anything still missing is a table this additive path is not allowed\n * to create (a junction, or a relation left to a migration). Enabling RLS on a\n * non-existent table would error, so those are recorded as skipped, not failed.\n */\nexport async function ensureCollectionPolicies(\n client: Queryable,\n collections: CollectionConfig[],\n log?: (message: string) => void\n): Promise<PolicyEnsureResult> {\n const result: PolicyEnsureResult = { policiesApplied: 0, tablesSecured: 0, skipped: [], failures: [] };\n\n const plans = planCollectionPolicies(collections);\n if (plans.length === 0) return result;\n\n const schemas = Array.from(new Set(plans.map(p => p.schema)));\n const existing = await readExistingSchema(client, schemas);\n\n for (const plan of plans) {\n if (!existing.tables.has(plan.qualified)) {\n result.skipped.push({\n table: plan.qualified,\n reason: \"table is not present in the database; create it with `rebase db push` / `rebase db migrate`\"\n });\n continue;\n }\n\n try {\n // Enable first: if a later policy statement fails, the table is left\n // locked (deny-all for the user role) rather than open.\n await client.query(plan.enableRls);\n result.tablesSecured++;\n\n let created = 0;\n for (const statement of plan.policyStatements) {\n await client.query(statement);\n if (isCreatePolicy(statement)) {\n result.policiesApplied++;\n created++;\n }\n }\n log?.(`${plan.qualified}: RLS enabled, ${created} policy(ies) applied`);\n } catch (err) {\n result.failures.push({\n table: plan.qualified,\n error: err instanceof Error ? err.message : String(err)\n });\n }\n }\n\n return result;\n}\n"],"mappings":";;;;;AAgDA,IAAM,kBAAkB,cAA+B,qBAAqB,KAAK,SAAS;;;;;;;;;AAU1F,eAAsB,yBAClB,QACA,aACA,KAC2B;CAC3B,MAAM,SAA6B;EAAE,iBAAiB;EAAG,eAAe;EAAG,SAAS,CAAC;EAAG,UAAU,CAAC;CAAE;CAErG,MAAM,QAAQ,uBAAuB,WAAW;CAChD,IAAI,MAAM,WAAW,GAAG,OAAO;CAG/B,MAAM,WAAW,MAAM,mBAAmB,QAD1B,MAAM,KAAK,IAAI,IAAI,MAAM,KAAI,MAAK,EAAE,MAAM,CAAC,CACT,CAAO;CAEzD,KAAK,MAAM,QAAQ,OAAO;EACtB,IAAI,CAAC,SAAS,OAAO,IAAI,KAAK,SAAS,GAAG;GACtC,OAAO,QAAQ,KAAK;IAChB,OAAO,KAAK;IACZ,QAAQ;GACZ,CAAC;GACD;EACJ;EAEA,IAAI;GAGA,MAAM,OAAO,MAAM,KAAK,SAAS;GACjC,OAAO;GAEP,IAAI,UAAU;GACd,KAAK,MAAM,aAAa,KAAK,kBAAkB;IAC3C,MAAM,OAAO,MAAM,SAAS;IAC5B,IAAI,eAAe,SAAS,GAAG;KAC3B,OAAO;KACP;IACJ;GACJ;GACA,MAAM,GAAG,KAAK,UAAU,iBAAiB,QAAQ,qBAAqB;EAC1E,SAAS,KAAK;GACV,OAAO,SAAS,KAAK;IACjB,OAAO,KAAK;IACZ,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GAC1D,CAAC;EACL;CACJ;CAEA,OAAO;AACX"}
1
+ {"version":3,"file":"ensure-collection-policies-DMUdRQdM.js","names":[],"sources":["../src/schema/ensure-collection-policies.ts"],"sourcesContent":["/**\n * Applying a bundle's RLS policies to a database at boot, idempotently.\n *\n * ## Why this exists\n *\n * {@link ensureCollectionTables} creates the collection *tables* a managed\n * runtime boots against, but a table with row-level security disabled and no\n * policies is not servable: authenticated requests run as the restricted\n * `rebase_user` role, so a read with no `SELECT` policy returns nothing (a\n * public collection answered 401) and a write with no `INSERT`/`UPDATE` policy\n * is denied. The policies live in the collections' `securityRules`; nothing at\n * boot applied them. `rebase db push` does — but it drives Atlas against a\n * local `DATABASE_URL`, and a managed tenant's database is reachable only from\n * inside the cluster, by the runtime that is already connected to it. So the\n * runtime is the only thing that *can* apply them, and this is where it does.\n *\n * ## Why this is safe to run on every boot\n *\n * Every statement is idempotent: `ENABLE ROW LEVEL SECURITY` is a no-op once\n * enabled, and each policy is a `DROP POLICY IF EXISTS` immediately followed by\n * a `CREATE POLICY`, so re-applying asserts exactly the declared state. It adds\n * and replaces; it never drops data. (It does not *reconcile* — a policy a\n * previous push left behind under an old name is not removed here; that stays a\n * `db push` / `db migrate` concern, alongside destructive schema changes.)\n *\n * Unlike table creation, a failure here is not fatal: RLS stays enabled, so a\n * table whose policies could not be applied fails **closed** (denies) rather\n * than leaking rows. One collection's policy failing (e.g. a rule that\n * references a table a real migration has not created yet) must not crash-loop\n * the whole deployment and take the other collections' working routes down with\n * it. Failures are reported loudly and per-table so the operator can see\n * exactly which collection is not yet servable and why.\n */\nimport { type CollectionConfig } from \"@rebasepro/types\";\nimport { planCollectionPolicies } from \"./generate-postgres-ddl-logic\";\nimport { readExistingSchema, type Queryable } from \"./ensure-collection-tables\";\n\nexport interface PolicyEnsureResult {\n /** `CREATE POLICY` statements that ran successfully. */\n policiesApplied: number;\n /** Tables that had RLS enabled. */\n tablesSecured: number;\n /** Declared tables absent from the database — left to a real migration. */\n skipped: { table: string; reason: string }[];\n /** Tables whose RLS could not be fully applied (fail closed). */\n failures: { table: string; error: string }[];\n}\n\nconst isCreatePolicy = (statement: string): boolean => /^\\s*CREATE POLICY/i.test(statement);\n\n/**\n * Bring the declared collections' RLS policies up to date. Returns what it did.\n *\n * Only tables that already exist are touched: the boot-time table creator runs\n * first, so anything still missing is a table this additive path is not allowed\n * to create (a junction, or a relation left to a migration). Enabling RLS on a\n * non-existent table would error, so those are recorded as skipped, not failed.\n */\nexport async function ensureCollectionPolicies(\n client: Queryable,\n collections: CollectionConfig[],\n log?: (message: string) => void\n): Promise<PolicyEnsureResult> {\n const result: PolicyEnsureResult = { policiesApplied: 0, tablesSecured: 0, skipped: [], failures: [] };\n\n const plans = planCollectionPolicies(collections);\n if (plans.length === 0) return result;\n\n const schemas = Array.from(new Set(plans.map(p => p.schema)));\n const existing = await readExistingSchema(client, schemas);\n\n for (const plan of plans) {\n if (!existing.tables.has(plan.qualified)) {\n result.skipped.push({\n table: plan.qualified,\n reason: \"table is not present in the database; create it with `rebase db push` / `rebase db migrate`\"\n });\n continue;\n }\n\n try {\n // Enable first: if a later policy statement fails, the table is left\n // locked (deny-all for the user role) rather than open.\n await client.query(plan.enableRls);\n result.tablesSecured++;\n\n let created = 0;\n for (const statement of plan.policyStatements) {\n await client.query(statement);\n if (isCreatePolicy(statement)) {\n result.policiesApplied++;\n created++;\n }\n }\n log?.(`${plan.qualified}: RLS enabled, ${created} policy(ies) applied`);\n } catch (err) {\n result.failures.push({\n table: plan.qualified,\n error: err instanceof Error ? err.message : String(err)\n });\n }\n }\n\n return result;\n}\n"],"mappings":";;;;;AAgDA,IAAM,kBAAkB,cAA+B,qBAAqB,KAAK,SAAS;;;;;;;;;AAU1F,eAAsB,yBAClB,QACA,aACA,KAC2B;CAC3B,MAAM,SAA6B;EAAE,iBAAiB;EAAG,eAAe;EAAG,SAAS,CAAC;EAAG,UAAU,CAAC;CAAE;CAErG,MAAM,QAAQ,uBAAuB,WAAW;CAChD,IAAI,MAAM,WAAW,GAAG,OAAO;CAG/B,MAAM,WAAW,MAAM,mBAAmB,QAD1B,MAAM,KAAK,IAAI,IAAI,MAAM,KAAI,MAAK,EAAE,MAAM,CAAC,CACT,CAAO;CAEzD,KAAK,MAAM,QAAQ,OAAO;EACtB,IAAI,CAAC,SAAS,OAAO,IAAI,KAAK,SAAS,GAAG;GACtC,OAAO,QAAQ,KAAK;IAChB,OAAO,KAAK;IACZ,QAAQ;GACZ,CAAC;GACD;EACJ;EAEA,IAAI;GAGA,MAAM,OAAO,MAAM,KAAK,SAAS;GACjC,OAAO;GAEP,IAAI,UAAU;GACd,KAAK,MAAM,aAAa,KAAK,kBAAkB;IAC3C,MAAM,OAAO,MAAM,SAAS;IAC5B,IAAI,eAAe,SAAS,GAAG;KAC3B,OAAO;KACP;IACJ;GACJ;GACA,MAAM,GAAG,KAAK,UAAU,iBAAiB,QAAQ,qBAAqB;EAC1E,SAAS,KAAK;GACV,OAAO,SAAS,KAAK;IACjB,OAAO,KAAK;IACZ,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GAC1D,CAAC;EACL;CACJ;CAEA,OAAO;AACX"}
@@ -1,8 +1,8 @@
1
1
  import { createRequire as __createRequire } from "module";
2
2
  import "process";
3
3
  __createRequire(import.meta.url);
4
- import { A as toSnakeCase, D as getPolicyNamesForRule, E as legacyForeignKeyName, T as generateForeignKeyName, a as getJunctionSecurityRules, c as policyToPostgres, d as findRelation, g as resolveCollectionRelations, i as getJunctionCollectionConfig, l as securityRuleToConditions, m as getTableName, o as resolveJunctionSpecs, r as resolveStringColumnLength, s as getEffectiveSecurityRules } from "./src-DlPBctw_.js";
5
- import { n as isPostgresCollectionConfig } from "./src-DoU9yPqq.js";
4
+ import { A as generateForeignKeyName, I as toSnakeCase, M as getPolicyNamesForRule, c as resolveStringColumnLength, d as resolveJunctionSpecs, f as getEffectiveSecurityRules, g as findRelation, i as isAuthCollection, j as legacyForeignKeyName, l as getJunctionCollectionConfig, m as securityRuleToConditions, n as authUsersColumnDefinition, p as policyToPostgres, r as authUsersColumnSql, s as relationalCollections, t as AUTH_USERS_COLUMNS, u as getJunctionSecurityRules, x as resolveCollectionRelations, y as getTableName } from "./auth-users-columns-Do9mw5Y5.js";
5
+ import { c as isPostgresCollectionConfig } from "./src-C_wvdMnl.js";
6
6
  import { logger } from "@rebasepro/server";
7
7
  //#region src/schema/generate-postgres-ddl-logic.ts
8
8
  var resolveColumnName = (propName, prop) => {
@@ -167,8 +167,30 @@ var getSqlColumnType = (propName, prop, collection, collections) => {
167
167
  };
168
168
  var schemaOfCollection = (collection) => isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : "public";
169
169
  var bareTableName = (name) => name.includes(".") ? name.split(".").pop() : name;
170
+ /**
171
+ * Truncate a derived identifier the way Postgres does: to 63 bytes, silently.
172
+ *
173
+ * This is not cosmetic, and not really a naming choice at all — it is agreeing
174
+ * with the name the database ALREADY stored. `ADD CONSTRAINT` on a longer name
175
+ * succeeds and records the truncated form, so the untruncated name this used to
176
+ * derive matched nothing in the catalogue. Boot-ensure compares its planned
177
+ * constraints against `readExistingSchema`, which reads catalogue names, so the
178
+ * comparison could never hit: every boot re-issued `ADD CONSTRAINT` for the same
179
+ * constraint, forever, and got "already exists" every time. Non-fatal (foreign
180
+ * keys are the one action allowed to fail) and therefore permanent — an error in
181
+ * the log on every restart of a project whose table and column names happened to
182
+ * be long.
183
+ *
184
+ * Byte length, not string length: NAMEDATALEN is 64 bytes, and a multi-byte
185
+ * character straddling the boundary would be cut mid-sequence by `slice(0, 63)`.
186
+ */
187
+ var toPostgresIdentifier = (name) => {
188
+ const bytes = Buffer.from(name, "utf8");
189
+ if (bytes.byteLength <= 63) return name;
190
+ return bytes.subarray(0, 63).toString("utf8").replace(/�+$/, "");
191
+ };
170
192
  var foreignKeyPlan = (args) => {
171
- const constraintName = `${args.table}_${args.column}_fkey`;
193
+ const constraintName = toPostgresIdentifier(`${args.table}_${args.column}_fkey`);
172
194
  const onUpdate = args.onUpdate ? ` ON UPDATE ${args.onUpdate.toUpperCase()}` : "";
173
195
  return {
174
196
  constraintName,
@@ -196,7 +218,8 @@ var foreignKeyPlan = (args) => {
196
218
  * is unknown yields the column without a constraint. Both mirror the generator
197
219
  * exactly — a divergence here is a schema fork between boot and `db push`.
198
220
  */
199
- var planRelationalColumns = (collections) => {
221
+ var planRelationalColumns = (allCollections) => {
222
+ const collections = relationalCollections(allCollections);
200
223
  const plans = [];
201
224
  for (const collection of collections) {
202
225
  const tableName = getTableName(collection);
@@ -272,7 +295,8 @@ var planRelationalColumns = (collections) => {
272
295
  * junction with row-level security left off is readable and writable by every
273
296
  * signed-in user, which is why the two must ship together.
274
297
  */
275
- var planJunctionTables = (collections) => {
298
+ var planJunctionTables = (allCollections) => {
299
+ const collections = relationalCollections(allCollections);
276
300
  const plans = [];
277
301
  for (const spec of resolveJunctionSpecs(collections).values()) {
278
302
  const [source, target] = spec.endpoints;
@@ -324,7 +348,8 @@ var planJunctionTables = (collections) => {
324
348
  * writable by every signed-in user. A junction whose table is still absent is
325
349
  * skipped by the applier, not planned away here.
326
350
  */
327
- var planCollectionPolicies = (collections) => {
351
+ var planCollectionPolicies = (allCollections) => {
352
+ const collections = relationalCollections(allCollections);
328
353
  const resolveCollection = (slug) => collections.find((c) => c.slug === slug || getTableName(c) === slug);
329
354
  const plans = [];
330
355
  const seen = /* @__PURE__ */ new Set();
@@ -436,7 +461,8 @@ function requiredEnums(collection) {
436
461
  * that reference them, tables before the columns added to other tables (a new
437
462
  * table may be the target of a relation), and nothing is emitted twice.
438
463
  */
439
- function planCollectionSchemaEnsure(collections, existing) {
464
+ function planCollectionSchemaEnsure(allCollections, existing) {
465
+ const collections = relationalCollections(allCollections);
440
466
  const actions = [];
441
467
  const plannedEnums = /* @__PURE__ */ new Set();
442
468
  for (const collection of collections) for (const { name, values } of requiredEnums(collection)) {
@@ -459,10 +485,8 @@ function planCollectionSchemaEnsure(collections, existing) {
459
485
  const idEntry = Object.entries(collection.properties ?? {}).find(([n, p]) => isIdProperty(n, p, collection));
460
486
  const idName = idEntry ? resolveColumnName(idEntry[0], idEntry[1]) : "id";
461
487
  const idProp = idEntry?.[1];
462
- let idDef;
463
- if (idProp?.type === "number") idDef = `"${idName}" BIGSERIAL PRIMARY KEY`;
464
- else if (idProp && idProp.type === "string" && idProp.isId === "uuid") idDef = `"${idName}" UUID PRIMARY KEY DEFAULT gen_random_uuid()`;
465
- else idDef = `"${idName}" TEXT PRIMARY KEY`;
488
+ let idDef = `"${idName}" ${idProp ? getSqlColumnType(idEntry[0], idProp, collection, collections) : "TEXT"} PRIMARY KEY`;
489
+ if (idProp?.type === "string" && idProp.isId === "uuid") idDef += " DEFAULT gen_random_uuid()";
466
490
  actions.push({
467
491
  kind: "create-table",
468
492
  target: key,
@@ -510,23 +534,43 @@ function planCollectionSchemaEnsure(collections, existing) {
510
534
  });
511
535
  return true;
512
536
  };
513
- const addColumn = (key, schema, table, column, type) => {
537
+ const addColumn = (key, schema, table, column, definition) => {
514
538
  if (existing.tables.get(key)?.has(column)) return;
515
539
  actions.push({
516
540
  kind: "add-column",
517
541
  target: `${key}.${column}`,
518
- sql: `ALTER TABLE "${schema}"."${table}" ADD COLUMN IF NOT EXISTS "${column}" ${type};`
542
+ sql: `ALTER TABLE "${schema}"."${table}" ADD COLUMN IF NOT EXISTS "${column}" ${definition};`
519
543
  });
520
544
  };
521
545
  for (const collection of collections) {
522
546
  const key = qualified(collection);
523
547
  const schema = schemaOf(collection);
524
548
  const table = getTableName(collection);
549
+ const fresh = created.has(key);
550
+ const auth = isAuthCollection(collection);
525
551
  for (const [propName, prop] of Object.entries(collection.properties ?? {})) {
526
552
  const p = prop;
527
553
  if (isIdProperty(propName, p, collection)) continue;
528
554
  if (p.type === "reference" || p.type === "relation") continue;
529
- addColumn(key, schema, table, resolveColumnName(propName, p), getSqlColumnType(propName, p, collection, collections));
555
+ const column = resolveColumnName(propName, p);
556
+ const authDefinition = auth ? authUsersColumnDefinition(column) : void 0;
557
+ if (authDefinition) {
558
+ addColumn(key, schema, table, column, authDefinition);
559
+ continue;
560
+ }
561
+ let definition = getSqlColumnType(propName, p, collection, collections);
562
+ if (fresh && p.validation?.unique) definition += " UNIQUE";
563
+ const autoValue = p.autoValue;
564
+ if (p.type === "date" && (autoValue === "on_create" || autoValue === "on_update")) definition += " DEFAULT now()";
565
+ if (fresh && p.validation?.required) definition += " NOT NULL";
566
+ addColumn(key, schema, table, column, definition);
567
+ }
568
+ if (auth) {
569
+ const declared = new Set(Object.entries(collection.properties ?? {}).map(([name, prop]) => resolveColumnName(name, prop)));
570
+ for (const spec of AUTH_USERS_COLUMNS) {
571
+ if (declared.has(spec.column)) continue;
572
+ addColumn(key, schema, table, spec.column, authUsersColumnSql(spec));
573
+ }
530
574
  }
531
575
  }
532
576
  for (const junction of junctions) {
@@ -647,4 +691,4 @@ async function ensureCollectionTables(client, collections, log) {
647
691
  //#endregion
648
692
  export { ensureCollectionTables, readExistingSchema, planCollectionPolicies as t };
649
693
 
650
- //# sourceMappingURL=ensure-collection-tables-CBQdOETu.js.map
694
+ //# sourceMappingURL=ensure-collection-tables-DSIxvLLD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensure-collection-tables-DSIxvLLD.js","names":[],"sources":["../src/schema/generate-postgres-ddl-logic.ts","../src/schema/ensure-collection-tables.ts"],"sourcesContent":["import { CollectionConfig, NumberProperty, Property, ResolvedRelation, RelationProperty, SecurityOperation, SecurityRule, StringProperty, isPostgresCollectionConfig, DateProperty, ArrayProperty, MapProperty, ReferenceProperty, VectorProperty, BinaryProperty, isManyToMany, type ResolvedManyToMany, type ResolvedBelongsTo } from \"@rebasepro/types\";\nimport { getEnumVarName, getTableName, resolveCollectionRelations, findRelation, securityRuleToConditions, policyToPostgres, getEffectiveSecurityRules, getInjectedSecurityRules, resolveJunctionSpecs, getJunctionSecurityRules, getJunctionCollectionConfig, resolveStringColumnLength, relationalCollections } from \"@rebasepro/common\";\nimport { toSnakeCase, getPolicyNamesForRule, generateForeignKeyName, legacyForeignKeyName } from \"@rebasepro/utils\";\nimport { AUTH_USERS_COLUMNS, authUsersColumnDefinition, authUsersColumnSql, isAuthCollection } from \"./auth-users-columns\";\nimport { REBASE_SCHEMA } from \"@rebasepro/types\";\n\n// --- Helper Functions ---\n\nexport const resolveColumnName = (propName: string, prop?: Property | null): string => {\n if (prop && \"columnName\" in prop && typeof prop.columnName === \"string\") {\n return prop.columnName;\n }\n return toSnakeCase(propName);\n};\n\nexport const getPrimaryKeyProp = (collection: CollectionConfig): { name: string, type: \"string\" | \"number\", isUuid: boolean } => {\n if (collection.properties) {\n const idPropEntry = Object.entries(collection.properties).find(([_, prop]) => \"isId\" in (prop as unknown as object) && Boolean((prop as unknown as Record<string, unknown>).isId));\n if (idPropEntry) {\n const prop = idPropEntry[1] as unknown as Property;\n const isUuid = prop.type === \"string\" && \"isId\" in prop && (prop as unknown as StringProperty).isId === \"uuid\";\n return { name: idPropEntry[0], type: prop.type === \"number\" ? \"number\" : \"string\", isUuid };\n }\n }\n const idProp = collection.properties?.[\"id\"] as unknown as Property | undefined;\n if (idProp?.type === \"number\") {\n return { name: \"id\", type: \"number\", isUuid: false };\n }\n const isUuid = idProp?.type === \"string\" && \"isId\" in idProp && (idProp as unknown as StringProperty).isId === \"uuid\";\n return { name: \"id\", type: \"string\", isUuid: isUuid ?? false };\n};\n\nexport const isNumericId = (collection: CollectionConfig): boolean => {\n return getPrimaryKeyProp(collection).type === \"number\";\n};\n\nexport const getPrimaryKeyName = (collection: CollectionConfig): string => {\n return getPrimaryKeyProp(collection).name;\n};\n\n/** The column type a junction holds for one endpoint's primary key. */\nconst junctionKeyType = (collection: CollectionConfig): string =>\n isNumericId(collection) ? \"INTEGER\" : (getPrimaryKeyProp(collection).isUuid ? \"UUID\" : \"TEXT\");\n\nexport const isIdProperty = (propName: string, prop: Property, collection: CollectionConfig): boolean => {\n if (\"isId\" in prop && Boolean(prop.isId)) return true;\n const hasExplicitId = Object.values(collection.properties ?? {}).some(p => \"isId\" in (p as unknown as object) && Boolean((p as unknown as Record<string, unknown>).isId));\n return !hasExplicitId && propName === \"id\";\n};\n\n\ntype ResolveCollection = (slug: string) => CollectionConfig | undefined;\n\n/**\n * Render statements produced by {@link generatePolicyStatements} back into the\n * exact string the DDL/policies files have always carried: each statement on\n * its own line, terminated by a newline. Keeping the string form derived from\n * the statement array means the two can never drift — the boot-time applier and\n * the generated `policies.sql` emit the same SQL, from the same source.\n */\nconst statementsToDdl = (statements: string[]): string => statements.map(s => `${s}\\n`).join(\"\");\n\nconst generatePolicyDdl = (collection: CollectionConfig, rule: SecurityRule, resolveCollection: ResolveCollection): string =>\n statementsToDdl(generatePolicyStatements(collection, rule, resolveCollection));\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 = (collection: CollectionConfig, rule: SecurityRule, resolveCollection: ResolveCollection): string[] => {\n const tableName = getTableName(collection);\n const ops: readonly SecurityOperation[] = rule.operations && rule.operations.length > 0\n ? rule.operations\n : [rule.operation ?? \"all\"];\n\n const policyNames = getPolicyNamesForRule(rule, tableName);\n\n return ops.flatMap((op, opIdx) => {\n return generateSinglePolicyStatements(collection, rule, op, policyNames[opIdx], resolveCollection);\n });\n};\n\nconst generateSinglePolicyStatements = (collection: CollectionConfig, rule: SecurityRule, operation: SecurityOperation, policyName: string, resolveCollection: ResolveCollection): string[] => {\n const schema = isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\";\n const tableName = getTableName(collection);\n const mode = (rule.mode ?? \"permissive\").toUpperCase();\n const operationUpper = operation.toUpperCase();\n const pgRoles = rule.pgRoles ? [...rule.pgRoles].sort() : [\"public\"];\n\n const needsUsing = operation !== \"insert\";\n const needsWithCheck = operation !== \"select\" && operation !== \"delete\";\n\n // Desugar the rule (access / ownerField / roles / structured condition / raw\n // SQL) into the shared PolicyExpression model, then compile to SQL. This is\n // the same normalization the client-side evaluator uses, so DDL and UI agree.\n const { usingExpr, withCheckExpr } = securityRuleToConditions(rule);\n\n let usingClause = needsUsing && usingExpr ? policyToPostgres(usingExpr, collection, { resolveCollection }) : null;\n let withCheckClause = needsWithCheck && withCheckExpr ? policyToPostgres(withCheckExpr, collection, { resolveCollection }) : null;\n\n if (!usingClause && needsUsing) {\n usingClause = \"false\";\n }\n if (!withCheckClause && needsWithCheck) {\n withCheckClause = \"false\";\n }\n\n const drop = `DROP POLICY IF EXISTS \"${policyName}\" ON \"${schema}\".\"${tableName}\";`;\n let create = `CREATE POLICY \"${policyName}\" ON \"${schema}\".\"${tableName}\" AS ${mode} FOR ${operationUpper} TO ${pgRoles.map(r => `\"${r}\"`).join(\", \")}`;\n if (usingClause) create += ` USING (${usingClause})`;\n if (withCheckClause) create += ` WITH CHECK (${withCheckClause})`;\n create += \";\";\n return [drop, create];\n};\n\n/**\n * Single-quote escaping for a SQL string literal (PostgreSQL doubles the\n * quote). Enum labels come straight from user-authored collection config, so a\n * label like `it's` closes the literal early and the whole generated file stops\n * parsing at the `CREATE TYPE`. Lives here rather than next to its other caller\n * because ensure-collection-tables already imports from this module — the\n * reverse would be a cycle.\n */\nexport const quoteSqlLiteral = (value: string): string => `'${value.replace(/'/g, \"''\")}'`;\n\nexport const getSqlColumnType = (propName: string, prop: Property, collection: CollectionConfig, collections: CollectionConfig[]): string => {\n switch (prop.type) {\n case \"string\": {\n const stringProp = prop as StringProperty;\n if (stringProp.enum) {\n const tableName = getTableName(collection);\n const colName = resolveColumnName(propName, prop);\n const schema = isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\";\n return `\"${schema}\".\"${tableName}_${colName}\"`;\n }\n if (stringProp.isId === \"uuid\" || stringProp.columnType === \"uuid\") {\n return \"UUID\";\n }\n // Width comes from `validation.max` when the property states one.\n // It used to be a hardcoded 255 here and *absent* on the Drizzle\n // path, so the same property produced a bounded column down one\n // generator and an unbounded one down the other.\n if (stringProp.columnType === \"char\") {\n return `CHAR(${resolveStringColumnLength(stringProp)})`;\n }\n if (stringProp.columnType === \"varchar\") {\n return `VARCHAR(${resolveStringColumnLength(stringProp)})`;\n }\n // `text` is the default. The two generators disagreed here before:\n // this one emitted VARCHAR(255) while the drizzle path emitted a bare\n // `varchar()`, which Postgres treats as unbounded — so the same\n // property produced a capped column down one path and an uncapped one\n // down the other.\n return \"TEXT\";\n }\n case \"number\": {\n const numProp = prop as NumberProperty;\n const isId = isIdProperty(propName, prop, collection);\n if (\"isId\" in numProp && numProp.isId === \"increment\") {\n return \"INTEGER GENERATED BY DEFAULT AS IDENTITY\";\n }\n if (numProp.columnType) {\n if (numProp.columnType === \"double precision\") return \"DOUBLE PRECISION\";\n return numProp.columnType.toUpperCase();\n }\n return (numProp.validation?.integer || isId) ? \"INTEGER\" : \"NUMERIC\";\n }\n case \"boolean\":\n return \"BOOLEAN\";\n case \"date\": {\n const dateProp = prop as DateProperty;\n if (dateProp.columnType === \"date\") return \"DATE\";\n if (dateProp.columnType === \"time\") return \"TIME\";\n return \"TIMESTAMP WITH TIME ZONE\";\n }\n case \"map\": {\n const mapProp = prop as MapProperty;\n return mapProp.columnType === \"json\" ? \"JSON\" : \"JSONB\";\n }\n case \"array\": {\n const arrayProp = prop as ArrayProperty;\n let colType = arrayProp.columnType;\n if (!colType && arrayProp.of && !Array.isArray(arrayProp.of)) {\n const ofProp = arrayProp.of as Property;\n if (ofProp.type === \"string\") {\n colType = \"text[]\";\n } else if (ofProp.type === \"number\") {\n colType = ofProp.validation?.integer ? \"integer[]\" : \"numeric[]\";\n } else if (ofProp.type === \"boolean\") {\n colType = \"boolean[]\";\n }\n }\n if (colType === \"json\") return \"JSON\";\n if (colType === \"text[]\") return \"TEXT[]\";\n if (colType === \"integer[]\") return \"INTEGER[]\";\n if (colType === \"boolean[]\") return \"BOOLEAN[]\";\n if (colType === \"numeric[]\") return \"NUMERIC[]\";\n return \"JSONB\";\n }\n case \"vector\": {\n const vp = prop as VectorProperty;\n return `VECTOR(${vp.dimensions})`;\n }\n case \"binary\": {\n return \"BYTEA\";\n }\n case \"relation\": {\n const refProp = prop as RelationProperty;\n const resolvedRelations = resolveCollectionRelations(collection);\n const relation = findRelation(resolvedRelations, refProp.relation?.relationName ?? propName);\n if (relation?.kind !== \"belongsTo\") {\n throw new Error(`Relation ${propName} does not put a column on this table (only \\`belongsTo\\` does)`);\n }\n let targetCollection: CollectionConfig;\n try {\n targetCollection = relation.target();\n } catch {\n return \"TEXT\";\n }\n const pkProp = getPrimaryKeyProp(targetCollection);\n return pkProp.type === \"number\" ? \"INTEGER\" : (pkProp.isUuid ? \"UUID\" : \"TEXT\");\n }\n case \"reference\": {\n const refProp = prop as ReferenceProperty;\n const targetCollection = collections.find(c => c.slug === refProp.path || getTableName(c) === refProp.path);\n if (!targetCollection) return \"TEXT\";\n const pkProp = getPrimaryKeyProp(targetCollection);\n return pkProp.type === \"number\" ? \"INTEGER\" : (pkProp.isUuid ? \"UUID\" : \"TEXT\");\n }\n default:\n return \"TEXT\";\n }\n};\n\nexport const generatePostgresDdl = async (\n allCollections: CollectionConfig[],\n options: { includePolicies?: boolean } = { includePolicies: true }\n): Promise<string> => {\n // Only the collections this engine stores. See `relationalCollections`.\n const collections = relationalCollections(allCollections);\n let ddl = \"-- This file is auto-generated by the Rebase DDL generator. Do not edit manually.\\n\\n\";\n\n // 1. Create 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. It used to appear here only when some collection\n // happened to declare `schema: \"rebase\"`; the scaffold's users collection\n // does, which is why nobody hit it, but nothing guaranteed it.\n const uniqueSchemas = Array.from(new Set([\n REBASE_SCHEMA,\n ...collections.map(c => isPostgresCollectionConfig(c) ? c.schema : undefined).filter(Boolean)\n ]));\n uniqueSchemas.forEach(schema => {\n if (schema) ddl += `CREATE SCHEMA IF NOT EXISTS \"${schema}\";\\n`;\n });\n if (uniqueSchemas.length > 0) ddl += \"\\n\";\n\n // 2. Generate Enums\n //\n // The enum type name is derived from table + column, so two collections\n // mapped onto the same table — or two properties whose `columnName`\n // resolves to the same column — land on the same name. `CREATE TYPE` has no\n // IF NOT EXISTS, so emitting it twice aborts the whole file on the second\n // statement. Dedupe by name, matching planCollectionSchemaEnsure.\n const emittedEnums = new Set<string>();\n collections.forEach(collection => {\n const collectionTable = getTableName(collection);\n const schema = isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\";\n Object.entries(collection.properties ?? {}).forEach(([propName, prop]) => {\n if ((\"enum\" in prop) && (prop.type === \"string\" || prop.type === \"number\") && prop.enum) {\n const enumDbName = `${collectionTable}_${resolveColumnName(propName, prop)}`;\n const values = Array.isArray(prop.enum)\n ? (prop.enum as (string | number | { id: string | number })[]).map((v: string | number | { id: string | number }) =>\n String(typeof v === \"object\" && v !== null && \"id\" in v ? v.id : v)\n )\n : Object.keys(prop.enum);\n if (values.length > 0 && !emittedEnums.has(`${schema}.${enumDbName}`)) {\n emittedEnums.add(`${schema}.${enumDbName}`);\n ddl += `CREATE TYPE \"${schema}\".\"${enumDbName}\" AS ENUM (${values.map(quoteSqlLiteral).join(\", \")});\\n`;\n }\n }\n });\n });\n if (ddl.endsWith(\";\\n\")) ddl += \"\\n\";\n\n // Junction policy derivation needs every declaring side of each junction,\n // not just the first relation that reached it in the walk below.\n const junctionSpecs = resolveJunctionSpecs(collections);\n\n const allTablesToGenerate = new Map<string, {\n collection: CollectionConfig,\n isJunction?: boolean,\n relation?: ResolvedRelation,\n sourceCollection?: CollectionConfig\n }>();\n\n // Identify all tables\n for (const collection of collections) {\n const tableName = getTableName(collection);\n if (tableName) {\n allTablesToGenerate.set(tableName, { collection });\n }\n\n const resolvedRelations = resolveCollectionRelations(collection);\n for (const relation of Object.values(resolvedRelations)) {\n if (isManyToMany(relation)) {\n const junctionTableName = relation.through.table;\n if (!allTablesToGenerate.has(junctionTableName)) {\n allTablesToGenerate.set(junctionTableName, {\n collection: {\n table: junctionTableName,\n properties: {}\n } as CollectionConfig,\n isJunction: true,\n relation: relation,\n sourceCollection: collection\n });\n }\n }\n }\n }\n\n // 3. Generate tables\n const fkStatements: string[] = [];\n // Policies are emitted after every CREATE TABLE, like the FK constraints:\n // a 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 for (const [tableName, {\n collection,\n isJunction,\n relation,\n sourceCollection\n }] of allTablesToGenerate.entries()) {\n const schema = isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\";\n const baseTableName = tableName.includes(\".\") ? tableName.split(\".\").pop()! : tableName;\n\n if (isJunction && relation && sourceCollection && isManyToMany(relation)) {\n const targetCollection = relation.target();\n const sourceTable = getTableName(sourceCollection);\n const targetTable = getTableName(targetCollection);\n const sourceSchema = isPostgresCollectionConfig(sourceCollection) && sourceCollection.schema ? sourceCollection.schema : \"public\";\n const targetSchema = isPostgresCollectionConfig(targetCollection) && targetCollection.schema ? targetCollection.schema : \"public\";\n const { sourceColumn, targetColumn } = relation.through;\n\n // TEXT, matching the string default: a junction column has to have the\n // same type as the primary key it references.\n const sourceColType = isNumericId(sourceCollection) ? \"INTEGER\" : (getPrimaryKeyProp(sourceCollection).isUuid ? \"UUID\" : \"TEXT\");\n const targetColType = isNumericId(targetCollection) ? \"INTEGER\" : (getPrimaryKeyProp(targetCollection).isUuid ? \"UUID\" : \"TEXT\");\n const sourceId = getPrimaryKeyName(sourceCollection);\n const targetId = getPrimaryKeyName(targetCollection);\n\n const onDelete = relation.onDelete ?? \"CASCADE\";\n\n ddl += `CREATE TABLE \"${schema}\".\"${baseTableName}\" (\\n`;\n ddl += ` \"${sourceColumn}\" ${sourceColType} NOT NULL,\\n`;\n ddl += ` \"${targetColumn}\" ${targetColType} NOT NULL,\\n`;\n ddl += ` PRIMARY KEY (\"${sourceColumn}\", \"${targetColumn}\")\\n`;\n ddl += `);\\n\\n`;\n\n fkStatements.push(`ALTER TABLE \"${schema}\".\"${baseTableName}\" ADD CONSTRAINT \"${toPostgresIdentifier(`${baseTableName}_${sourceColumn}_fkey`)}\" FOREIGN KEY (\"${sourceColumn}\") REFERENCES \"${sourceSchema}\".\"${sourceTable}\" (\"${sourceId}\") ON DELETE ${onDelete.toUpperCase()};`);\n fkStatements.push(`ALTER TABLE \"${schema}\".\"${baseTableName}\" ADD CONSTRAINT \"${toPostgresIdentifier(`${baseTableName}_${targetColumn}_fkey`)}\" FOREIGN KEY (\"${targetColumn}\") REFERENCES \"${targetSchema}\".\"${targetTable}\" (\"${targetId}\") ON DELETE ${onDelete.toUpperCase()};`);\n\n if (options.includePolicies) {\n // Junction tables are generated tables like any other: locked by\n // default, with derived policies — reads follow the endpoints'\n // visibility, writes follow the declaring side's update rules.\n // Without this they were the one kind of generated table with no\n // RLS at all, readable and writable by every signed-in user.\n ddl += `ALTER TABLE \"${schema}\".\"${baseTableName}\" ENABLE ROW LEVEL SECURITY;\\n`;\n ddl += `\\n`;\n\n const spec = junctionSpecs.get(baseTableName);\n if (spec) {\n const junctionCollection = getJunctionCollectionConfig(spec);\n const resolveCollection: ResolveCollection = (slug) => collections.find(c => c.slug === slug || getTableName(c) === slug);\n getJunctionSecurityRules(spec).forEach((rule: SecurityRule) => {\n policyStatements.push(generatePolicyDdl(junctionCollection, rule, resolveCollection));\n });\n }\n }\n } else if (!isJunction) {\n ddl += `CREATE TABLE \"${schema}\".\"${baseTableName}\" (\\n`;\n const columns: string[] = [];\n\n Object.entries(collection.properties ?? {}).forEach(([propName, prop]) => {\n if (prop.type === \"relation\") {\n const refProp = prop as RelationProperty;\n const resolvedRelations = resolveCollectionRelations(collection);\n const relInfo = findRelation(resolvedRelations, refProp.relation?.relationName ?? propName);\n\n if (relInfo?.kind !== \"belongsTo\") {\n return;\n }\n\n if (collection.properties[relInfo.localKey] && propName !== relInfo.localKey) {\n return;\n }\n\n let targetCollection: CollectionConfig;\n try {\n targetCollection = relInfo.target();\n } catch {\n return;\n }\n\n const targetTable = getTableName(targetCollection);\n const targetSchema = isPostgresCollectionConfig(targetCollection) && targetCollection.schema ? targetCollection.schema : \"public\";\n const targetId = getPrimaryKeyName(targetCollection);\n const fkColType = getSqlColumnType(propName, prop, collection, collections);\n \n const onUpdate = relInfo.onUpdate ? ` ON UPDATE ${relInfo.onUpdate.toUpperCase()}` : \"\";\n const required = prop.validation?.required;\n const onDeleteVal = relInfo.onDelete ?? (required ? \"CASCADE\" : \"SET NULL\");\n \n let colDef = ` \"${relInfo.localKey}\" ${fkColType}`;\n if (required) colDef += \" NOT NULL\";\n columns.push(colDef);\n\n fkStatements.push(`ALTER TABLE \"${schema}\".\"${baseTableName}\" ADD CONSTRAINT \"${toPostgresIdentifier(`${baseTableName}_${relInfo.localKey}_fkey`)}\" FOREIGN KEY (\"${relInfo.localKey}\") REFERENCES \"${targetSchema}\".\"${targetTable}\" (\"${targetId}\") ON DELETE ${onDeleteVal.toUpperCase()}${onUpdate};`);\n } else if (prop.type === \"reference\") {\n const refProp = prop as ReferenceProperty;\n const targetCollection = collections.find(c => c.slug === refProp.path || getTableName(c) === refProp.path);\n const colName = resolveColumnName(propName, prop);\n const colType = getSqlColumnType(propName, prop, collection, collections);\n const required = prop.validation?.required;\n\n if (!targetCollection) {\n let colDef = ` \"${colName}\" ${colType}`;\n if (required) colDef += \" NOT NULL\";\n columns.push(colDef);\n } else {\n const targetTable = getTableName(targetCollection);\n const targetSchema = isPostgresCollectionConfig(targetCollection) && targetCollection.schema ? targetCollection.schema : \"public\";\n const targetId = getPrimaryKeyName(targetCollection);\n const onDelete = required ? \"CASCADE\" : \"SET NULL\";\n\n let colDef = ` \"${colName}\" ${colType}`;\n if (required) colDef += \" NOT NULL\";\n columns.push(colDef);\n\n fkStatements.push(`ALTER TABLE \"${schema}\".\"${baseTableName}\" ADD CONSTRAINT \"${toPostgresIdentifier(`${baseTableName}_${colName}_fkey`)}\" FOREIGN KEY (\"${colName}\") REFERENCES \"${targetSchema}\".\"${targetTable}\" (\"${targetId}\") ON DELETE ${onDelete.toUpperCase()};`);\n }\n } else {\n const colName = resolveColumnName(propName, prop);\n\n // On an auth collection, the columns auth reads and writes\n // are defined once, in `auth-users-columns`, and every\n // creator of this table emits that definition. Before this,\n // the desired state here was built from the collection file\n // alone — which knows nothing of `is_anonymous` or\n // `tokens_valid_after` — so `db push` and the runtime\n // disagreed about `email`'s nullability and about which\n // columns exist at all.\n const authDefinition = isAuthCollection(collection)\n ? authUsersColumnDefinition(colName)\n : undefined;\n if (authDefinition && !isIdProperty(propName, prop, collection)) {\n columns.push(` \"${colName}\" ${authDefinition}`);\n return;\n }\n\n const colType = getSqlColumnType(propName, prop, collection, collections);\n let colDef = ` \"${colName}\" ${colType}`;\n\n if (isIdProperty(propName, prop, collection)) {\n colDef += \" PRIMARY KEY\";\n }\n\n if (\"isId\" in prop && prop.isId !== \"manual\" && prop.isId !== true && prop.isId !== \"increment\") {\n if (prop.isId === \"uuid\") {\n colDef += \" DEFAULT gen_random_uuid()\";\n } else if (prop.isId === \"cuid\") {\n colDef += \" DEFAULT cuid()\";\n } else if (typeof prop.isId === \"string\") {\n colDef += ` DEFAULT ${prop.isId}`;\n }\n }\n\n if (!isIdProperty(propName, prop, collection) && prop.validation?.unique) {\n colDef += \" UNIQUE\";\n }\n\n if (prop.type === \"date\") {\n const dateProp = prop as DateProperty;\n if (dateProp.autoValue === \"on_create\" || dateProp.autoValue === \"on_update\") {\n colDef += \" DEFAULT now()\";\n }\n }\n\n if (prop.validation?.required && !colDef.includes(\"PRIMARY KEY\")) {\n colDef += \" NOT NULL\";\n }\n\n columns.push(colDef);\n }\n });\n\n // ── Auth columns the collection file never mentions ──────────────\n // `db push` is declarative: Atlas diffs the database against exactly\n // what is emitted here and drops anything else. The scaffold's users\n // collection describes 12 columns while auth needs 14, so\n // `is_anonymous` and `tokens_valid_after` — created at boot by\n // `ensureAuthTablesExist` — read as unmanaged drift, and a push run\n // after the server had started once planned to DROP them. The\n // destructive gate catches it, which makes it a data-loss prompt on\n // the auth table rather than silent damage; either way the desired\n // state was wrong. Emit the full contract so the two agree.\n if (isAuthCollection(collection)) {\n const declared = new Set(\n Object.entries(collection.properties ?? {})\n .map(([name, prop]) => resolveColumnName(name, prop))\n );\n for (const spec of AUTH_USERS_COLUMNS) {\n if (declared.has(spec.column)) continue;\n columns.push(` \"${spec.column}\" ${authUsersColumnSql(spec)}`);\n }\n }\n\n // Backwards compatibility: add default id primary key if missing\n const hasPk = columns.some(c => c.includes(\"PRIMARY KEY\"));\n if (!hasPk) {\n columns.unshift(' \"id\" TEXT PRIMARY KEY');\n }\n\n ddl += columns.join(\",\\n\");\n ddl += `\\n);\\n\\n`;\n\n if (options.includePolicies) {\n // Enable RLS and add Policies. No FORCE: authenticated requests\n // run as the non-owner `rebase_user` role, which plain ENABLE\n // already binds. The owner (server context) must bypass — it is\n // the trusted plane (auth flows, dataAsAdmin).\n ddl += `ALTER TABLE \"${schema}\".\"${baseTableName}\" ENABLE ROW LEVEL SECURITY;\\n`;\n ddl += `\\n`;\n\n const securityRules = getEffectiveSecurityRules(collection);\n if (securityRules.length > 0) {\n const resolveCollection: ResolveCollection = (slug) => collections.find(c => c.slug === slug || getTableName(c) === slug);\n securityRules.forEach((rule: SecurityRule) => {\n policyStatements.push(generatePolicyDdl(collection, rule, resolveCollection));\n });\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/** The RLS statements one declared collection's table needs, ready to run. */\n/**\n * A foreign key, as both its parts and the statement that creates it.\n *\n * `ALTER TABLE … ADD CONSTRAINT` has no `IF NOT EXISTS`, so a caller applying\n * these has to skip by name — hence the name is a field and not only a substring\n * of the SQL.\n */\nexport interface ForeignKeyPlan {\n constraintName: string;\n schema: string;\n /** Bare table name, no schema prefix. */\n table: string;\n column: string;\n targetSchema: string;\n targetTable: string;\n targetColumn: string;\n sql: string;\n}\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 generator declares it. */\n type: string;\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 — set only when the two differ and the column name\n * is the derived default rather than one the author wrote.\n *\n * Carried so the boot-time ensure can notice a database provisioned under\n * the old rule. It is 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 schemaOfCollection = (collection: CollectionConfig): string =>\n isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\";\n\nconst bareTableName = (name: string): string => (name.includes(\".\") ? name.split(\".\").pop()! : name);\n\n/**\n * Truncate a derived identifier the way Postgres does: to 63 bytes, silently.\n *\n * This is not cosmetic, and not really a naming choice at all — it is agreeing\n * with the name the database ALREADY stored. `ADD CONSTRAINT` on a longer name\n * succeeds and records the truncated form, so the untruncated name this used to\n * derive matched nothing in the catalogue. Boot-ensure compares its planned\n * constraints against `readExistingSchema`, which reads catalogue names, so the\n * comparison could never hit: every boot re-issued `ADD CONSTRAINT` for the same\n * constraint, forever, and got \"already exists\" every time. Non-fatal (foreign\n * keys are the one action allowed to fail) and therefore permanent — an error in\n * the log on every restart of a project whose table and column names happened to\n * be long.\n *\n * Byte length, not string length: NAMEDATALEN is 64 bytes, and a multi-byte\n * character straddling the boundary would be cut mid-sequence by `slice(0, 63)`.\n */\nconst toPostgresIdentifier = (name: string): string => {\n const bytes = Buffer.from(name, \"utf8\");\n if (bytes.byteLength <= 63) return name;\n // `toString` on a slice that ends mid-character yields U+FFFD; dropping it\n // lands on the last whole character that fits, which is what Postgres does.\n return bytes.subarray(0, 63).toString(\"utf8\").replace(/�+$/, \"\");\n};\n\nconst foreignKeyPlan = (\n args: Omit<ForeignKeyPlan, \"constraintName\" | \"sql\"> & { onDelete: string; onUpdate?: string }\n): ForeignKeyPlan => {\n const constraintName = toPostgresIdentifier(`${args.table}_${args.column}_fkey`);\n const onUpdate = args.onUpdate ? ` ON UPDATE ${args.onUpdate.toUpperCase()}` : \"\";\n return {\n constraintName,\n schema: args.schema,\n table: args.table,\n column: args.column,\n targetSchema: args.targetSchema,\n targetTable: args.targetTable,\n targetColumn: args.targetColumn,\n sql:\n `ALTER TABLE \"${args.schema}\".\"${args.table}\" ADD CONSTRAINT \"${constraintName}\" ` +\n `FOREIGN KEY (\"${args.column}\") REFERENCES \"${args.targetSchema}\".\"${args.targetTable}\" ` +\n `(\"${args.targetColumn}\") ON DELETE ${args.onDelete.toUpperCase()}${onUpdate};`\n };\n};\n\n/**\n * The FK columns the declared collections own — one entry per `relation`\n * (`belongsTo` side) or `reference` property.\n *\n * Split out of {@link generatePostgresDdl} so the boot-time schema ensure can\n * create the same columns with the same names, types and constraints. Before\n * this it skipped them outright, which was survivable only because `db push`\n * always followed; on a managed tenant nothing follows, so a table arrived\n * without the column its own collection reads and wrote 400 on every insert.\n *\n * A relation whose target is not in the bundle yields no column at all (the\n * generator returns early on an unresolvable target); a `reference` whose target\n * is unknown yields the column without a constraint. Both mirror the generator\n * exactly — a divergence here is a schema fork between boot and `db push`.\n */\nexport const planRelationalColumns = (allCollections: CollectionConfig[]): RelationalColumnPlan[] => {\n const collections = relationalCollections(allCollections);\n const plans: RelationalColumnPlan[] = [];\n\n for (const collection of collections) {\n const tableName = getTableName(collection);\n if (!tableName) continue;\n const schema = schemaOfCollection(collection);\n const table = bareTableName(tableName);\n\n for (const [propName, rawProp] of Object.entries(collection.properties ?? {})) {\n const prop = rawProp as Property;\n\n if (prop.type === \"relation\") {\n const refProp = prop as RelationProperty;\n const resolvedRelations = resolveCollectionRelations(collection);\n const relInfo = findRelation(resolvedRelations, refProp.relation?.relationName ?? propName);\n if (relInfo?.kind !== \"belongsTo\") continue;\n // The relation and an explicit FK property can both be declared;\n // the explicit one owns the column.\n if (collection.properties[relInfo.localKey] && propName !== relInfo.localKey) continue;\n\n let targetCollection: CollectionConfig;\n try {\n targetCollection = relInfo.target();\n } catch {\n continue;\n }\n if (!targetCollection) continue;\n\n const required = prop.validation?.required;\n // Only a *derived* column name can be affected by the change to\n // `generateForeignKeyName`; one the author wrote is theirs.\n const relationName = refProp.relation?.relationName ?? propName;\n const legacyKey = legacyForeignKeyName(relationName);\n const derived = relInfo.localKey === generateForeignKeyName(relationName);\n plans.push({\n schema,\n table,\n column: relInfo.localKey,\n legacyColumn: derived && legacyKey !== relInfo.localKey ? legacyKey : undefined,\n type: getSqlColumnType(propName, prop, collection, collections),\n foreignKey: foreignKeyPlan({\n schema,\n table,\n column: relInfo.localKey,\n targetSchema: schemaOfCollection(targetCollection),\n targetTable: bareTableName(getTableName(targetCollection)),\n targetColumn: getPrimaryKeyName(targetCollection),\n onDelete: relInfo.onDelete ?? (required ? \"CASCADE\" : \"SET NULL\"),\n onUpdate: relInfo.onUpdate\n })\n });\n } else if (prop.type === \"reference\") {\n const refProp = prop as ReferenceProperty;\n const targetCollection = collections.find(\n c => c.slug === refProp.path || getTableName(c) === refProp.path\n );\n const column = resolveColumnName(propName, prop);\n const type = getSqlColumnType(propName, prop, collection, collections);\n const required = prop.validation?.required;\n\n plans.push({\n schema,\n table,\n column,\n type,\n foreignKey: targetCollection\n ? foreignKeyPlan({\n schema,\n table,\n column,\n targetSchema: schemaOfCollection(targetCollection),\n targetTable: bareTableName(getTableName(targetCollection)),\n targetColumn: getPrimaryKeyName(targetCollection),\n onDelete: required ? \"CASCADE\" : \"SET NULL\"\n })\n : undefined\n });\n }\n }\n }\n\n return plans;\n};\n\n/**\n * The junction tables a bundle's many-to-many relations imply.\n *\n * Derived from {@link resolveJunctionSpecs}, the same source the junction RLS\n * comes from, so a table created here always has policies planned for it — a\n * junction with row-level security left off is readable and writable by every\n * signed-in user, which is why the two must ship together.\n */\nexport const planJunctionTables = (allCollections: CollectionConfig[]): JunctionTablePlan[] => {\n const collections = relationalCollections(allCollections);\n const plans: JunctionTablePlan[] = [];\n\n for (const spec of resolveJunctionSpecs(collections).values()) {\n const [source, target] = spec.endpoints;\n // A junction column's default name is derived from the endpoint\n // collection's slug — which is normally plural, so this is where the\n // singularization change actually lands: a `categories` endpoint used\n // to give `categorie_id` and now gives `category_id`. Recorded, not\n // used, so boot-ensure can recognise a table built under the old rule.\n const legacyFor = (endpoint: typeof source): string | undefined => {\n const slug = toSnakeCase(endpoint.collection.slug ?? endpoint.collection.name ?? \"\");\n const legacy = legacyForeignKeyName(slug);\n const derived = endpoint.junctionColumn === generateForeignKeyName(slug);\n return derived && legacy !== endpoint.junctionColumn ? legacy : undefined;\n };\n const columns = [\n { name: source.junctionColumn, type: junctionKeyType(source.collection), legacyName: legacyFor(source) },\n { name: target.junctionColumn, type: junctionKeyType(target.collection), legacyName: legacyFor(target) }\n ];\n // Every declaring side agrees on the edge's lifetime; the first one wins,\n // as it does in the generator's walk.\n const onDelete = spec.declaringSides[0]?.relation.onDelete ?? \"CASCADE\";\n\n plans.push({\n schema: spec.schema,\n table: spec.table,\n columns,\n createTable:\n `CREATE TABLE IF NOT EXISTS \"${spec.schema}\".\"${spec.table}\" (` +\n columns.map(c => `\"${c.name}\" ${c.type} NOT NULL`).join(\", \") +\n `, PRIMARY KEY (${columns.map(c => `\"${c.name}\"`).join(\", \")}));`,\n foreignKeys: [source, target].map((endpoint, i) =>\n foreignKeyPlan({\n schema: spec.schema,\n table: spec.table,\n column: columns[i].name,\n targetSchema: schemaOfCollection(endpoint.collection),\n targetTable: bareTableName(getTableName(endpoint.collection)),\n targetColumn: getPrimaryKeyName(endpoint.collection),\n onDelete\n })\n )\n });\n }\n\n return plans;\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, as executable\n * statements — what the managed runtime applies at boot so a freshly\n * provisioned tenant database serves data instead of 401ing every read.\n *\n * Mirrors {@link generatePostgresPoliciesDdl} exactly (same\n * `generatePolicyStatements`, same enable-RLS, same effective rules, same\n * derived junction rules), so boot and `db push` produce identical policies from\n * identical collections.\n *\n * Junction tables are included, and have to be: boot creates them now\n * ({@link planJunctionTables}), and a junction with RLS left off is readable and\n * writable by every signed-in user. A junction whose table is still absent is\n * skipped by the applier, not planned away here.\n */\nexport const planCollectionPolicies = (allCollections: CollectionConfig[]): CollectionPolicyPlan[] => {\n // A store with no RLS gets no policies planned for it — `supportsRLS` is\n // false for exactly the engines `relationalCollections` filters out.\n const collections = relationalCollections(allCollections);\n const resolveCollection: ResolveCollection = (slug) => collections.find(c => c.slug === slug || getTableName(c) === slug);\n const plans: CollectionPolicyPlan[] = [];\n const seen = new Set<string>();\n\n for (const collection of collections) {\n const tableName = getTableName(collection);\n if (!tableName) continue;\n const schema = isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\";\n const baseTableName = tableName.includes(\".\") ? tableName.split(\".\").pop()! : tableName;\n const qualified = `${schema}.${baseTableName}`;\n if (seen.has(qualified)) continue;\n seen.add(qualified);\n\n const policyStatements: string[] = [];\n for (const rule of getEffectiveSecurityRules(collection)) {\n policyStatements.push(...generatePolicyStatements(collection, rule, resolveCollection));\n }\n\n plans.push({\n schema,\n table: baseTableName,\n qualified,\n enableRls: `ALTER TABLE \"${schema}\".\"${baseTableName}\" ENABLE ROW LEVEL SECURITY;`,\n policyStatements\n });\n }\n\n // Junctions are derived from `through` relations rather than declared, so\n // the walk above never sees them.\n for (const spec of resolveJunctionSpecs(collections).values()) {\n const qualified = `${spec.schema}.${spec.table}`;\n if (seen.has(qualified)) continue;\n seen.add(qualified);\n\n const junctionCollection = getJunctionCollectionConfig(spec);\n const policyStatements: string[] = [];\n for (const rule of getJunctionSecurityRules(spec)) {\n policyStatements.push(...generatePolicyStatements(junctionCollection, rule, resolveCollection));\n }\n\n plans.push({\n schema: spec.schema,\n table: spec.table,\n qualified,\n enableRls: `ALTER TABLE \"${spec.schema}\".\"${spec.table}\" ENABLE ROW LEVEL SECURITY;`,\n policyStatements\n });\n }\n\n return plans;\n};\n\nexport const generatePostgresPoliciesDdl = (allCollections: CollectionConfig[]): string => {\n // Also the expectation `checkPolicyDrift` reconciles the database against,\n // so a non-SQL collection filtered out here is one the drift report cannot\n // invent a missing policy for.\n const collections = relationalCollections(allCollections);\n let ddl = \"-- This file contains RLS policies generated by Rebase. Applied separately from migrations.\\n\\n\";\n\n const allTablesToGenerate = new Map<string, {\n collection: CollectionConfig\n }>();\n\n for (const collection of collections) {\n const tableName = getTableName(collection);\n if (tableName) {\n allTablesToGenerate.set(tableName, { collection });\n }\n }\n\n for (const [tableName, { collection }] of allTablesToGenerate.entries()) {\n const schema = isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\";\n const baseTableName = tableName.includes(\".\") ? tableName.split(\".\").pop()! : tableName;\n\n // No FORCE: user requests run as the non-owner `rebase_user` role\n // (plain ENABLE binds them); the owner is the trusted server context.\n ddl += `ALTER TABLE \"${schema}\".\"${baseTableName}\" ENABLE ROW LEVEL SECURITY;\\n`;\n ddl += `\\n`;\n\n const securityRules = getEffectiveSecurityRules(collection);\n if (securityRules.length > 0) {\n const resolveCollection: ResolveCollection = (slug) => collections.find(c => c.slug === slug || getTableName(c) === slug);\n const injectedNames = new Set(getInjectedSecurityRules(collection).map((rule) => rule.name));\n\n securityRules.forEach((rule: SecurityRule) => {\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.\n if (rule.name && injectedNames.has(rule.name)) {\n ddl += `-- Injected by Rebase (not from this collection's securityRules).\\n`;\n ddl += `-- Set \\`disableDefaultPolicies: true\\` on \"${collection.slug}\" to drop these and own its RLS outright.\\n`;\n }\n ddl += generatePolicyDdl(collection, rule, resolveCollection);\n });\n ddl += \"\\n\";\n }\n }\n\n // Junction tables are generated from `through` relations, not declared as\n // collections, so the walk above never sees them. They get the same\n // treatment as any generated table: locked by default, with derived\n // policies — reads follow the endpoints, writes follow the declaring\n // side's update rules.\n const junctionSpecs = resolveJunctionSpecs(collections);\n for (const spec of junctionSpecs.values()) {\n ddl += `ALTER TABLE \"${spec.schema}\".\"${spec.table}\" ENABLE ROW LEVEL SECURITY;\\n`;\n ddl += `\\n`;\n\n const junctionRules = getJunctionSecurityRules(spec);\n if (junctionRules.length === 0) continue;\n\n const junctionCollection = getJunctionCollectionConfig(spec);\n const resolveCollection: ResolveCollection = (slug) => collections.find(c => c.slug === slug || getTableName(c) === slug);\n const declaringSlugs = spec.declaringSides.map(s => s.collection.slug).join('\", \"');\n\n ddl += `-- Derived by Rebase for the junction \"${spec.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 junctionRules.forEach((rule: SecurityRule) => {\n ddl += generatePolicyDdl(junctionCollection, rule, resolveCollection);\n });\n ddl += \"\\n\";\n }\n\n return ddl;\n};\n\n","/**\n * Bringing a database up to date with a bundle's collections, additively.\n *\n * ## Why this exists\n *\n * A managed runtime boots someone else's compiled project against a database it\n * has never seen. Auth tables are ensured at boot already, but collection tables\n * were not created by anything: the platform ran the app and every `/api/data/*`\n * request answered 500 on a missing relation. `rebase db push` cannot help — it\n * is an Atlas-driven CLI command, and the runtime image ships no CLI.\n *\n * ## Why additive-only, forever\n *\n * This runs unattended, against a database with customers' data in it, with no\n * human reading a diff. So it may only ever do things that cannot lose data:\n * create a missing table, add a missing column, create a missing enum type.\n *\n * It will **never** drop a table or a column, narrow a type, or alter a\n * constraint. A removed field leaves its column behind; a renamed field looks\n * like an addition and the old column stays. That is the correct trade for an\n * automated path — the alternative is an unattended process that can silently\n * destroy a column, which is precisely the failure `db push` was hardened\n * against. Destructive changes stay a deliberate, human-reviewed migration.\n *\n * Because of that, this is safe to run on every boot, and re-running it is a\n * no-op.\n */\nimport { type CollectionConfig, type Property, isPostgresCollectionConfig } from \"@rebasepro/types\";\nimport { getTableName, relationalCollections } from \"@rebasepro/common\";\nimport { logger } from \"@rebasepro/server\";\nimport {\n getSqlColumnType,\n resolveColumnName,\n isIdProperty,\n planRelationalColumns,\n planJunctionTables,\n quoteSqlLiteral\n} from \"./generate-postgres-ddl-logic\";\nimport {\n AUTH_USERS_COLUMNS,\n authUsersColumnDefinition,\n authUsersColumnSql,\n isAuthCollection\n} from \"./auth-users-columns\";\n\n/**\n * The subset of a database handle this needs: run a statement, get rows back.\n *\n * Deliberately parameterless. Everything here is DDL or catalogue reads keyed by\n * schema name, and schema names are identifiers — they cannot be bound as\n * parameters anyway. They are validated against {@link SAFE_IDENTIFIER} before\n * they reach a statement, so a config that somehow carried a quote is refused\n * rather than concatenated.\n */\nexport interface Queryable {\n query<T = unknown>(sql: string): Promise<{ rows: T[] }>;\n}\n\n/** Postgres identifiers this module is willing to interpolate. */\nconst SAFE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_$]*$/;\n\nfunction assertSafeIdentifier(value: string, what: string): string {\n if (!SAFE_IDENTIFIER.test(value)) {\n throw new Error(`Refusing to build SQL with an unsafe ${what}: ${JSON.stringify(value)}`);\n }\n return value;\n}\n\n/** What the database currently has, as the planner needs it. */\nexport interface ExistingSchema {\n /** `schema.table` → set of column names. */\n tables: Map<string, Set<string>>;\n /** `schema.typename` of every enum type that already exists. */\n enums: Set<string>;\n /**\n * `schema.table.constraint` of every constraint that already exists.\n *\n * Optional so a caller that only cares about tables can still build one by\n * hand; absent is read as \"none known\", which at worst re-attempts a\n * constraint that then fails harmlessly as a duplicate.\n */\n constraints?: Set<string>;\n}\n\nexport interface EnsureAction {\n kind: \"create-enum\" | \"create-table\" | \"add-column\" | \"add-constraint\" | \"rename-column\";\n /** Qualified target, for logging: `public.posts` or `public.posts.title`. */\n target: string;\n sql: string;\n}\n\nexport interface EnsurePlan {\n actions: EnsureAction[];\n /** Every statement, in dependency order. Empty when the schema is current. */\n statements: string[];\n /**\n * Relation columns this plan is about to create where the table already\n * carries the same column under its pre-singularization name.\n *\n * The reason this is reported rather than silently handled: the ensure is\n * additive, so it would add `category_id` beside a populated\n * `categorie_id` and the relation would then read the new, empty one. No\n * statement fails, no table is missing, and the only symptom is relations\n * resolving to nothing — which is indistinguishable from having no data.\n */\n legacyForeignKeys: LegacyForeignKey[];\n}\n\n/** A relation column whose old and new spellings both plausibly apply. */\nexport interface LegacyForeignKey {\n /** `schema.table`. */\n table: string;\n /** The name the current rule derives, and what this plan would create. */\n expected: string;\n /** The name the old rule derived, which the table already has. */\n legacy: string;\n}\n\nexport interface EnsureOutcome extends EnsurePlan {\n /**\n * Constraints that could not be added — always non-fatal.\n *\n * A foreign key can only fail on data that already violates it, and the\n * column it would police exists either way, so the collection still serves.\n * Refusing to boot over one would turn a pre-existing data problem into an\n * outage. Reported loudly instead.\n */\n failures: { target: string; error: string }[];\n}\n\nfunction schemaOf(collection: CollectionConfig): string {\n return isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\";\n}\n\nfunction qualified(collection: CollectionConfig): string {\n return `${schemaOf(collection)}.${getTableName(collection)}`;\n}\n\n/**\n * Enum types a collection's properties require, as `schema.typename`.\n *\n * Named exactly as the DDL generator names them (`<table>_<column>`), because\n * a column added here has to reference the same type the generator would have\n * created — a second, differently-named type for the same field would be a\n * silent schema fork.\n */\nfunction requiredEnums(collection: CollectionConfig): { name: string; values: string[] }[] {\n const table = getTableName(collection);\n const schema = schemaOf(collection);\n const out: { name: string; values: string[] }[] = [];\n for (const [propName, prop] of Object.entries(collection.properties ?? {})) {\n const p = prop as Property;\n if (!(\"enum\" in p) || !p.enum) continue;\n if (p.type !== \"string\" && p.type !== \"number\") continue;\n const values = (p.enum as unknown[])\n .map(entry =>\n entry && typeof entry === \"object\" && \"id\" in (entry as Record<string, unknown>)\n ? String((entry as Record<string, unknown>).id)\n : String(entry)\n )\n .filter(v => v.length > 0);\n if (values.length === 0) continue;\n out.push({ name: `${schema}.${table}_${resolveColumnName(propName, p)}`, values });\n }\n return out;\n}\n\n/**\n * Decide what to add. Pure — the caller supplies what exists and runs the result.\n *\n * Ordering matters and is deliberate: enum types before the tables and columns\n * that reference them, tables before the columns added to other tables (a new\n * table may be the target of a relation), and nothing is emitted twice.\n */\nexport function planCollectionSchemaEnsure(\n allCollections: CollectionConfig[],\n existing: ExistingSchema\n): EnsurePlan {\n // Boot receives every collection the bundle declares, including the ones\n // served by another engine entirely. Creating a Postgres table for a\n // Firestore collection is not a harmless extra: the app keeps reading\n // documents from Firestore while an empty table with the same name accretes\n // policies and shows up in every drift report.\n const collections = relationalCollections(allCollections);\n const actions: EnsureAction[] = [];\n const plannedEnums = new Set<string>();\n\n // 1. Enum types. `CREATE TYPE` has no IF NOT EXISTS, so an existing type is\n // skipped by name rather than guarded in SQL.\n for (const collection of collections) {\n for (const { name, values } of requiredEnums(collection)) {\n if (existing.enums.has(name) || plannedEnums.has(name)) continue;\n plannedEnums.add(name);\n const [schema, typeName] = name.split(\".\");\n actions.push({\n kind: \"create-enum\",\n target: name,\n sql: `CREATE TYPE \"${schema}\".\"${typeName}\" AS ENUM (${values.map(quoteSqlLiteral).join(\", \")});`\n });\n }\n }\n\n // 2. Missing tables. Only the identity column is created here; every other\n // column is added by step 3, so a new table and an existing table that\n // gained a field travel the exact same code path. One way to build a\n // column means one way for it to be wrong.\n const created = new Set<string>();\n for (const collection of collections) {\n const key = qualified(collection);\n if (existing.tables.has(key) || created.has(key)) continue;\n created.add(key);\n const schema = schemaOf(collection);\n const table = getTableName(collection);\n const idEntry = Object.entries(collection.properties ?? {}).find(([n, p]) =>\n isIdProperty(n, p as Property, collection)\n );\n const idName = idEntry ? resolveColumnName(idEntry[0], idEntry[1] as Property) : \"id\";\n const idProp = idEntry?.[1] as Property | undefined;\n // Derived through the generator's own type mapping, not re-decided here.\n // This branch used to emit BIGSERIAL for a numeric id while `db push`\n // emitted INTEGER GENERATED BY DEFAULT AS IDENTITY, so the same project\n // got an int8 key when the runtime brought the schema up and an int4 key\n // when a human pushed it. Two consequences, both real: node-postgres\n // hands back int8 as a *string*, so a collection declaring\n // `type: \"number\"` served `\"1\"` instead of `1` on the managed path only;\n // and every foreign key and junction column pointing at it stayed\n // INTEGER on both paths, which is a truncation waiting for the sequence\n // to pass 2^31. The agreement test now pins this.\n const idType = idProp\n ? getSqlColumnType(idEntry![0], idProp, collection, collections)\n : \"TEXT\";\n let idDef = `\"${idName}\" ${idType} PRIMARY KEY`;\n if (idProp?.type === \"string\" && (idProp as { isId?: unknown }).isId === \"uuid\") {\n idDef += \" DEFAULT gen_random_uuid()\";\n }\n actions.push({\n kind: \"create-table\",\n target: key,\n sql: `CREATE TABLE IF NOT EXISTS \"${schema}\".\"${table}\" (${idDef});`\n });\n }\n\n // 2b. Junction tables behind many-to-many relations. No collection declares\n // them, so the walk above never sees them — and until they existed, an\n // m2m write had nowhere to land and the junction's derived RLS had\n // nothing to attach to.\n const junctions = planJunctionTables(collections);\n for (const junction of junctions) {\n const key = `${junction.schema}.${junction.table}`;\n if (existing.tables.has(key) || created.has(key)) continue;\n created.add(key);\n actions.push({ kind: \"create-table\", target: key, sql: junction.createTable });\n }\n\n // 3. Missing columns, on both brand-new and pre-existing tables.\n const legacyForeignKeys: LegacyForeignKey[] = [];\n\n /**\n * Move a relation column that is only missing because it was renamed.\n *\n * Returns true when it handled the column, so the caller skips the ordinary\n * ADD. Adding here would be the wrong move and a quiet one: the data is in\n * the old column, `ADD COLUMN` creates the new one empty beside it, every\n * statement succeeds, and the relation reads the empty one. A rename is\n * metadata-only in Postgres, keeps the values, and carries the column's\n * indexes and constraints with it.\n *\n * Only ever reached when the new name is absent and the old name is\n * present, so there is nothing to overwrite and nothing to choose between.\n */\n const renameLegacyColumn = (\n key: string,\n schema: string,\n table: string,\n column: string,\n legacyName: string | undefined\n ): boolean => {\n const present = existing.tables.get(key);\n if (!legacyName || !present) return false;\n if (present.has(column) || !present.has(legacyName)) return false;\n\n legacyForeignKeys.push({ table: key, expected: column, legacy: legacyName });\n actions.push({\n kind: \"rename-column\",\n target: `${key}.${column}`,\n sql: `ALTER TABLE \"${schema}\".\"${table}\" RENAME COLUMN \"${legacyName}\" TO \"${column}\";`\n });\n return true;\n };\n\n const addColumn = (\n key: string,\n schema: string,\n table: string,\n column: string,\n definition: string\n ): void => {\n const present = existing.tables.get(key);\n if (present?.has(column)) return;\n actions.push({\n kind: \"add-column\",\n target: `${key}.${column}`,\n sql: `ALTER TABLE \"${schema}\".\"${table}\" ADD COLUMN IF NOT EXISTS \"${column}\" ${definition};`\n });\n };\n\n for (const collection of collections) {\n const key = qualified(collection);\n const schema = schemaOf(collection);\n const table = getTableName(collection);\n // A table this run is creating has no rows yet, so the constraints\n // `db push` writes are free to apply. On a table that already exists\n // they are not: `SET NOT NULL` is checked against live rows and a UNIQUE\n // would fail on existing duplicates, and this module runs unattended\n // against customer data with nobody reading a diff. So the constraints\n // are emitted for the fresh case — which is the whole managed-runtime\n // path, and the one that diverged from `db push` — and withheld for the\n // adopted one. `rebase db push` remains how an existing table gets them.\n const fresh = created.has(key);\n const auth = isAuthCollection(collection);\n for (const [propName, prop] of Object.entries(collection.properties ?? {})) {\n const p = prop as Property;\n if (isIdProperty(propName, p, collection)) continue;\n // Relation and reference columns are planned from the shared\n // relational planner below, which derives the column name, type and\n // foreign key the same way `db push` does. Deriving them here as\n // plain columns is what once produced a column with no constraint.\n if (p.type === \"reference\" || p.type === \"relation\") continue;\n\n const column = resolveColumnName(propName, p);\n // On an auth collection, the columns auth itself reads and writes\n // have exactly one definition, wherever the table is created from —\n // see `auth-users-columns`. Anything else on that collection is an\n // ordinary user-declared field and is generated like any other.\n const authDefinition = auth ? authUsersColumnDefinition(column) : undefined;\n if (authDefinition) {\n addColumn(key, schema, table, column, authDefinition);\n continue;\n }\n\n // Assembled in the generator's order — type, UNIQUE, DEFAULT,\n // NOT NULL — so the two produce byte-identical column definitions\n // and the agreement test can compare them directly instead of\n // checking that a column merely exists, which is how BIGSERIAL-vs-\n // INTEGER and every missing constraint went unnoticed.\n let definition = getSqlColumnType(propName, p, collection, collections);\n if (fresh && p.validation?.unique) definition += \" UNIQUE\";\n // Not gated on `fresh`: a default binds future writes only, so it is\n // safe on a live table, and a column added without it would take the\n // value the application forgot to send rather than `now()`.\n const autoValue = (p as { autoValue?: string }).autoValue;\n if (p.type === \"date\" && (autoValue === \"on_create\" || autoValue === \"on_update\")) {\n definition += \" DEFAULT now()\";\n }\n if (fresh && p.validation?.required) definition += \" NOT NULL\";\n addColumn(key, schema, table, column, definition);\n }\n\n // The auth columns the collection never mentions. The scaffold's users\n // collection describes 12 of the 14 auth reads and writes, so planning\n // only from properties left `is_anonymous` and `tokens_valid_after` to\n // `ensureAuthTablesExist` — which does create them, but only because\n // that function happens to run later in the same boot. Planning them\n // here makes this path self-contained and identical to `db push`, so\n // neither depends on the other having run.\n if (auth) {\n const declared = new Set(\n Object.entries(collection.properties ?? {})\n .map(([name, prop]) => resolveColumnName(name, prop as Property))\n );\n for (const spec of AUTH_USERS_COLUMNS) {\n if (declared.has(spec.column)) continue;\n addColumn(key, schema, table, spec.column, authUsersColumnSql(spec));\n }\n }\n }\n\n // 3b. A junction that already existed, but is short a column. One created\n // above already carries both — unlike a collection table, whose CREATE\n // declares only the identity column — so re-listing them would log two\n // no-op statements and inflate the count of changes applied.\n for (const junction of junctions) {\n const key = `${junction.schema}.${junction.table}`;\n if (created.has(key)) continue;\n for (const column of junction.columns) {\n if (renameLegacyColumn(key, junction.schema, junction.table, column.name, column.legacyName)) continue;\n addColumn(key, junction.schema, junction.table, column.name, column.type);\n }\n }\n\n // 3c. The columns relation and reference properties own.\n for (const relational of planRelationalColumns(collections)) {\n const relKey = `${relational.schema}.${relational.table}`;\n if (renameLegacyColumn(relKey, relational.schema, relational.table, relational.column, relational.legacyColumn)) continue;\n addColumn(\n relKey,\n relational.schema,\n relational.table,\n relational.column,\n relational.type\n );\n }\n\n // 4. Foreign keys, last: the tables and columns on both ends have to exist\n // first, and a constraint is the one thing here that can fail on data\n // rather than on schema, so nothing else depends on it.\n const knownConstraints = existing.constraints ?? new Set<string>();\n const plannedConstraints = new Set<string>();\n const foreignKeys = [\n ...planRelationalColumns(collections).map(r => r.foreignKey),\n ...junctions.flatMap(j => j.foreignKeys)\n ];\n for (const fk of foreignKeys) {\n if (!fk) continue;\n const name = `${fk.schema}.${fk.table}.${fk.constraintName}`;\n if (knownConstraints.has(name) || plannedConstraints.has(name)) continue;\n plannedConstraints.add(name);\n actions.push({\n kind: \"add-constraint\",\n target: `${fk.schema}.${fk.table}.${fk.constraintName}`,\n sql: fk.sql\n });\n }\n\n return { actions, statements: actions.map(a => a.sql), legacyForeignKeys };\n}\n\n/** Read what the database has, for the schemas the collections live in. */\nexport async function readExistingSchema(\n client: Queryable,\n schemas: string[]\n): Promise<ExistingSchema> {\n const tables = new Map<string, Set<string>>();\n const enums = new Set<string>();\n if (schemas.length === 0) return { tables, enums };\n\n const inList = schemas\n .map(schema => `'${assertSafeIdentifier(schema, \"schema name\")}'`)\n .join(\", \");\n\n const { rows: columns } = await client.query<{\n table_schema: string;\n table_name: string;\n column_name: string;\n }>(\n `SELECT table_schema, table_name, column_name\n FROM information_schema.columns\n WHERE table_schema IN (${inList})`\n );\n for (const row of columns) {\n const key = `${row.table_schema}.${row.table_name}`;\n if (!tables.has(key)) tables.set(key, new Set());\n tables.get(key)!.add(row.column_name);\n }\n\n const { rows: enumRows } = await client.query<{ schema: string; name: string }>(\n `SELECT n.nspname AS schema, t.typname AS name\n FROM pg_type t\n JOIN pg_namespace n ON t.typnamespace = n.oid\n WHERE t.typtype = 'e' AND n.nspname IN (${inList})`\n );\n for (const row of enumRows) enums.add(`${row.schema}.${row.name}`);\n\n // `ADD CONSTRAINT` has no IF NOT EXISTS, so an existing foreign key is\n // skipped by name rather than guarded in SQL.\n const constraints = new Set<string>();\n const { rows: constraintRows } = await client.query<{\n schema: string;\n table: string;\n name: string;\n }>(\n `SELECT n.nspname AS schema, c.relname AS table, con.conname AS name\n FROM pg_constraint con\n JOIN pg_class c ON con.conrelid = c.oid\n JOIN pg_namespace n ON c.relnamespace = n.oid\n WHERE n.nspname IN (${inList})`\n );\n for (const row of constraintRows) constraints.add(`${row.schema}.${row.table}.${row.name}`);\n\n return { tables, enums, constraints };\n}\n\n/**\n * Bring the database up to date. Returns what it did.\n *\n * Each statement runs on its own rather than in one transaction: they are all\n * independently safe and idempotent, and a single failure (an enum label that\n * cannot be added, say) should not roll back the tables that were created fine.\n * The error is surfaced with the statement that caused it.\n */\nexport async function ensureCollectionTables(\n client: Queryable,\n collections: CollectionConfig[],\n log?: (message: string) => void\n): Promise<EnsureOutcome> {\n // Junctions live alongside the collections that declare them, so their\n // schema has to be read too — otherwise an existing junction reads as\n // missing and its constraints as unplanned.\n const schemas = Array.from(new Set([\n ...collections.map(schemaOf),\n ...planJunctionTables(collections).map(j => j.schema)\n ]));\n for (const schema of schemas) {\n assertSafeIdentifier(schema, \"schema name\");\n if (schema !== \"public\") {\n await client.query(`CREATE SCHEMA IF NOT EXISTS \"${schema}\";`);\n }\n }\n\n const existing = await readExistingSchema(client, schemas);\n const plan = planCollectionSchemaEnsure(collections, existing);\n const failures: { target: string; error: string }[] = [];\n\n // Reported, not warned: this is a rename the ensure is about to perform, and\n // the operator should be able to see in the log why a column changed name.\n // The interesting case is the one that no longer happens — before this,\n // ensure added the new column empty beside the populated old one, every\n // statement succeeded, and the relation read the empty one.\n for (const legacy of plan.legacyForeignKeys) {\n const message =\n `Renaming \"${legacy.table}\".\"${legacy.legacy}\" to \"${legacy.expected}\". The old name is ` +\n \"the one Rebase derived for this relation before it singularized properly; the column \" +\n \"keeps its data, indexes and constraints. To keep the old name instead, set \" +\n `\\`localKey: \"${legacy.legacy}\"\\` on the relation and this will stop.`;\n logger.info(`[schema] ${message}`);\n log?.(message);\n }\n\n if (plan.actions.length === 0) {\n log?.(\"Schema is up to date; nothing to create.\");\n return { ...plan, failures };\n }\n\n for (const action of plan.actions) {\n try {\n await client.query(action.sql);\n log?.(`${action.kind}: ${action.target}`);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n // A foreign key is the only action that can fail on the customer's\n // data rather than on the schema. The column it polices is already\n // there, so the collection serves either way — record it and carry\n // on rather than crash-looping the deployment.\n if (action.kind === \"add-constraint\") {\n failures.push({ target: action.target, error: message });\n continue;\n }\n throw new Error(\n `Failed to ${action.kind} ${action.target}: ${message}\\n ${action.sql}`\n );\n }\n }\n return { ...plan, failures };\n}\n"],"mappings":"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"}