@rebasepro/server-postgres 0.25.0-canary.ge06257e → 0.25.1
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.
- package/dist/{BranchService-DNg4txyf.js → BranchService-ClklxyAQ.js} +283 -20
- package/dist/BranchService-ClklxyAQ.js.map +1 -0
- package/dist/backup/backup-cli.d.ts +70 -1
- package/dist/backup/backup-service.d.ts +79 -0
- package/dist/backup/pg-binary.d.ts +2 -2
- package/dist/backup/pg-tools.d.ts +60 -1
- package/dist/backup-cli-gijeXe0u.js +1961 -0
- package/dist/backup-cli-gijeXe0u.js.map +1 -0
- package/dist/{boot-ddl-Dnvpmm_T.js → boot-ddl-DYH95Qeg.js} +3 -3
- package/dist/{boot-ddl-Dnvpmm_T.js.map → boot-ddl-DYH95Qeg.js.map} +1 -1
- package/dist/branch-argv.d.ts +15 -0
- package/dist/branch-prune.d.ts +18 -0
- package/dist/{cli-console-C_ISGwJo.js → cli-console-CQrQ0lAY.js} +19 -13
- package/dist/cli-console-CQrQ0lAY.js.map +1 -0
- package/dist/cli-console.d.ts +4 -3
- package/dist/{cli-errors-mx4sSjB8.js → cli-errors-DiA0XFIS.js} +110 -14
- package/dist/{cli-errors-mx4sSjB8.js.map → cli-errors-DiA0XFIS.js.map} +1 -1
- package/dist/cli-errors.d.ts +26 -1
- package/dist/cli-flags.d.ts +51 -0
- package/dist/{cli-prompt-egtNKkZl.js → cli-prompt-DXG0yk-h.js} +39 -4
- package/dist/{cli-prompt-egtNKkZl.js.map → cli-prompt-DXG0yk-h.js.map} +1 -1
- package/dist/cli.js +120 -184
- package/dist/cli.js.map +1 -1
- package/dist/collections/validate-relations.d.ts +8 -2
- package/dist/{column-plan-helpers-D_V4oaZh.js → column-plan-helpers-IG6uFb1s.js} +2 -2
- package/dist/{column-plan-helpers-D_V4oaZh.js.map → column-plan-helpers-IG6uFb1s.js.map} +1 -1
- package/dist/{connection-C938nR3E.js → connection-czvI7TZL.js} +57 -10
- package/dist/connection-czvI7TZL.js.map +1 -0
- package/dist/{databasePoolManager-Yqdw14wY.js → databasePoolManager-3stMNFL-.js} +4 -6
- package/dist/databasePoolManager-3stMNFL-.js.map +1 -0
- package/dist/db-command-line.d.ts +14 -0
- package/dist/{rls-bootstrap-sql-Bd-hJk08.js → db-subcommands-BPoHCxJR.js} +455 -3
- package/dist/db-subcommands-BPoHCxJR.js.map +1 -0
- package/dist/db-subcommands.d.ts +11 -0
- package/dist/{deferred-index-build-Ca6OqKyR.js → deferred-index-build-BE0I6sYU.js} +8 -8
- package/dist/{deferred-index-build-Ca6OqKyR.js.map → deferred-index-build-BE0I6sYU.js.map} +1 -1
- package/dist/{doctor-CJyuhV1n.js → doctor-BT--1kq3.js} +107 -11
- package/dist/doctor-BT--1kq3.js.map +1 -0
- package/dist/{ensure-collection-policies-C1ux3V_z.js → ensure-collection-policies-BnGOURpG.js} +60 -11
- package/dist/ensure-collection-policies-BnGOURpG.js.map +1 -0
- package/dist/{ensure-collection-tables-Do-Bvb64.js → ensure-collection-tables-Cet4650m.js} +9 -9
- package/dist/{ensure-collection-tables-Do-Bvb64.js.map → ensure-collection-tables-Cet4650m.js.map} +1 -1
- package/dist/{ensure-tables-Dl_Eh9ow.js → ensure-tables-l_OgFQjN.js} +5 -5
- package/dist/{ensure-tables-Dl_Eh9ow.js.map → ensure-tables-l_OgFQjN.js.map} +1 -1
- package/dist/{enum-labels-kept-D9eTIWCz.js → enum-labels-kept-nheku1pB.js} +3 -3
- package/dist/{enum-labels-kept-D9eTIWCz.js.map → enum-labels-kept-nheku1pB.js.map} +1 -1
- package/dist/{generate-drizzle-schema-Bf8canaR.js → generate-drizzle-schema-DC0BOUpn.js} +3 -3
- package/dist/{generate-drizzle-schema-Bf8canaR.js.map → generate-drizzle-schema-DC0BOUpn.js.map} +1 -1
- package/dist/{generate-drizzle-schema-logic-BaT0H7Gq.js → generate-drizzle-schema-logic-CPzCRD-8.js} +4 -4
- package/dist/{generate-drizzle-schema-logic-BaT0H7Gq.js.map → generate-drizzle-schema-logic-CPzCRD-8.js.map} +1 -1
- package/dist/{generate-files-D50Pxw1G.js → generate-files-BP-4W_gm.js} +6 -6
- package/dist/{generate-files-D50Pxw1G.js.map → generate-files-BP-4W_gm.js.map} +1 -1
- package/dist/{generate-next-step-DCd7yiJ2.js → generate-next-step-DgK5BlBn.js} +22 -14
- package/dist/{generate-next-step-DCd7yiJ2.js.map → generate-next-step-DgK5BlBn.js.map} +1 -1
- package/dist/{generate-postgres-ddl-logic-B4k9djAV.js → generate-postgres-ddl-logic-DPl1oH_z.js} +4 -4
- package/dist/{generate-postgres-ddl-logic-B4k9djAV.js.map → generate-postgres-ddl-logic-DPl1oH_z.js.map} +1 -1
- package/dist/generated-schema-diff-eZCklRZQ.js +1025 -0
- package/dist/generated-schema-diff-eZCklRZQ.js.map +1 -0
- package/dist/{generated-schema-staleness-FlxM2i4i.js → generated-schema-staleness-BvNF3i-s.js} +2 -2
- package/dist/{generated-schema-staleness-FlxM2i4i.js.map → generated-schema-staleness-BvNF3i-s.js.map} +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +70 -11
- package/dist/index.es.js.map +1 -1
- package/dist/{introspect-db-logic-j2vYhmdP.js → introspect-db-logic-DgQVzccW.js} +2 -2
- package/dist/{introspect-db-logic-j2vYhmdP.js.map → introspect-db-logic-DgQVzccW.js.map} +1 -1
- package/dist/migrate-policy-stamps-3eqfhes6.js +89 -0
- package/dist/migrate-policy-stamps-3eqfhes6.js.map +1 -0
- package/dist/{open-rebase-D8rytTP0.js → open-rebase-EmqVmS14.js} +78 -1027
- package/dist/open-rebase-EmqVmS14.js.map +1 -0
- package/dist/{pg-error-utils-CdsGYMTm.js → pg-error-utils-CFEnm2m9.js} +11 -4
- package/dist/pg-error-utils-CFEnm2m9.js.map +1 -0
- package/dist/{plan-schema-CsNgK0qi.js → plan-schema-Czf3q4-Q.js} +4 -4
- package/dist/{plan-schema-CsNgK0qi.js.map → plan-schema-Czf3q4-Q.js.map} +1 -1
- package/dist/{policy-drift-CY5P1ZRu.js → policy-drift-X4A-xAzv.js} +3 -3
- package/dist/{policy-drift-CY5P1ZRu.js.map → policy-drift-X4A-xAzv.js.map} +1 -1
- package/dist/pool-disconnects.d.ts +28 -0
- package/dist/{push-index-prebuild-BDFcfRPo.js → push-index-prebuild-COd7i6wm.js} +4 -4
- package/dist/{push-index-prebuild-BDFcfRPo.js.map → push-index-prebuild-COd7i6wm.js.map} +1 -1
- package/dist/{push-legacy-renames-D4bvMWAb.js → push-legacy-renames-CLgYYwPE.js} +4 -4
- package/dist/{push-legacy-renames-D4bvMWAb.js.map → push-legacy-renames-CLgYYwPE.js.map} +1 -1
- package/dist/{push-policy-plan-DTXE6aVa.js → push-policy-plan-D2gpQsdv.js} +23 -7
- package/dist/push-policy-plan-D2gpQsdv.js.map +1 -0
- package/dist/{render-ddl-CrfEe__B.js → render-ddl-4v88y-Gv.js} +4 -4
- package/dist/{render-ddl-CrfEe__B.js.map → render-ddl-4v88y-Gv.js.map} +1 -1
- package/dist/{rls-enforcement-D7P4-ZYX.js → rls-enforcement-DDyNoOSy.js} +2 -2
- package/dist/{rls-enforcement-D7P4-ZYX.js.map → rls-enforcement-DDyNoOSy.js.map} +1 -1
- package/dist/schema/atlas-argv.d.ts +3 -1
- package/dist/schema/doctor-cli.js +5 -5
- package/dist/schema/doctor.d.ts +20 -2
- package/dist/schema/ensure-collection-policies.d.ts +28 -1
- package/dist/schema/generate-drizzle-schema.js +1 -1
- package/dist/schema/generate-next-step.d.ts +20 -8
- package/dist/schema/generate-postgres-ddl.js +2 -2
- package/dist/schema/generated-schema-diff.d.ts +21 -0
- package/dist/schema/introspect-db.js +6 -6
- package/dist/schema/introspect-db.js.map +1 -1
- package/dist/schema/migrate-policy-stamps.d.ts +23 -0
- package/dist/schema/push-policy-plan.d.ts +10 -2
- package/dist/services/BranchService.d.ts +85 -0
- package/dist/services/pg-notify-listener.d.ts +2 -0
- package/dist/{source-DB6xJv3p.js → source-fZb92pN8.js} +46 -3
- package/dist/source-fZb92pN8.js.map +1 -0
- package/dist/{vector-index-BFhyEaiz.js → vector-index-DsQ3V2d2.js} +2 -2
- package/dist/{vector-index-BFhyEaiz.js.map → vector-index-DsQ3V2d2.js.map} +1 -1
- package/dist/{websocket-DMeXOoyv.js → websocket-BKZwsjmf.js} +18 -5
- package/dist/websocket-BKZwsjmf.js.map +1 -0
- package/package.json +7 -7
- package/dist/BranchService-DNg4txyf.js.map +0 -1
- package/dist/backup-cli-HCEkPcQj.js +0 -556
- package/dist/backup-cli-HCEkPcQj.js.map +0 -1
- package/dist/backup-service-D-UpT7DH.js +0 -857
- package/dist/backup-service-D-UpT7DH.js.map +0 -1
- package/dist/cli-console-C_ISGwJo.js.map +0 -1
- package/dist/cli-flags-DUozBFyK.js +0 -205
- package/dist/cli-flags-DUozBFyK.js.map +0 -1
- package/dist/connection-C938nR3E.js.map +0 -1
- package/dist/databasePoolManager-Yqdw14wY.js.map +0 -1
- package/dist/doctor-CJyuhV1n.js.map +0 -1
- package/dist/ensure-collection-policies-C1ux3V_z.js.map +0 -1
- package/dist/open-rebase-D8rytTP0.js.map +0 -1
- package/dist/pg-error-utils-CdsGYMTm.js.map +0 -1
- package/dist/push-policy-plan-DTXE6aVa.js.map +0 -1
- package/dist/rls-bootstrap-sql-Bd-hJk08.js.map +0 -1
- package/dist/rolldown-runtime-V7rFbMRe.js +0 -39
- package/dist/source-DB6xJv3p.js.map +0 -1
- package/dist/websocket-DMeXOoyv.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-drizzle-schema-logic-BaT0H7Gq.js","names":[],"sources":["../src/schema/plan/render-drizzle.ts","../src/schema/generate-drizzle-schema-logic.ts"],"sourcesContent":["/**\n * `schema.generated.ts`, rendered from a {@link SchemaPlan}.\n *\n * This file is what a developer running drizzle-kit themselves diffs against,\n * and what the runtime's query builder is typed by. It has to *compile*, which\n * nothing in this repo checked until `generated-schema-compiles.test.ts`: six\n * ordinary inputs used to produce a file TypeScript rejects, and the class the\n * bigint incident already taught is that a file which will not build gets\n * hand-patched and then sits stale until a security fix misses production.\n *\n * Two structural rules keep that from coming back:\n *\n * - **The import list is derived from what was emitted.** Every builder is\n * recorded as it is used ({@link BuilderUses}), and the header is written\n * last, from that set. It used to be a fixed roster plus three\n * property-scanning heuristics, so `columnType: \"uuid\"` on a plain string\n * emitted `uuid(…)` and imported nothing.\n * - **Nothing here reads a `Property`.** Types, defaults, keys and constraints\n * all arrive decided. The Drizzle generator disagreeing with the DDL one is\n * what left `geopoint` with a database column and no Drizzle key.\n */\nimport type { ColumnPlan, ForeignKeyPlan, OneRelationPlan, PgType, PolicyPlan, RelationPlan, SchemaPlan, TablePlan } from \"./types\";\nimport { renderPredicate } from \"../collection-index\";\n\n/** What the generated file may leave out. */\nexport interface DrizzleRenderOptions {\n /**\n * `false` strips the `pgPolicy(...)` calls. RLS is still enabled on every\n * table regardless — a bare table must default-deny, not fail open.\n */\n policies?: boolean;\n}\n\nconst JS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\n/**\n * A string literal for the generated file.\n *\n * Column names, table names and enum values are all written in as literals and\n * none of them is constrained to be quote-free: a Postgres identifier only has\n * to be quoted, and `O'Brien` is an ordinary enum value. Interpolating them raw\n * ended the literal early.\n */\nconst quote = (value: string): string => JSON.stringify(value);\n\n/** An object key: verbatim when it is an identifier, quoted otherwise. */\nconst propKey = (name: string): string => (JS_IDENTIFIER.test(name) ? name : quote(name));\n\n/**\n * A property access on a generated table variable.\n *\n * `users.full name` is not an expression; `users[\"full name\"]` is, and Drizzle\n * treats the two identically.\n */\nconst member = (object: string, key: string): string =>\n (JS_IDENTIFIER.test(key) ? `${object}.${key}` : `${object}[${quote(key)}]`);\n\n/**\n * The drizzle-orm builders this file emitted, collected as they are written.\n * The only way to use a builder is to record it here.\n */\ntype BuilderUses = Set<string>;\n\n/** Record a builder and hand back its name, so a call site cannot use one silently. */\nconst needs = (uses: BuilderUses, builder: string): string => {\n uses.add(builder);\n return builder;\n};\n\n/**\n * Wraps a compiled SQL clause in a Drizzle `sql\\`...\\`` template literal.\n *\n * The clause is SQL being written into a TypeScript file, so it has to survive\n * being read back as a template literal. Three characters do not:\n *\n * - `` ` `` closes the template early, and the rest of the clause becomes code.\n * - `${` opens an interpolation — the file stops compiling, or worse, compiles\n * against whatever identifier happens to be in scope.\n * - `\\` is an escape, and Drizzle's `sql` tag reads the *cooked* strings, not\n * `.raw`. So a policy written as `email ~ '^admin\\.user@corp\\.com$'` reached\n * the database as `^admin.user@corp.com$`, where every `\\.` now matches any\n * character. A `USING` clause is a security boundary and that one silently\n * widened it.\n */\nconst wrapSql = (clause: string): string =>\n `sql\\`${clause.replace(/\\\\/g, \"\\\\\\\\\").replace(/`/g, \"\\\\`\").replace(/\\$\\{/g, \"\\\\${\")}\\``;\n\n/** The pg-core builder call for a tagged type, minus the column name. */\nconst builderFor = (type: PgType, column: string, uses: BuilderUses): string => {\n const name = quote(column);\n switch (type.kind) {\n case \"text\": return `${needs(uses, \"text\")}(${name})`;\n case \"varchar\": return `${needs(uses, \"varchar\")}(${name}, { length: ${type.length} })`;\n case \"char\": return `${needs(uses, \"char\")}(${name}, { length: ${type.length} })`;\n case \"uuid\": return `${needs(uses, \"uuid\")}(${name})`;\n // The enum variable is declared at the top of the same file; it is not\n // a pg-core import.\n case \"enum\": return `${type.varName}(${name})`;\n case \"smallint\": return `${needs(uses, \"smallint\")}(${name})`;\n case \"integer\": return `${needs(uses, \"integer\")}(${name})`;\n // `bigint` and `bigserial` are the only pg-core builders that *require*\n // a config argument: without `mode`, drizzle cannot know whether to hand\n // back a `number` or a `bigint`, and the emitted call does not\n // typecheck. This is why `schema.generated.ts` drifted — regenerating it\n // produced a file that would not compile, so the bigint lines were\n // hand-patched, and every regeneration after that looked like a large,\n // alarming diff nobody wanted to ship.\n //\n // `number` rather than `bigint`: these are counters and byte totals that\n // every caller already treats as numbers.\n case \"bigint\": return `${needs(uses, \"bigint\")}(${name}, { mode: \"number\" })`;\n case \"bigserial\": return `${needs(uses, \"bigserial\")}(${name}, { mode: \"number\" })`;\n case \"smallserial\": return `${needs(uses, \"smallserial\")}(${name})`;\n case \"serial\": return `${needs(uses, \"serial\")}(${name})`;\n case \"real\": return `${needs(uses, \"real\")}(${name})`;\n case \"doublePrecision\": return `${needs(uses, \"doublePrecision\")}(${name})`;\n case \"numeric\":\n if (type.precision === undefined) return `${needs(uses, \"numeric\")}(${name})`;\n return type.scale === undefined\n ? `${needs(uses, \"numeric\")}(${name}, { precision: ${type.precision} })`\n : `${needs(uses, \"numeric\")}(${name}, { precision: ${type.precision}, scale: ${type.scale} })`;\n case \"boolean\": return `${needs(uses, \"boolean\")}(${name})`;\n case \"timestamptz\": return `${needs(uses, \"timestamp\")}(${name}, { withTimezone: true, mode: 'string' })`;\n case \"date\": return `${needs(uses, \"date\")}(${name}, { mode: 'string' })`;\n case \"time\": return `${needs(uses, \"time\")}(${name})`;\n case \"json\": return `${needs(uses, \"json\")}(${name})`;\n case \"jsonb\": return `${needs(uses, \"jsonb\")}(${name})`;\n case \"vector\": return `${needs(uses, \"vector\")}(${name}, { dimensions: ${type.dimensions} })`;\n case \"bytea\": return `${needs(uses, \"customType\")}({ dataType() { return 'bytea'; } })(${name})`;\n case \"tsvector\": return `${needs(uses, \"customType\")}({ dataType() { return 'tsvector'; } })(${name})`;\n // pg-core has no array *type*; `.array()` is a modifier on the element.\n case \"array\": return `${builderFor(type.of, column, uses)}.array()`;\n }\n};\n\n/**\n * The `.references(...)` clause a foreign key column carries.\n *\n * The `(): AnyPgColumn =>` annotation is not optional decoration and it is\n * emitted on every reference rather than only the self-referential ones. A\n * `belongsTo` pointing at its own table — a comment thread, a category tree —\n * produces `.references(() => posts.id)` inside the initializer of `posts`, and\n * TypeScript cannot infer a type for a `const` that appears in its own\n * initializer (TS7022). Drizzle documents the annotation as the fix; applying\n * it everywhere means the generated line does not depend on whether the author\n * happened to point the relation at another table.\n */\nconst referencesClause = (\n fk: ForeignKeyPlan,\n targetVar: string,\n targetKey: string,\n uses: BuilderUses\n): string => {\n needs(uses, \"type AnyPgColumn\");\n const parts = [\n fk.onUpdate ? `onUpdate: \"${fk.onUpdate.toLowerCase()}\"` : \"\",\n `onDelete: \"${fk.onDelete.toLowerCase()}\"`\n ].filter(Boolean);\n return `.references((): AnyPgColumn => ${member(targetVar, targetKey)}, { ${parts.join(\", \")} })`;\n};\n\n/** The declaration a column compiles to, without its object key. */\nconst renderColumn = (column: ColumnPlan, table: TablePlan, plan: SchemaPlan, uses: BuilderUses): string => {\n let out = builderFor(column.type, column.column, uses);\n\n if (column.generated) {\n return `${out}.generatedAlwaysAs(${wrapSql(column.generated.expression)})`;\n }\n\n // A junction endpoint spells `.notNull()` before its reference; every other\n // column spells it last. Both are the same constraint — this keeps the\n // generated file byte-stable across the move to one planner.\n const junctionKey = column.source.kind === \"junction-key\";\n if (junctionKey) out += \".notNull()\";\n\n if (column.default?.kind === \"identity\") out += \".generatedByDefaultAsIdentity()\";\n if (column.primaryKey) out += \".primaryKey()\";\n if (column.default && column.default.kind !== \"identity\") {\n const expression = column.default.kind === \"sql\" ? column.default.expression : column.default.sql;\n // `.defaultRandom()` is drizzle's own spelling of `gen_random_uuid()`,\n // and it exists **only on the uuid builder** — a `text` id whose\n // strategy happens to be spelled ``isId: \"sql`gen_random_uuid()`\"`` has\n // to take the generic form, which is what the file has always emitted\n // for it.\n out += column.type.kind === \"uuid\" && expression === \"gen_random_uuid()\"\n ? \".defaultRandom()\"\n : `.default(${wrapSql(expression)})`;\n }\n // A plain property that carries a relation's foreign key column gets the\n // constraint the relation would have emitted. `postId: { columnName:\n // \"post_id\" }` beside a `belongsTo` on `post_id` used to emit an integer\n // with no `.references()` at all, so that project got no foreign key on the\n // Drizzle side while `db push` created one.\n const foreignKey = column.foreignKey\n ?? table.columns.find(c => c.columnOwnedByProperty && c.column === column.column)?.foreignKey;\n if (foreignKey) {\n const target = plan.tables.find(t =>\n t.schema === foreignKey.targetSchema && t.table === foreignKey.targetTable);\n if (target) {\n const targetColumn = target.columns.find(c => c.column === foreignKey.targetColumn);\n out += referencesClause(foreignKey, target.varName, targetColumn?.key ?? foreignKey.targetColumn, uses);\n }\n }\n if (column.unique) out += \".unique()\";\n if (!column.nullable && !column.primaryKey && !junctionKey) out += \".notNull()\";\n return out;\n};\n\n/** The table's indexes — declared, tenant and foreign-key — as Drizzle table extras. */\nconst indexExtras = (table: TablePlan, uses: BuilderUses): string[] =>\n table.indexes.map(spec => {\n const builder = spec.unique ? needs(uses, \"uniqueIndex\") : needs(uses, \"index\");\n // The spec holds COLUMN names; the generated table is keyed by field\n // name (`authorId` for `author_id`).\n const column = (name: string): string =>\n member(\"table\", table.columns.find(c => c.column === name)?.key ?? name);\n const keys = spec.keys.map(key => {\n if (spec.method !== \"btree\") return column(key.column);\n const direction = key.direction === \"desc\" ? \".desc()\" : \".asc()\";\n const nulls = key.nulls === \"first\" ? \".nullsFirst()\" : \".nullsLast()\";\n return `${column(key.column)}${direction}${nulls}`;\n });\n const on = spec.method === \"btree\"\n ? `.on(${keys.join(\", \")})`\n : `.using(${quote(spec.method)}, ${keys.join(\", \")})`;\n const where = spec.predicate ? `.where(sql\\`${renderPredicate(spec.predicate)}\\`)` : \"\";\n // drizzle-orm has no INCLUDE in its index builder (0.45), so a covering\n // index is emitted here as its key columns alone. The database still\n // gets the INCLUDE — `schema.sql` and boot-ensure both emit it.\n const covering = spec.include.length > 0\n ? ` // INCLUDE (${spec.include.join(\", \")}) — drizzle cannot express it; schema.sql does`\n : \"\";\n return ` ${builder}(${quote(spec.indexName)})${on}${where},${covering}`;\n });\n\nconst policyExtra = (policy: PolicyPlan, uses: BuilderUses): string => {\n needs(uses, \"pgPolicy\");\n const parts = [\n `as: \"${policy.mode}\"`,\n `for: \"${policy.operation}\"`,\n `to: [${policy.roles.map(r => `\"${r}\"`).join(\", \")}]`\n ];\n if (policy.using) parts.push(`using: ${wrapSql(policy.using)}`);\n if (policy.withCheck) parts.push(`withCheck: ${wrapSql(policy.withCheck)}`);\n return ` pgPolicy(${quote(policy.name)}, { ${parts.join(\", \")} }),`;\n};\n\nexport function renderDrizzleSchema(plan: SchemaPlan, options: DrizzleRenderOptions = {}): string {\n const withPolicies = options.policies !== false;\n const uses: BuilderUses = new Set();\n // The body is assembled first and the header composed around it, because\n // the header cannot be written until the body has said what it needs.\n let body = \"\";\n\n let schemaDeclarations = \"\";\n if (plan.declaredSchemas.length > 0) {\n needs(uses, \"pgSchema\");\n plan.declaredSchemas.forEach(schema => {\n schemaDeclarations += `export const ${schema}Schema = pgSchema(\"${schema}\");\\n`;\n });\n schemaDeclarations += \"\\n\";\n }\n\n const enumVars: string[] = [];\n for (const enumPlan of plan.enums) {\n // A collection with `schema: \"app\"` gets `CREATE TYPE \"app\".\"…\"` from\n // the DDL renderer, so the type has to be declared in the same schema\n // here — `pgEnum` is `public` and nothing else. Unqualified, the runtime\n // and drizzle-kit looked for a type that does not exist in `public`.\n const labels = enumPlan.labels.map(quote).join(\", \");\n const declaration = enumPlan.declaredSchema\n ? `${enumPlan.declaredSchema}Schema.enum(${quote(enumPlan.name)}, [${labels}])`\n : `${needs(uses, \"pgEnum\")}(${quote(enumPlan.name)}, [${labels}])`;\n body += `export const ${enumPlan.varName} = ${declaration};\\n`;\n if (!enumVars.includes(enumPlan.varName)) enumVars.push(enumPlan.varName);\n }\n body += \"\\n\";\n\n const tableVars: string[] = [];\n for (const table of plan.tables) {\n const creator = table.declaredSchema\n ? `${table.declaredSchema}Schema.table`\n : needs(uses, \"pgTable\");\n body += `export const ${table.varName} = ${creator}(\"${table.table}\", {\\n`;\n\n // The implicit `id` goes last here and first in the SQL file: the DDL\n // generator unshifts it so the key leads the `CREATE TABLE`, and this\n // one appends it after the generated search columns. Both describe the\n // same table; only the reading order differs.\n const ordered = [\n ...table.columns.filter(c => c.source.kind !== \"implicit-id\"),\n ...table.columns.filter(c => c.source.kind === \"implicit-id\")\n ].filter(c =>\n !c.columnOwnedByProperty\n // The auth columns a users collection does not itself declare —\n // `is_anonymous`, `tokens_valid_after`. `db push` emits them because\n // Atlas diffs the database against exactly what it is shown and\n // would otherwise plan a DROP; drizzle-kit creates no auth table, so\n // this file describes the collection as written and leaves auth's\n // own contract to the SQL side that owns it.\n && c.source.kind !== \"auth\");\n\n const lines = ordered.map(column => ` ${propKey(column.key)}: ${renderColumn(column, table, plan, uses)}`);\n // A junction's column list carries a trailing comma; a collection's does\n // not. Kept as the two files have always spelled them.\n body += table.kind === \"junction\"\n ? `${lines.join(\",\\n\")},\\n`\n : `${lines.join(\",\\n\")}\\n`;\n\n const extras: string[] = [];\n // A key over several columns is one `primaryKey({ columns })` extra —\n // `.primaryKey()` on each column would declare several primary keys.\n // The plan lists COLUMN names and the table is keyed by field name\n // (`authorId` for `author_id`), so each is looked up.\n if (table.primaryKey.length > 1) {\n const keyMember = (name: string): string =>\n member(\"table\", table.columns.find(c => c.column === name && !c.columnOwnedByProperty)?.key ?? name);\n extras.push(` ${needs(uses, \"primaryKey\")}({ columns: [${table.primaryKey.map(keyMember).join(\", \")}] }),`);\n }\n // A junction has indexes too since the plan derives one for its\n // target column, and the file drizzle-kit plans from has to carry it\n // or a `drizzle-kit push` would drop what `db push` created.\n extras.push(...indexExtras(table, uses));\n if (withPolicies) {\n extras.push(...table.policies.map(policy => policyExtra(policy, uses)));\n }\n\n if (extras.length > 0) {\n body += \"}, (table) => ([\\n\";\n body += `${extras.join(\"\\n\")}\\n`;\n body += \"])).enableRLS();\\n\\n\";\n } else {\n // No explicit policies and no declared indexes — RLS enabled with a\n // deny-all default (Postgres denies everything when RLS is on and no\n // permissive policies exist).\n body += \"}).enableRLS();\\n\\n\";\n }\n if (!tableVars.includes(table.varName)) tableVars.push(table.varName);\n }\n\n const relationVars: string[] = [];\n for (const table of plan.tables) {\n const entries = plan.relations.filter(r => r.tableVar === table.varName);\n if (entries.length === 0) continue;\n const rendered = entries.map(relation => renderRelation(table, relation, uses));\n const varName = `${table.varName}Relations`;\n body += `export const ${varName} = drizzleRelations(${table.varName}, ({ one, many }) => ({\\n${rendered.join(\",\\n\")}\\n}));\\n\\n`;\n if (!relationVars.includes(varName)) relationVars.push(varName);\n }\n\n // ── The header, written last because the body decides what is in it ──────\n const imports = Array.from(uses).sort((a, b) =>\n a.replace(/^type /, \"\").localeCompare(b.replace(/^type /, \"\")));\n\n let out = \"// This file is auto-generated by the Rebase Drizzle generator. Do not edit manually.\\n\\n\";\n out += `import { ${imports.join(\", \")} } from 'drizzle-orm/pg-core';\\n`;\n out += \"import { relations as drizzleRelations, sql } from 'drizzle-orm';\\n\\n\";\n out += schemaDeclarations;\n out += body;\n out += `export const tables = { ${tableVars.join(\", \")} };\\n`;\n out += `export const enums = { ${enumVars.join(\", \")} };\\n`;\n out += `export const relations = { ${relationVars.join(\", \")} };\\n\\n`;\n return out;\n}\n\nconst renderRelation = (table: TablePlan, relation: RelationPlan, uses: BuilderUses): string => {\n if (relation.kind === \"many\") {\n return ` ${quote(relation.key)}: many(${relation.targetVar}, { relationName: ${quote(relation.relationName)} })`;\n }\n return ` ${quote(relation.key)}: one${oneTypeArguments(table, relation, uses)}(${relation.targetVar}, {\\n` +\n ` fields: [${relation.fields.map(f => member(table.varName, f)).join(\", \")}],\\n` +\n ` references: [${relation.references.map(r => member(relation.targetVar, r)).join(\", \")}],\\n` +\n ` relationName: ${quote(relation.relationName)}\\n` +\n \" })\";\n};\n\n/**\n * Explicit type arguments for a `one()` whose target row may not exist.\n *\n * Drizzle types a `one()` as never null when every column in `fields` is NOT\n * NULL — right for a foreign key, wrong for a `hasOne`, whose `fields` are this\n * table's own key. Giving `TColumns` as a column of this table with no declared\n * nullability makes the related row `T | null` in a relational query's result,\n * which is what the database returns. Empty for every other `one()`, whose\n * inferred type is already right.\n */\nconst oneTypeArguments = (table: TablePlan, relation: OneRelationPlan, uses: BuilderUses): string => {\n if (!relation.nullable) return \"\";\n needs(uses, \"type AnyPgColumn\");\n const column = `AnyPgColumn<{ tableName: ${quote(table.table)} }>`;\n return `<typeof ${relation.targetVar}, [${relation.fields.map(() => column).join(\", \")}]>`;\n};\n","/**\n * `schema.generated.ts`, the Drizzle half of `rebase db generate`.\n *\n * There is no interpretation left in this file. `schema/plan/plan-schema.ts`\n * reads the collections into a {@link SchemaPlan} and\n * `schema/plan/render-drizzle.ts` writes the TypeScript; this is the entry\n * point they are reached through.\n *\n * It used to hold `getDrizzleColumn` — one of three `switch (prop.type)`\n * statements, each with its own reading of relations, enums, search and\n * `validation.unique`, and each disagreeing with the other two about something.\n * See `schema/plan/types.ts`.\n */\nimport type { CollectionConfig } from \"@rebasepro/types\";\nimport { relationalCollections, sortCollectionsBySlug } from \"@rebasepro/common\";\nimport { planSchema } from \"./plan/plan-schema\";\nimport { renderDrizzleSchema, type DrizzleRenderOptions } from \"./plan/render-drizzle\";\n\n/**\n * The generated Drizzle schema for a set of collections.\n *\n * Synchronous, and takes an options object. It was `async` with nothing to\n * await and a positional `stripPolicies` boolean — `generateSchema(c, true)`,\n * which reads as neither \"with policies\" nor \"without\".\n *\n * Sorted by slug here rather than in the writer script: the output is\n * order-dependent and `rebase doctor` regenerates it in memory to compare\n * against the file on disk, so with the sort in the writer only, a project\n * whose file order differed from its slug order was reported stale forever.\n */\nexport const generateSchema = (\n allCollections: CollectionConfig[],\n options: DrizzleRenderOptions = {}\n): string => {\n // A Firestore or MongoDB collection has no table to generate, and\n // generating one for it is not merely wasted output: `db push` would create\n // it, and `rebase doctor` would then report the store the collection\n // actually reads from as drift.\n const collections = sortCollectionsBySlug(relationalCollections(allCollections));\n return renderDrizzleSchema(planSchema(collections), options);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generate-drizzle-schema-logic-CPzCRD-8.js","names":[],"sources":["../src/schema/plan/render-drizzle.ts","../src/schema/generate-drizzle-schema-logic.ts"],"sourcesContent":["/**\n * `schema.generated.ts`, rendered from a {@link SchemaPlan}.\n *\n * This file is what a developer running drizzle-kit themselves diffs against,\n * and what the runtime's query builder is typed by. It has to *compile*, which\n * nothing in this repo checked until `generated-schema-compiles.test.ts`: six\n * ordinary inputs used to produce a file TypeScript rejects, and the class the\n * bigint incident already taught is that a file which will not build gets\n * hand-patched and then sits stale until a security fix misses production.\n *\n * Two structural rules keep that from coming back:\n *\n * - **The import list is derived from what was emitted.** Every builder is\n * recorded as it is used ({@link BuilderUses}), and the header is written\n * last, from that set. It used to be a fixed roster plus three\n * property-scanning heuristics, so `columnType: \"uuid\"` on a plain string\n * emitted `uuid(…)` and imported nothing.\n * - **Nothing here reads a `Property`.** Types, defaults, keys and constraints\n * all arrive decided. The Drizzle generator disagreeing with the DDL one is\n * what left `geopoint` with a database column and no Drizzle key.\n */\nimport type { ColumnPlan, ForeignKeyPlan, OneRelationPlan, PgType, PolicyPlan, RelationPlan, SchemaPlan, TablePlan } from \"./types\";\nimport { renderPredicate } from \"../collection-index\";\n\n/** What the generated file may leave out. */\nexport interface DrizzleRenderOptions {\n /**\n * `false` strips the `pgPolicy(...)` calls. RLS is still enabled on every\n * table regardless — a bare table must default-deny, not fail open.\n */\n policies?: boolean;\n}\n\nconst JS_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\n/**\n * A string literal for the generated file.\n *\n * Column names, table names and enum values are all written in as literals and\n * none of them is constrained to be quote-free: a Postgres identifier only has\n * to be quoted, and `O'Brien` is an ordinary enum value. Interpolating them raw\n * ended the literal early.\n */\nconst quote = (value: string): string => JSON.stringify(value);\n\n/** An object key: verbatim when it is an identifier, quoted otherwise. */\nconst propKey = (name: string): string => (JS_IDENTIFIER.test(name) ? name : quote(name));\n\n/**\n * A property access on a generated table variable.\n *\n * `users.full name` is not an expression; `users[\"full name\"]` is, and Drizzle\n * treats the two identically.\n */\nconst member = (object: string, key: string): string =>\n (JS_IDENTIFIER.test(key) ? `${object}.${key}` : `${object}[${quote(key)}]`);\n\n/**\n * The drizzle-orm builders this file emitted, collected as they are written.\n * The only way to use a builder is to record it here.\n */\ntype BuilderUses = Set<string>;\n\n/** Record a builder and hand back its name, so a call site cannot use one silently. */\nconst needs = (uses: BuilderUses, builder: string): string => {\n uses.add(builder);\n return builder;\n};\n\n/**\n * Wraps a compiled SQL clause in a Drizzle `sql\\`...\\`` template literal.\n *\n * The clause is SQL being written into a TypeScript file, so it has to survive\n * being read back as a template literal. Three characters do not:\n *\n * - `` ` `` closes the template early, and the rest of the clause becomes code.\n * - `${` opens an interpolation — the file stops compiling, or worse, compiles\n * against whatever identifier happens to be in scope.\n * - `\\` is an escape, and Drizzle's `sql` tag reads the *cooked* strings, not\n * `.raw`. So a policy written as `email ~ '^admin\\.user@corp\\.com$'` reached\n * the database as `^admin.user@corp.com$`, where every `\\.` now matches any\n * character. A `USING` clause is a security boundary and that one silently\n * widened it.\n */\nconst wrapSql = (clause: string): string =>\n `sql\\`${clause.replace(/\\\\/g, \"\\\\\\\\\").replace(/`/g, \"\\\\`\").replace(/\\$\\{/g, \"\\\\${\")}\\``;\n\n/** The pg-core builder call for a tagged type, minus the column name. */\nconst builderFor = (type: PgType, column: string, uses: BuilderUses): string => {\n const name = quote(column);\n switch (type.kind) {\n case \"text\": return `${needs(uses, \"text\")}(${name})`;\n case \"varchar\": return `${needs(uses, \"varchar\")}(${name}, { length: ${type.length} })`;\n case \"char\": return `${needs(uses, \"char\")}(${name}, { length: ${type.length} })`;\n case \"uuid\": return `${needs(uses, \"uuid\")}(${name})`;\n // The enum variable is declared at the top of the same file; it is not\n // a pg-core import.\n case \"enum\": return `${type.varName}(${name})`;\n case \"smallint\": return `${needs(uses, \"smallint\")}(${name})`;\n case \"integer\": return `${needs(uses, \"integer\")}(${name})`;\n // `bigint` and `bigserial` are the only pg-core builders that *require*\n // a config argument: without `mode`, drizzle cannot know whether to hand\n // back a `number` or a `bigint`, and the emitted call does not\n // typecheck. This is why `schema.generated.ts` drifted — regenerating it\n // produced a file that would not compile, so the bigint lines were\n // hand-patched, and every regeneration after that looked like a large,\n // alarming diff nobody wanted to ship.\n //\n // `number` rather than `bigint`: these are counters and byte totals that\n // every caller already treats as numbers.\n case \"bigint\": return `${needs(uses, \"bigint\")}(${name}, { mode: \"number\" })`;\n case \"bigserial\": return `${needs(uses, \"bigserial\")}(${name}, { mode: \"number\" })`;\n case \"smallserial\": return `${needs(uses, \"smallserial\")}(${name})`;\n case \"serial\": return `${needs(uses, \"serial\")}(${name})`;\n case \"real\": return `${needs(uses, \"real\")}(${name})`;\n case \"doublePrecision\": return `${needs(uses, \"doublePrecision\")}(${name})`;\n case \"numeric\":\n if (type.precision === undefined) return `${needs(uses, \"numeric\")}(${name})`;\n return type.scale === undefined\n ? `${needs(uses, \"numeric\")}(${name}, { precision: ${type.precision} })`\n : `${needs(uses, \"numeric\")}(${name}, { precision: ${type.precision}, scale: ${type.scale} })`;\n case \"boolean\": return `${needs(uses, \"boolean\")}(${name})`;\n case \"timestamptz\": return `${needs(uses, \"timestamp\")}(${name}, { withTimezone: true, mode: 'string' })`;\n case \"date\": return `${needs(uses, \"date\")}(${name}, { mode: 'string' })`;\n case \"time\": return `${needs(uses, \"time\")}(${name})`;\n case \"json\": return `${needs(uses, \"json\")}(${name})`;\n case \"jsonb\": return `${needs(uses, \"jsonb\")}(${name})`;\n case \"vector\": return `${needs(uses, \"vector\")}(${name}, { dimensions: ${type.dimensions} })`;\n case \"bytea\": return `${needs(uses, \"customType\")}({ dataType() { return 'bytea'; } })(${name})`;\n case \"tsvector\": return `${needs(uses, \"customType\")}({ dataType() { return 'tsvector'; } })(${name})`;\n // pg-core has no array *type*; `.array()` is a modifier on the element.\n case \"array\": return `${builderFor(type.of, column, uses)}.array()`;\n }\n};\n\n/**\n * The `.references(...)` clause a foreign key column carries.\n *\n * The `(): AnyPgColumn =>` annotation is not optional decoration and it is\n * emitted on every reference rather than only the self-referential ones. A\n * `belongsTo` pointing at its own table — a comment thread, a category tree —\n * produces `.references(() => posts.id)` inside the initializer of `posts`, and\n * TypeScript cannot infer a type for a `const` that appears in its own\n * initializer (TS7022). Drizzle documents the annotation as the fix; applying\n * it everywhere means the generated line does not depend on whether the author\n * happened to point the relation at another table.\n */\nconst referencesClause = (\n fk: ForeignKeyPlan,\n targetVar: string,\n targetKey: string,\n uses: BuilderUses\n): string => {\n needs(uses, \"type AnyPgColumn\");\n const parts = [\n fk.onUpdate ? `onUpdate: \"${fk.onUpdate.toLowerCase()}\"` : \"\",\n `onDelete: \"${fk.onDelete.toLowerCase()}\"`\n ].filter(Boolean);\n return `.references((): AnyPgColumn => ${member(targetVar, targetKey)}, { ${parts.join(\", \")} })`;\n};\n\n/** The declaration a column compiles to, without its object key. */\nconst renderColumn = (column: ColumnPlan, table: TablePlan, plan: SchemaPlan, uses: BuilderUses): string => {\n let out = builderFor(column.type, column.column, uses);\n\n if (column.generated) {\n return `${out}.generatedAlwaysAs(${wrapSql(column.generated.expression)})`;\n }\n\n // A junction endpoint spells `.notNull()` before its reference; every other\n // column spells it last. Both are the same constraint — this keeps the\n // generated file byte-stable across the move to one planner.\n const junctionKey = column.source.kind === \"junction-key\";\n if (junctionKey) out += \".notNull()\";\n\n if (column.default?.kind === \"identity\") out += \".generatedByDefaultAsIdentity()\";\n if (column.primaryKey) out += \".primaryKey()\";\n if (column.default && column.default.kind !== \"identity\") {\n const expression = column.default.kind === \"sql\" ? column.default.expression : column.default.sql;\n // `.defaultRandom()` is drizzle's own spelling of `gen_random_uuid()`,\n // and it exists **only on the uuid builder** — a `text` id whose\n // strategy happens to be spelled ``isId: \"sql`gen_random_uuid()`\"`` has\n // to take the generic form, which is what the file has always emitted\n // for it.\n out += column.type.kind === \"uuid\" && expression === \"gen_random_uuid()\"\n ? \".defaultRandom()\"\n : `.default(${wrapSql(expression)})`;\n }\n // A plain property that carries a relation's foreign key column gets the\n // constraint the relation would have emitted. `postId: { columnName:\n // \"post_id\" }` beside a `belongsTo` on `post_id` used to emit an integer\n // with no `.references()` at all, so that project got no foreign key on the\n // Drizzle side while `db push` created one.\n const foreignKey = column.foreignKey\n ?? table.columns.find(c => c.columnOwnedByProperty && c.column === column.column)?.foreignKey;\n if (foreignKey) {\n const target = plan.tables.find(t =>\n t.schema === foreignKey.targetSchema && t.table === foreignKey.targetTable);\n if (target) {\n const targetColumn = target.columns.find(c => c.column === foreignKey.targetColumn);\n out += referencesClause(foreignKey, target.varName, targetColumn?.key ?? foreignKey.targetColumn, uses);\n }\n }\n if (column.unique) out += \".unique()\";\n if (!column.nullable && !column.primaryKey && !junctionKey) out += \".notNull()\";\n return out;\n};\n\n/** The table's indexes — declared, tenant and foreign-key — as Drizzle table extras. */\nconst indexExtras = (table: TablePlan, uses: BuilderUses): string[] =>\n table.indexes.map(spec => {\n const builder = spec.unique ? needs(uses, \"uniqueIndex\") : needs(uses, \"index\");\n // The spec holds COLUMN names; the generated table is keyed by field\n // name (`authorId` for `author_id`).\n const column = (name: string): string =>\n member(\"table\", table.columns.find(c => c.column === name)?.key ?? name);\n const keys = spec.keys.map(key => {\n if (spec.method !== \"btree\") return column(key.column);\n const direction = key.direction === \"desc\" ? \".desc()\" : \".asc()\";\n const nulls = key.nulls === \"first\" ? \".nullsFirst()\" : \".nullsLast()\";\n return `${column(key.column)}${direction}${nulls}`;\n });\n const on = spec.method === \"btree\"\n ? `.on(${keys.join(\", \")})`\n : `.using(${quote(spec.method)}, ${keys.join(\", \")})`;\n const where = spec.predicate ? `.where(sql\\`${renderPredicate(spec.predicate)}\\`)` : \"\";\n // drizzle-orm has no INCLUDE in its index builder (0.45), so a covering\n // index is emitted here as its key columns alone. The database still\n // gets the INCLUDE — `schema.sql` and boot-ensure both emit it.\n const covering = spec.include.length > 0\n ? ` // INCLUDE (${spec.include.join(\", \")}) — drizzle cannot express it; schema.sql does`\n : \"\";\n return ` ${builder}(${quote(spec.indexName)})${on}${where},${covering}`;\n });\n\nconst policyExtra = (policy: PolicyPlan, uses: BuilderUses): string => {\n needs(uses, \"pgPolicy\");\n const parts = [\n `as: \"${policy.mode}\"`,\n `for: \"${policy.operation}\"`,\n `to: [${policy.roles.map(r => `\"${r}\"`).join(\", \")}]`\n ];\n if (policy.using) parts.push(`using: ${wrapSql(policy.using)}`);\n if (policy.withCheck) parts.push(`withCheck: ${wrapSql(policy.withCheck)}`);\n return ` pgPolicy(${quote(policy.name)}, { ${parts.join(\", \")} }),`;\n};\n\nexport function renderDrizzleSchema(plan: SchemaPlan, options: DrizzleRenderOptions = {}): string {\n const withPolicies = options.policies !== false;\n const uses: BuilderUses = new Set();\n // The body is assembled first and the header composed around it, because\n // the header cannot be written until the body has said what it needs.\n let body = \"\";\n\n let schemaDeclarations = \"\";\n if (plan.declaredSchemas.length > 0) {\n needs(uses, \"pgSchema\");\n plan.declaredSchemas.forEach(schema => {\n schemaDeclarations += `export const ${schema}Schema = pgSchema(\"${schema}\");\\n`;\n });\n schemaDeclarations += \"\\n\";\n }\n\n const enumVars: string[] = [];\n for (const enumPlan of plan.enums) {\n // A collection with `schema: \"app\"` gets `CREATE TYPE \"app\".\"…\"` from\n // the DDL renderer, so the type has to be declared in the same schema\n // here — `pgEnum` is `public` and nothing else. Unqualified, the runtime\n // and drizzle-kit looked for a type that does not exist in `public`.\n const labels = enumPlan.labels.map(quote).join(\", \");\n const declaration = enumPlan.declaredSchema\n ? `${enumPlan.declaredSchema}Schema.enum(${quote(enumPlan.name)}, [${labels}])`\n : `${needs(uses, \"pgEnum\")}(${quote(enumPlan.name)}, [${labels}])`;\n body += `export const ${enumPlan.varName} = ${declaration};\\n`;\n if (!enumVars.includes(enumPlan.varName)) enumVars.push(enumPlan.varName);\n }\n body += \"\\n\";\n\n const tableVars: string[] = [];\n for (const table of plan.tables) {\n const creator = table.declaredSchema\n ? `${table.declaredSchema}Schema.table`\n : needs(uses, \"pgTable\");\n body += `export const ${table.varName} = ${creator}(\"${table.table}\", {\\n`;\n\n // The implicit `id` goes last here and first in the SQL file: the DDL\n // generator unshifts it so the key leads the `CREATE TABLE`, and this\n // one appends it after the generated search columns. Both describe the\n // same table; only the reading order differs.\n const ordered = [\n ...table.columns.filter(c => c.source.kind !== \"implicit-id\"),\n ...table.columns.filter(c => c.source.kind === \"implicit-id\")\n ].filter(c =>\n !c.columnOwnedByProperty\n // The auth columns a users collection does not itself declare —\n // `is_anonymous`, `tokens_valid_after`. `db push` emits them because\n // Atlas diffs the database against exactly what it is shown and\n // would otherwise plan a DROP; drizzle-kit creates no auth table, so\n // this file describes the collection as written and leaves auth's\n // own contract to the SQL side that owns it.\n && c.source.kind !== \"auth\");\n\n const lines = ordered.map(column => ` ${propKey(column.key)}: ${renderColumn(column, table, plan, uses)}`);\n // A junction's column list carries a trailing comma; a collection's does\n // not. Kept as the two files have always spelled them.\n body += table.kind === \"junction\"\n ? `${lines.join(\",\\n\")},\\n`\n : `${lines.join(\",\\n\")}\\n`;\n\n const extras: string[] = [];\n // A key over several columns is one `primaryKey({ columns })` extra —\n // `.primaryKey()` on each column would declare several primary keys.\n // The plan lists COLUMN names and the table is keyed by field name\n // (`authorId` for `author_id`), so each is looked up.\n if (table.primaryKey.length > 1) {\n const keyMember = (name: string): string =>\n member(\"table\", table.columns.find(c => c.column === name && !c.columnOwnedByProperty)?.key ?? name);\n extras.push(` ${needs(uses, \"primaryKey\")}({ columns: [${table.primaryKey.map(keyMember).join(\", \")}] }),`);\n }\n // A junction has indexes too since the plan derives one for its\n // target column, and the file drizzle-kit plans from has to carry it\n // or a `drizzle-kit push` would drop what `db push` created.\n extras.push(...indexExtras(table, uses));\n if (withPolicies) {\n extras.push(...table.policies.map(policy => policyExtra(policy, uses)));\n }\n\n if (extras.length > 0) {\n body += \"}, (table) => ([\\n\";\n body += `${extras.join(\"\\n\")}\\n`;\n body += \"])).enableRLS();\\n\\n\";\n } else {\n // No explicit policies and no declared indexes — RLS enabled with a\n // deny-all default (Postgres denies everything when RLS is on and no\n // permissive policies exist).\n body += \"}).enableRLS();\\n\\n\";\n }\n if (!tableVars.includes(table.varName)) tableVars.push(table.varName);\n }\n\n const relationVars: string[] = [];\n for (const table of plan.tables) {\n const entries = plan.relations.filter(r => r.tableVar === table.varName);\n if (entries.length === 0) continue;\n const rendered = entries.map(relation => renderRelation(table, relation, uses));\n const varName = `${table.varName}Relations`;\n body += `export const ${varName} = drizzleRelations(${table.varName}, ({ one, many }) => ({\\n${rendered.join(\",\\n\")}\\n}));\\n\\n`;\n if (!relationVars.includes(varName)) relationVars.push(varName);\n }\n\n // ── The header, written last because the body decides what is in it ──────\n const imports = Array.from(uses).sort((a, b) =>\n a.replace(/^type /, \"\").localeCompare(b.replace(/^type /, \"\")));\n\n let out = \"// This file is auto-generated by the Rebase Drizzle generator. Do not edit manually.\\n\\n\";\n out += `import { ${imports.join(\", \")} } from 'drizzle-orm/pg-core';\\n`;\n out += \"import { relations as drizzleRelations, sql } from 'drizzle-orm';\\n\\n\";\n out += schemaDeclarations;\n out += body;\n out += `export const tables = { ${tableVars.join(\", \")} };\\n`;\n out += `export const enums = { ${enumVars.join(\", \")} };\\n`;\n out += `export const relations = { ${relationVars.join(\", \")} };\\n\\n`;\n return out;\n}\n\nconst renderRelation = (table: TablePlan, relation: RelationPlan, uses: BuilderUses): string => {\n if (relation.kind === \"many\") {\n return ` ${quote(relation.key)}: many(${relation.targetVar}, { relationName: ${quote(relation.relationName)} })`;\n }\n return ` ${quote(relation.key)}: one${oneTypeArguments(table, relation, uses)}(${relation.targetVar}, {\\n` +\n ` fields: [${relation.fields.map(f => member(table.varName, f)).join(\", \")}],\\n` +\n ` references: [${relation.references.map(r => member(relation.targetVar, r)).join(\", \")}],\\n` +\n ` relationName: ${quote(relation.relationName)}\\n` +\n \" })\";\n};\n\n/**\n * Explicit type arguments for a `one()` whose target row may not exist.\n *\n * Drizzle types a `one()` as never null when every column in `fields` is NOT\n * NULL — right for a foreign key, wrong for a `hasOne`, whose `fields` are this\n * table's own key. Giving `TColumns` as a column of this table with no declared\n * nullability makes the related row `T | null` in a relational query's result,\n * which is what the database returns. Empty for every other `one()`, whose\n * inferred type is already right.\n */\nconst oneTypeArguments = (table: TablePlan, relation: OneRelationPlan, uses: BuilderUses): string => {\n if (!relation.nullable) return \"\";\n needs(uses, \"type AnyPgColumn\");\n const column = `AnyPgColumn<{ tableName: ${quote(table.table)} }>`;\n return `<typeof ${relation.targetVar}, [${relation.fields.map(() => column).join(\", \")}]>`;\n};\n","/**\n * `schema.generated.ts`, the Drizzle half of `rebase db generate`.\n *\n * There is no interpretation left in this file. `schema/plan/plan-schema.ts`\n * reads the collections into a {@link SchemaPlan} and\n * `schema/plan/render-drizzle.ts` writes the TypeScript; this is the entry\n * point they are reached through.\n *\n * It used to hold `getDrizzleColumn` — one of three `switch (prop.type)`\n * statements, each with its own reading of relations, enums, search and\n * `validation.unique`, and each disagreeing with the other two about something.\n * See `schema/plan/types.ts`.\n */\nimport type { CollectionConfig } from \"@rebasepro/types\";\nimport { relationalCollections, sortCollectionsBySlug } from \"@rebasepro/common\";\nimport { planSchema } from \"./plan/plan-schema\";\nimport { renderDrizzleSchema, type DrizzleRenderOptions } from \"./plan/render-drizzle\";\n\n/**\n * The generated Drizzle schema for a set of collections.\n *\n * Synchronous, and takes an options object. It was `async` with nothing to\n * await and a positional `stripPolicies` boolean — `generateSchema(c, true)`,\n * which reads as neither \"with policies\" nor \"without\".\n *\n * Sorted by slug here rather than in the writer script: the output is\n * order-dependent and `rebase doctor` regenerates it in memory to compare\n * against the file on disk, so with the sort in the writer only, a project\n * whose file order differed from its slug order was reported stale forever.\n */\nexport const generateSchema = (\n allCollections: CollectionConfig[],\n options: DrizzleRenderOptions = {}\n): string => {\n // A Firestore or MongoDB collection has no table to generate, and\n // generating one for it is not merely wasted output: `db push` would create\n // it, and `rebase doctor` would then report the store the collection\n // actually reads from as drift.\n const collections = sortCollectionsBySlug(relationalCollections(allCollections));\n return renderDrizzleSchema(planSchema(collections), options);\n};\n"],"mappings":"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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "module";
|
|
2
2
|
__createRequire(import.meta.url);
|
|
3
|
-
import {
|
|
4
|
-
import { i as outError, n as describeFailure, r as out } from "./source-
|
|
5
|
-
import { t as generateSchema } from "./generate-drizzle-schema-logic-
|
|
6
|
-
import { a as generatePostgresVectorDdl, i as generatePostgresTriggersDdl, n as generatePostgresPoliciesDdl, r as generatePostgresSearchDdl, t as generatePostgresDdl } from "./generate-postgres-ddl-logic-
|
|
7
|
-
import { n as nextStepAfterGenerate } from "./generate-next-step-
|
|
3
|
+
import { i as __exportAll } from "./cli-prompt-DXG0yk-h.js";
|
|
4
|
+
import { i as outError, n as describeFailure, r as out } from "./source-fZb92pN8.js";
|
|
5
|
+
import { t as generateSchema } from "./generate-drizzle-schema-logic-CPzCRD-8.js";
|
|
6
|
+
import { a as generatePostgresVectorDdl, i as generatePostgresTriggersDdl, n as generatePostgresPoliciesDdl, r as generatePostgresSearchDdl, t as generatePostgresDdl } from "./generate-postgres-ddl-logic-DPl1oH_z.js";
|
|
7
|
+
import { n as nextStepAfterGenerate } from "./generate-next-step-DgK5BlBn.js";
|
|
8
8
|
import { loadCollectionsFromDirectory } from "@rebasepro/server";
|
|
9
9
|
import { declaredDatabaseExtensions } from "@rebasepro/types";
|
|
10
10
|
import path from "path";
|
|
@@ -155,4 +155,4 @@ async function writePostgresDdl(options) {
|
|
|
155
155
|
//#endregion
|
|
156
156
|
export { writeDrizzleSchema as n, writePostgresDdl as r, generate_files_exports as t };
|
|
157
157
|
|
|
158
|
-
//# sourceMappingURL=generate-files-
|
|
158
|
+
//# sourceMappingURL=generate-files-BP-4W_gm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-files-D50Pxw1G.js","names":[],"sources":["../src/schema/generate-files.ts"],"sourcesContent":["import { promises as fsPromises } from \"fs\";\nimport * as fs from \"fs\";\nimport path from \"path\";\nimport { pathToFileURL } from \"url\";\nimport { CollectionConfig, declaredDatabaseExtensions } from \"@rebasepro/types\";\nimport { loadCollectionsFromDirectory } from \"@rebasepro/server\";\nimport { generateSchema } from \"./generate-drizzle-schema-logic\";\nimport {\n generatePostgresDdl,\n generatePostgresPoliciesDdl,\n generatePostgresSearchDdl,\n generatePostgresTriggersDdl,\n generatePostgresVectorDdl\n} from \"./generate-postgres-ddl-logic\";\nimport { describeFailure, out, outError } from \"../cli-output\";\nimport { nextStepAfterGenerate } from \"./generate-next-step\";\n\n/**\n * The two generators `rebase db push` runs as its step 1, as functions.\n *\n * They used to exist only as scripts, each its own `tsx` process: the driver\n * (itself a `tsx` process) spawned one for the Drizzle schema and then one for\n * the DDL, and each paid a Node start, tsx's loader and an import of the whole\n * server package before reading a single collection — about 0.5 s apiece, a\n * second per push. The driver process is one-shot, so a fresh module graph per\n * generation bought nothing: the collections cannot change under it.\n *\n * The scripts remain (`generate-drizzle-schema.ts`, `generate-postgres-ddl.ts`)\n * for `--watch`, which re-imports the collections on every save and does need a\n * process of its own — Node's module cache would serve it the first version\n * forever.\n *\n * Both functions print their own success lines and their own error, exactly as\n * the scripts did, and throw {@link GenerationFailed} after printing — so a\n * caller can tell \"the generator said why\" from \"the generator never ran\".\n */\n\n/** Thrown once the generator has printed why it failed. */\nexport class GenerationFailed extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"GenerationFailed\";\n }\n}\n\nexport interface GenerateFileOptions {\n /** The collections directory (or single module), resolved against the cwd. */\n collectionsPath: string;\n /** Where to write. Absent: print to stdout instead. */\n outputPath?: string;\n /**\n * End on the line that says what to run next. Only `rebase schema\n * generate` itself wants it: inside `db push` it told the push to run\n * `db generate`, and inside `db generate` to run itself.\n */\n nextStep?: boolean;\n}\n\n/** The collections, in slug order so the output does not depend on the directory's. */\nasync function loadSortedCollections(collectionsPath: string): Promise<CollectionConfig[]> {\n // Shared with the runtime and the doctor: what gets generated here must\n // be exactly what the server serves, including directory-level defaults.\n const loaded = await loadCollectionsFromDirectory(path.resolve(collectionsPath));\n // A copy: the loader's array is its caller's, and in-process the same\n // collections are read again by the steps after this one.\n const collections: CollectionConfig[] = Array.isArray(loaded) ? [...loaded] : [];\n collections.sort((a, b) => a.slug.localeCompare(b.slug));\n return collections;\n}\n\n/** `src/schema.generated.ts`, from the collections. */\nexport async function writeDrizzleSchema(options: GenerateFileOptions): Promise<void> {\n try {\n const collections = await loadSortedCollections(options.collectionsPath);\n const schemaContent = generateSchema(collections);\n\n if (options.outputPath) {\n await fsPromises.mkdir(path.dirname(options.outputPath), { recursive: true });\n await fsPromises.writeFile(options.outputPath, schemaContent);\n out(`✅ Drizzle schema generated successfully at ${options.outputPath}`);\n } else {\n out(\"✅ Drizzle schema generated successfully.\");\n out(String(schemaContent));\n }\n\n if (options.nextStep !== false) out(nextStepAfterGenerate());\n } catch (error) {\n outError(`Error generating schema: ${describeFailure(error)}`);\n // `rebase db push` runs this as step 1 and must stop on it: going on\n // applied whatever `src/schema.generated.ts` was already there.\n throw new GenerationFailed(\"Drizzle schema generation failed\", { cause: error });\n }\n}\n\n/** Where a project declares its resources, relative to the config directory. */\nconst RESOURCE_ENTRIES = [\"resources.ts\", \"resources.js\", \"resources/index.ts\", \"resources/index.js\"];\n\n/**\n * Evaluate the project's `resources.ts`, so `declaredDatabaseExtensions()` can\n * answer.\n *\n * The DDL generator loads only the collections directory, so nothing else in it\n * would ever import the resources module — and a permission that reads as\n * absent because nobody loaded it is the worst shape this could take: the push\n * succeeds, `vector.sql` quietly omits the `CREATE EXTENSION`, and the failure\n * lands one statement later looking like a database problem.\n *\n * Imported directly rather than through the config index, for the reason\n * `boot/resource-loading.ts` gives: re-exporting from the index would work only\n * for projects that remember to.\n *\n * A project with no resources module is the normal case and declares nothing.\n * An import that *throws* is not swallowed — a resources module that does not\n * evaluate is a configuration error, and treating it as \"declared nothing\"\n * would turn it into a silently missing extension.\n */\nasync function loadDeclaredResources(configDir: string): Promise<void> {\n const entry = RESOURCE_ENTRIES\n .map(name => path.join(configDir, name))\n .find(candidate => fs.existsSync(candidate));\n if (!entry) return;\n await import(pathToFileURL(entry).href);\n}\n\n/**\n * `schema.sql` (Atlas's desired state) and, beside it, `policies.sql`,\n * `search.sql`, `vector.sql` and `triggers.sql` — the parts Atlas does not own.\n */\nexport async function writePostgresDdl(options: GenerateFileOptions): Promise<void> {\n try {\n const resolvedPath = path.resolve(options.collectionsPath);\n\n // `<config>/collections` is the path this is given, so the config\n // directory is its parent — the same relationship `rebase resources`\n // and the boot loader assume.\n await loadDeclaredResources(path.dirname(resolvedPath));\n\n const collections = await loadSortedCollections(resolvedPath);\n\n // `schema.sql` is Atlas's desired state, and it carries neither the RLS\n // policies, nor the search apparatus, nor the vector columns: Atlas\n // manages none of them, and in the search and vector cases cannot. All\n // three are written beside it and applied by the CLI in their own right.\n const ddlContent = generatePostgresDdl(collections, {\n includePolicies: false,\n includeSearch: false,\n includeVector: false\n });\n const policiesContent = generatePostgresPoliciesDdl(collections);\n const searchContent = generatePostgresSearchDdl(collections);\n const vectorContent = generatePostgresVectorDdl(collections, {\n extensions: declaredDatabaseExtensions()\n });\n const triggersContent = generatePostgresTriggersDdl(collections);\n\n const outputPath = options.outputPath;\n if (outputPath) {\n const outputDir = path.dirname(outputPath);\n await fsPromises.mkdir(outputDir, { recursive: true });\n await fsPromises.writeFile(outputPath, ddlContent);\n out(`✅ PostgreSQL DDL generated successfully at ${outputPath}`);\n\n const policiesPath = path.join(outputDir, \"policies.sql\");\n await fsPromises.writeFile(policiesPath, policiesContent);\n out(`✅ PostgreSQL Policies DDL generated successfully at ${policiesPath}`);\n\n // Removed when the last `search` block goes: the CLI applies this\n // file whenever it exists, and a stale one would keep re-creating\n // functions for collections that no longer want them.\n const searchPath = path.join(outputDir, \"search.sql\");\n if (searchContent) {\n await fsPromises.writeFile(searchPath, searchContent);\n out(`✅ PostgreSQL search DDL generated successfully at ${searchPath}`);\n } else if (fs.existsSync(searchPath)) {\n await fsPromises.rm(searchPath);\n }\n\n // Removed when the last `vector` property goes, for the same reason\n // `search.sql` is: the CLI applies whichever of these files exists,\n // and a stale one would keep re-adding a column the collections no\n // longer declare — which Atlas, no longer told to exclude it, would\n // then plan a DROP for on the very next push.\n const vectorPath = path.join(outputDir, \"vector.sql\");\n if (vectorContent) {\n await fsPromises.writeFile(vectorPath, vectorContent);\n out(`✅ PostgreSQL vector DDL generated successfully at ${vectorPath}`);\n } else if (fs.existsSync(vectorPath)) {\n await fsPromises.rm(vectorPath);\n }\n\n // `autoValue: \"on_update\"`, on the same terms as the two above: a\n // trigger is a function plus a binding, which Atlas's free tier\n // will not parse, and a stale file would keep re-installing a\n // trigger for a column nothing declares any more.\n const triggersPath = path.join(outputDir, \"triggers.sql\");\n if (triggersContent) {\n await fsPromises.writeFile(triggersPath, triggersContent);\n out(`✅ PostgreSQL trigger DDL generated successfully at ${triggersPath}`);\n } else if (fs.existsSync(triggersPath)) {\n await fsPromises.rm(triggersPath);\n }\n } else {\n out(\"✅ PostgreSQL DDL generated successfully.\");\n out(String(ddlContent));\n out(\"\\n✅ PostgreSQL Policies DDL generated successfully.\");\n out(String(policiesContent));\n }\n } catch (error) {\n outError(`Error generating DDL schema: ${describeFailure(error)}`);\n // Step 1 of `rebase db push` is this, and a failure has to stop it.\n // Going on made a resources module that does not evaluate — the single\n // most common cause here — report `✓ rebase db push completed\n // successfully` while Atlas applied whatever `drizzle/schema.sql`\n // happened to be on disk, sometimes minutes old. With no prior file the\n // same run died inside Atlas on `stat drizzle/schema.sql: no such file\n // or directory`, which hides the real cause two screens up.\n throw new GenerationFailed(\"Postgres DDL generation failed\", { cause: error });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generate-files-BP-4W_gm.js","names":[],"sources":["../src/schema/generate-files.ts"],"sourcesContent":["import { promises as fsPromises } from \"fs\";\nimport * as fs from \"fs\";\nimport path from \"path\";\nimport { pathToFileURL } from \"url\";\nimport { CollectionConfig, declaredDatabaseExtensions } from \"@rebasepro/types\";\nimport { loadCollectionsFromDirectory } from \"@rebasepro/server\";\nimport { generateSchema } from \"./generate-drizzle-schema-logic\";\nimport {\n generatePostgresDdl,\n generatePostgresPoliciesDdl,\n generatePostgresSearchDdl,\n generatePostgresTriggersDdl,\n generatePostgresVectorDdl\n} from \"./generate-postgres-ddl-logic\";\nimport { describeFailure, out, outError } from \"../cli-output\";\nimport { nextStepAfterGenerate } from \"./generate-next-step\";\n\n/**\n * The two generators `rebase db push` runs as its step 1, as functions.\n *\n * They used to exist only as scripts, each its own `tsx` process: the driver\n * (itself a `tsx` process) spawned one for the Drizzle schema and then one for\n * the DDL, and each paid a Node start, tsx's loader and an import of the whole\n * server package before reading a single collection — about 0.5 s apiece, a\n * second per push. The driver process is one-shot, so a fresh module graph per\n * generation bought nothing: the collections cannot change under it.\n *\n * The scripts remain (`generate-drizzle-schema.ts`, `generate-postgres-ddl.ts`)\n * for `--watch`, which re-imports the collections on every save and does need a\n * process of its own — Node's module cache would serve it the first version\n * forever.\n *\n * Both functions print their own success lines and their own error, exactly as\n * the scripts did, and throw {@link GenerationFailed} after printing — so a\n * caller can tell \"the generator said why\" from \"the generator never ran\".\n */\n\n/** Thrown once the generator has printed why it failed. */\nexport class GenerationFailed extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"GenerationFailed\";\n }\n}\n\nexport interface GenerateFileOptions {\n /** The collections directory (or single module), resolved against the cwd. */\n collectionsPath: string;\n /** Where to write. Absent: print to stdout instead. */\n outputPath?: string;\n /**\n * End on the line that says what to run next. Only `rebase schema\n * generate` itself wants it: inside `db push` it told the push to run\n * `db generate`, and inside `db generate` to run itself.\n */\n nextStep?: boolean;\n}\n\n/** The collections, in slug order so the output does not depend on the directory's. */\nasync function loadSortedCollections(collectionsPath: string): Promise<CollectionConfig[]> {\n // Shared with the runtime and the doctor: what gets generated here must\n // be exactly what the server serves, including directory-level defaults.\n const loaded = await loadCollectionsFromDirectory(path.resolve(collectionsPath));\n // A copy: the loader's array is its caller's, and in-process the same\n // collections are read again by the steps after this one.\n const collections: CollectionConfig[] = Array.isArray(loaded) ? [...loaded] : [];\n collections.sort((a, b) => a.slug.localeCompare(b.slug));\n return collections;\n}\n\n/** `src/schema.generated.ts`, from the collections. */\nexport async function writeDrizzleSchema(options: GenerateFileOptions): Promise<void> {\n try {\n const collections = await loadSortedCollections(options.collectionsPath);\n const schemaContent = generateSchema(collections);\n\n if (options.outputPath) {\n await fsPromises.mkdir(path.dirname(options.outputPath), { recursive: true });\n await fsPromises.writeFile(options.outputPath, schemaContent);\n out(`✅ Drizzle schema generated successfully at ${options.outputPath}`);\n } else {\n out(\"✅ Drizzle schema generated successfully.\");\n out(String(schemaContent));\n }\n\n if (options.nextStep !== false) out(nextStepAfterGenerate());\n } catch (error) {\n outError(`Error generating schema: ${describeFailure(error)}`);\n // `rebase db push` runs this as step 1 and must stop on it: going on\n // applied whatever `src/schema.generated.ts` was already there.\n throw new GenerationFailed(\"Drizzle schema generation failed\", { cause: error });\n }\n}\n\n/** Where a project declares its resources, relative to the config directory. */\nconst RESOURCE_ENTRIES = [\"resources.ts\", \"resources.js\", \"resources/index.ts\", \"resources/index.js\"];\n\n/**\n * Evaluate the project's `resources.ts`, so `declaredDatabaseExtensions()` can\n * answer.\n *\n * The DDL generator loads only the collections directory, so nothing else in it\n * would ever import the resources module — and a permission that reads as\n * absent because nobody loaded it is the worst shape this could take: the push\n * succeeds, `vector.sql` quietly omits the `CREATE EXTENSION`, and the failure\n * lands one statement later looking like a database problem.\n *\n * Imported directly rather than through the config index, for the reason\n * `boot/resource-loading.ts` gives: re-exporting from the index would work only\n * for projects that remember to.\n *\n * A project with no resources module is the normal case and declares nothing.\n * An import that *throws* is not swallowed — a resources module that does not\n * evaluate is a configuration error, and treating it as \"declared nothing\"\n * would turn it into a silently missing extension.\n */\nasync function loadDeclaredResources(configDir: string): Promise<void> {\n const entry = RESOURCE_ENTRIES\n .map(name => path.join(configDir, name))\n .find(candidate => fs.existsSync(candidate));\n if (!entry) return;\n await import(pathToFileURL(entry).href);\n}\n\n/**\n * `schema.sql` (Atlas's desired state) and, beside it, `policies.sql`,\n * `search.sql`, `vector.sql` and `triggers.sql` — the parts Atlas does not own.\n */\nexport async function writePostgresDdl(options: GenerateFileOptions): Promise<void> {\n try {\n const resolvedPath = path.resolve(options.collectionsPath);\n\n // `<config>/collections` is the path this is given, so the config\n // directory is its parent — the same relationship `rebase resources`\n // and the boot loader assume.\n await loadDeclaredResources(path.dirname(resolvedPath));\n\n const collections = await loadSortedCollections(resolvedPath);\n\n // `schema.sql` is Atlas's desired state, and it carries neither the RLS\n // policies, nor the search apparatus, nor the vector columns: Atlas\n // manages none of them, and in the search and vector cases cannot. All\n // three are written beside it and applied by the CLI in their own right.\n const ddlContent = generatePostgresDdl(collections, {\n includePolicies: false,\n includeSearch: false,\n includeVector: false\n });\n const policiesContent = generatePostgresPoliciesDdl(collections);\n const searchContent = generatePostgresSearchDdl(collections);\n const vectorContent = generatePostgresVectorDdl(collections, {\n extensions: declaredDatabaseExtensions()\n });\n const triggersContent = generatePostgresTriggersDdl(collections);\n\n const outputPath = options.outputPath;\n if (outputPath) {\n const outputDir = path.dirname(outputPath);\n await fsPromises.mkdir(outputDir, { recursive: true });\n await fsPromises.writeFile(outputPath, ddlContent);\n out(`✅ PostgreSQL DDL generated successfully at ${outputPath}`);\n\n const policiesPath = path.join(outputDir, \"policies.sql\");\n await fsPromises.writeFile(policiesPath, policiesContent);\n out(`✅ PostgreSQL Policies DDL generated successfully at ${policiesPath}`);\n\n // Removed when the last `search` block goes: the CLI applies this\n // file whenever it exists, and a stale one would keep re-creating\n // functions for collections that no longer want them.\n const searchPath = path.join(outputDir, \"search.sql\");\n if (searchContent) {\n await fsPromises.writeFile(searchPath, searchContent);\n out(`✅ PostgreSQL search DDL generated successfully at ${searchPath}`);\n } else if (fs.existsSync(searchPath)) {\n await fsPromises.rm(searchPath);\n }\n\n // Removed when the last `vector` property goes, for the same reason\n // `search.sql` is: the CLI applies whichever of these files exists,\n // and a stale one would keep re-adding a column the collections no\n // longer declare — which Atlas, no longer told to exclude it, would\n // then plan a DROP for on the very next push.\n const vectorPath = path.join(outputDir, \"vector.sql\");\n if (vectorContent) {\n await fsPromises.writeFile(vectorPath, vectorContent);\n out(`✅ PostgreSQL vector DDL generated successfully at ${vectorPath}`);\n } else if (fs.existsSync(vectorPath)) {\n await fsPromises.rm(vectorPath);\n }\n\n // `autoValue: \"on_update\"`, on the same terms as the two above: a\n // trigger is a function plus a binding, which Atlas's free tier\n // will not parse, and a stale file would keep re-installing a\n // trigger for a column nothing declares any more.\n const triggersPath = path.join(outputDir, \"triggers.sql\");\n if (triggersContent) {\n await fsPromises.writeFile(triggersPath, triggersContent);\n out(`✅ PostgreSQL trigger DDL generated successfully at ${triggersPath}`);\n } else if (fs.existsSync(triggersPath)) {\n await fsPromises.rm(triggersPath);\n }\n } else {\n out(\"✅ PostgreSQL DDL generated successfully.\");\n out(String(ddlContent));\n out(\"\\n✅ PostgreSQL Policies DDL generated successfully.\");\n out(String(policiesContent));\n }\n } catch (error) {\n outError(`Error generating DDL schema: ${describeFailure(error)}`);\n // Step 1 of `rebase db push` is this, and a failure has to stop it.\n // Going on made a resources module that does not evaluate — the single\n // most common cause here — report `✓ rebase db push completed\n // successfully` while Atlas applied whatever `drizzle/schema.sql`\n // happened to be on disk, sometimes minutes old. With no prior file the\n // same run died inside Atlas on `stat drizzle/schema.sql: no such file\n // or directory`, which hides the real cause two screens up.\n throw new GenerationFailed(\"Postgres DDL generation failed\", { cause: error });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "module";
|
|
2
2
|
__createRequire(import.meta.url);
|
|
3
|
-
import { t as chalk } from "./source-
|
|
3
|
+
import { t as chalk } from "./source-fZb92pN8.js";
|
|
4
4
|
//#region src/schema/generate-next-step.ts
|
|
5
5
|
/**
|
|
6
6
|
* The lines `rebase schema generate` and `rebase db generate` end on.
|
|
@@ -43,24 +43,32 @@ var nextStepAfterGenerate = () => {
|
|
|
43
43
|
return `You can now run ${command("rebase db generate")} to generate the SQL migration files.`;
|
|
44
44
|
};
|
|
45
45
|
/**
|
|
46
|
-
* What to do with the migration `rebase db generate` just wrote
|
|
46
|
+
* What to do with the migration `rebase db generate` just wrote, or nothing
|
|
47
|
+
* when it wrote none.
|
|
47
48
|
*
|
|
48
|
-
* `db migrate`, and on a database that has
|
|
49
|
-
* Boot creates every collection's tables,
|
|
50
|
-
* which creates them too — stops on `already exists`
|
|
51
|
-
* database and on a server of your own alike.
|
|
52
|
-
*
|
|
53
|
-
*
|
|
49
|
+
* `db migrate`, and for a project's first migration on a database that has
|
|
50
|
+
* already booted, `--baseline` instead. Boot creates every collection's tables,
|
|
51
|
+
* so the first migration — which creates them too — stops on `already exists`
|
|
52
|
+
* there, on the managed database and on a server of your own alike. The newest
|
|
53
|
+
* version is named: boot made the tables the collections declare now, which is
|
|
54
|
+
* what that migration describes.
|
|
55
|
+
*
|
|
56
|
+
* Not for a later one: boot may have made an additive change already, and then
|
|
57
|
+
* `db migrate` stops and names the version itself, but it never drops a
|
|
58
|
+
* column, and baselining a destructive migration would skip its DROP.
|
|
54
59
|
*/
|
|
55
|
-
var nextStepAfterDbGenerate = (
|
|
56
|
-
|
|
60
|
+
var nextStepAfterDbGenerate = (migration) => {
|
|
61
|
+
if (!migration.wrote) return [];
|
|
62
|
+
const apply = ` Apply it with ${chalk.bold.green("rebase db migrate")}.`;
|
|
63
|
+
if (!migration.first) return [`${apply} If boot has already made this change there, migrate stops and names the version to record.`];
|
|
64
|
+
const version = migration.newestVersion ?? "<version>";
|
|
57
65
|
return [
|
|
58
|
-
|
|
59
|
-
" On a database
|
|
60
|
-
`
|
|
66
|
+
apply,
|
|
67
|
+
" On a database boot has already set up, these tables exist: record this first migration as applied instead,",
|
|
68
|
+
` with ${chalk.bold.green(`rebase db migrate --baseline ${version}`)}.`
|
|
61
69
|
];
|
|
62
70
|
};
|
|
63
71
|
//#endregion
|
|
64
72
|
export { nextStepAfterGenerate as n, nextStepAfterDbGenerate as t };
|
|
65
73
|
|
|
66
|
-
//# sourceMappingURL=generate-next-step-
|
|
74
|
+
//# sourceMappingURL=generate-next-step-DgK5BlBn.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-next-step-
|
|
1
|
+
{"version":3,"file":"generate-next-step-DgK5BlBn.js","names":[],"sources":["../src/schema/generate-next-step.ts"],"sourcesContent":["/**\n * The lines `rebase schema generate` and `rebase db generate` end on.\n *\n * Its own module because `generate-drizzle-schema.ts` uses `import.meta` to\n * decide whether it was executed directly, which the driver's jest suite cannot\n * load at all — so the one sentence in that file with a decision in it had no\n * test and could not have one where it stood.\n *\n * Printed only by `rebase schema generate` itself. `db push` and `db generate`\n * run the same generator as their first step, and inside them the line was\n * wrong advice: `db push` was told to run `db generate`, and `db generate` to\n * run itself.\n */\nimport chalk from \"chalk\";\n\n/**\n * A command name, highlighted the way the rest of the driver's output is: with\n * chalk, which drops the colour under `NO_COLOR`, when stdout is not a terminal,\n * and in CI logs. The escapes used to be written by hand here, so\n * `NO_COLOR=1 rebase schema generate > log` put `\\x1b[1m\\x1b[44m…` in the file.\n */\nconst command = (text: string): string => chalk.bold.bgBlue.black(text);\n\n/**\n * What to do with the file that was just generated.\n *\n * Not the same answer on the managed development database: `rebase db generate`\n * plans with Atlas, Atlas needs a second empty database to diff against, and\n * PGlite serves exactly one — so the CLI refuses that command outright there.\n * This line recommended it unconditionally, which meant every stock scaffold\n * was told, by the tool itself, to run something the same tool would refuse.\n *\n * The kind arrives as `REBASE_DEV_DATABASE_KIND`, set by the CLI from the\n * ordered resolution in `dev-db/resolve.ts`. It is a pure decision — nothing is\n * started to make it — and the driver cannot make it for itself: on the managed\n * path `DATABASE_URL` is a perfectly ordinary connection string to a Postgres\n * on loopback. Absent (the driver run directly, outside the CLI) keeps the\n * original wording.\n */\nexport const nextStepAfterGenerate = (): string => {\n if (process.env.REBASE_DEV_DATABASE_KIND === \"managed\") {\n return `Start or restart ${command(\"rebase dev\")} — boot applies your collections to the managed development database.`;\n }\n\n return `You can now run ${command(\"rebase db generate\")} to generate the SQL migration files.`;\n};\n\n/**\n * What to do with the migration `rebase db generate` just wrote, or nothing\n * when it wrote none.\n *\n * `db migrate`, and for a project's first migration on a database that has\n * already booted, `--baseline` instead. Boot creates every collection's tables,\n * so the first migration — which creates them too — stops on `already exists`\n * there, on the managed database and on a server of your own alike. The newest\n * version is named: boot made the tables the collections declare now, which is\n * what that migration describes.\n *\n * Not for a later one: boot may have made an additive change already, and then\n * `db migrate` stops and names the version itself, but it never drops a\n * column, and baselining a destructive migration would skip its DROP.\n */\nexport const nextStepAfterDbGenerate = (migration: {\n /** The newest migration's version, or null when none can be read. */\n newestVersion: string | null;\n /** Whether this run wrote a migration. */\n wrote: boolean;\n /** Whether it is the project's first. */\n first: boolean;\n}): string[] => {\n if (!migration.wrote) return [];\n const apply = ` Apply it with ${chalk.bold.green(\"rebase db migrate\")}.`;\n if (!migration.first) {\n return [`${apply} If boot has already made this change there, migrate stops and names the version to record.`];\n }\n const version = migration.newestVersion ?? \"<version>\";\n return [\n apply,\n \" On a database boot has already set up, these tables exist: record this first migration as applied instead,\",\n ` with ${chalk.bold.green(`rebase db migrate --baseline ${version}`)}.`\n ];\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAqBA,IAAM,WAAW,SAAyB,MAAM,KAAK,OAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;AAkBtE,IAAa,8BAAsC;CAC/C,IAAI,QAAQ,IAAI,6BAA6B,WACzC,OAAO,oBAAoB,QAAQ,YAAY,EAAE;CAGrD,OAAO,mBAAmB,QAAQ,oBAAoB,EAAE;AAC5D;;;;;;;;;;;;;;;;AAiBA,IAAa,2BAA2B,cAOxB;CACZ,IAAI,CAAC,UAAU,OAAO,OAAO,CAAC;CAC9B,MAAM,QAAQ,mBAAmB,MAAM,KAAK,MAAM,mBAAmB,EAAE;CACvE,IAAI,CAAC,UAAU,OACX,OAAO,CAAC,GAAG,MAAM,4FAA4F;CAEjH,MAAM,UAAU,UAAU,iBAAiB;CAC3C,OAAO;EACH;EACA;EACA,UAAU,MAAM,KAAK,MAAM,gCAAgC,SAAS,EAAE;CAC1E;AACJ"}
|
package/dist/{generate-postgres-ddl-logic-B4k9djAV.js → generate-postgres-ddl-logic-DPl1oH_z.js}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "module";
|
|
2
2
|
__createRequire(import.meta.url);
|
|
3
|
-
import {
|
|
4
|
-
import { n as planSchema } from "./plan-schema-
|
|
5
|
-
import { a as renderPolicyStatements, c as renderSearchDdl, d as searchExcludePatternsOf, f as triggerExcludePatternsOf, i as renderPoliciesDdl, l as renderTriggersDdl, o as renderPostgresDdl, p as vectorExcludePatternsOf, r as renderPgType, u as renderVectorDdl } from "./render-ddl-
|
|
3
|
+
import { i as __exportAll } from "./cli-prompt-DXG0yk-h.js";
|
|
4
|
+
import { n as planSchema } from "./plan-schema-Czf3q4-Q.js";
|
|
5
|
+
import { a as renderPolicyStatements, c as renderSearchDdl, d as searchExcludePatternsOf, f as triggerExcludePatternsOf, i as renderPoliciesDdl, l as renderTriggersDdl, o as renderPostgresDdl, p as vectorExcludePatternsOf, r as renderPgType, u as renderVectorDdl } from "./render-ddl-4v88y-Gv.js";
|
|
6
6
|
import "@rebasepro/types";
|
|
7
7
|
import "@rebasepro/common";
|
|
8
8
|
//#region src/schema/generate-postgres-ddl-logic.ts
|
|
@@ -149,4 +149,4 @@ var searchColumnDependencies = (allCollections) => {
|
|
|
149
149
|
//#endregion
|
|
150
150
|
export { generatePostgresVectorDdl as a, planJunctionTables as c, generatePostgresTriggersDdl as i, generatePostgresPoliciesDdl as n, generate_postgres_ddl_logic_exports as o, generatePostgresSearchDdl as r, planCollectionPolicies as s, generatePostgresDdl as t };
|
|
151
151
|
|
|
152
|
-
//# sourceMappingURL=generate-postgres-ddl-logic-
|
|
152
|
+
//# sourceMappingURL=generate-postgres-ddl-logic-DPl1oH_z.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-postgres-ddl-logic-B4k9djAV.js","names":[],"sources":["../src/schema/generate-postgres-ddl-logic.ts"],"sourcesContent":["/**\n * The SQL half of `rebase db generate`, as a plan and a renderer.\n *\n * There is no interpretation left in this file. `schema/plan/plan-schema.ts`\n * reads the collections into a {@link SchemaPlan} and `schema/plan/render-ddl.ts`\n * writes the files; what remains here is the public entry points those two are\n * reached through, plus the smaller plans (`planRelationalColumns`,\n * `planJunctionTables`, `planCollectionPolicies`) that other modules ask for as\n * slices of the same plan.\n *\n * It used to hold `getSqlColumnType` and a `CREATE TABLE` walk with its own\n * reading of relations, junctions, enums and search — one of three such\n * readings. See `schema/plan/types.ts` for what that cost.\n */\nimport { CollectionConfig, SecurityRule, isPostgresCollectionConfig } from \"@rebasepro/types\";\nimport { getTableName } from \"@rebasepro/common\";\nimport { planSchema, compileSecurityRule, quoteSqlLiteral, defaultBelongsToOnDelete } from \"./plan/plan-schema\";\nimport {\n renderPgType,\n renderPoliciesDdl,\n renderPolicyStatements,\n renderPostgresDdl,\n renderSearchDdl,\n renderTriggersDdl,\n renderVectorDdl,\n searchExcludePatternsOf,\n triggerExcludePatternsOf,\n vectorExcludePatternsOf,\n type DdlRenderOptions\n} from \"./plan/render-ddl\";\nimport type { ForeignKeyPlan, SchemaPlan } from \"./plan/types\";\nimport type { GeneratedColumnDependency } from \"./generated-column-conflicts\";\n\nexport { quoteSqlLiteral, defaultBelongsToOnDelete };\nexport type { ForeignKeyPlan };\n\ntype ResolveCollection = (slug: string) => CollectionConfig | undefined;\n\n/**\n * The individual SQL statements a single security rule compiles to: a\n * `DROP POLICY IF EXISTS` / `CREATE POLICY` pair per operation, each a complete\n * statement (terminated by `;`, no trailing newline).\n *\n * This is the primitive the boot-time RLS applier runs one statement at a time\n * (the runtime's DB handle speaks the extended query protocol, which forbids\n * multiple commands in one execute), while `db push` writes the joined string.\n */\nexport const generatePolicyStatements = (\n collection: CollectionConfig,\n rule: SecurityRule,\n resolveCollection: ResolveCollection\n): string[] => {\n const table = {\n schema: isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\",\n table: getTableName(collection)\n };\n return compileSecurityRule(collection, rule, resolveCollection, false)\n .flatMap(policy => renderPolicyStatements(table, policy));\n};\n\n/**\n * `.rebase/sql/schema.sql` — the desired state Atlas diffs the database against.\n *\n * Synchronous. It was `async` with nothing to await, which made every caller\n * (the writer script, the doctor, the live editor, two gates) carry a promise\n * for no reason and made the one place that forgot to await it silently compare\n * `[object Promise]`.\n */\nexport const generatePostgresDdl = (\n allCollections: CollectionConfig[],\n options: DdlRenderOptions = {}\n): string => renderPostgresDdl(planSchema(allCollections), options);\n\n/** `.rebase/sql/policies.sql` — the RLS policies. Atlas has no model for a policy. */\nexport const generatePostgresPoliciesDdl = (allCollections: CollectionConfig[]): string =>\n renderPoliciesDdl(planSchema(allCollections));\n\n/**\n * `.rebase/sql/search.sql` — everything a `search` block needs, as a file Rebase\n * applies itself.\n *\n * Search is one of the parts of the schema Atlas does not own, for two\n * independent reasons and either alone would be enough:\n *\n * 1. Its free tier refuses to *parse* a desired-state file that so much as\n * contains a function — \"functions and procedures are available to\n * logged-in users only\". A generated `tsvector` column cannot avoid one:\n * `unaccent` is STABLE and jsonb flattening needs a set-returning function,\n * so both have to be wrapped in an IMMUTABLE helper to be legal in a\n * generated column at all.\n * 2. Even with the file accepted, Atlas *wipes* the dev database it diffs\n * against, so a helper seeded there beforehand is gone by the time the plan\n * is analysed. There is no hook to reinstate it.\n *\n * Empty when nothing opted in — the caller writes no file then.\n */\nexport const generatePostgresSearchDdl = (allCollections: CollectionConfig[]): string =>\n renderSearchDdl(planSchema(allCollections));\n\n/**\n * Glob patterns telling Atlas to leave the search column and its index alone.\n *\n * Without these, a desired state that omits search reads to Atlas as an\n * instruction to drop the column — taking the index and the whole search\n * feature with it on the next push.\n */\nexport const searchExcludePatterns = (allCollections: CollectionConfig[]): string[] =>\n searchExcludePatternsOf(planSchema(allCollections));\n\n/**\n * `.rebase/sql/vector.sql` — the `vector` columns, their ANN indexes and, when a\n * database declared it, the extension.\n *\n * Vector is out of Atlas's hands for a narrower reason than search: not that\n * Atlas refuses to parse the file, but that it cannot *execute* it. To compute\n * a desired state Atlas materialises `schema.sql` in a dev database, that\n * database is created empty, and Atlas empties it again at the start of every\n * run — so `VECTOR(384)` resolves against a database that structurally cannot\n * have pgvector, and every push dies with `type \"vector\" does not exist`.\n */\nexport const generatePostgresVectorDdl = (\n allCollections: CollectionConfig[],\n options: { extensions?: readonly string[] } = {}\n): string => renderVectorDdl(planSchema(allCollections, { databaseExtensions: options.extensions }));\n\n/** @see searchExcludePatterns */\nexport const vectorExcludePatterns = (allCollections: CollectionConfig[]): string[] =>\n vectorExcludePatternsOf(planSchema(allCollections));\n\n/**\n * `.rebase/sql/triggers.sql` — the `BEFORE UPDATE` triggers behind\n * `autoValue: \"on_update\"`, and the one function they share.\n *\n * A trigger is a function plus a binding, so it is carved out of Atlas's view\n * for the first of the two reasons search is. Empty when no property declares\n * `on_update` — the caller writes no file then, and removes a stale one.\n */\nexport const generatePostgresTriggersDdl = (allCollections: CollectionConfig[]): string =>\n renderTriggersDdl(planSchema(allCollections));\n\n/** @see searchExcludePatterns */\nexport const triggerExcludePatterns = (allCollections: CollectionConfig[]): string[] =>\n triggerExcludePatternsOf(planSchema(allCollections));\n\n// ── Slices of the plan other modules ask for ─────────────────────────────────\n\n/** A column a `relation` or `reference` property owns on its own table. */\nexport interface RelationalColumnPlan {\n schema: string;\n /** Bare table name, no schema prefix. */\n table: string;\n column: string;\n /** Postgres type, exactly as the DDL renderer declares it. */\n type: string;\n /** `validation.required` on the property that owns the link. */\n required: boolean;\n /**\n * True when a declared property emits this column and the relation\n * contributes only the constraint — `postId` with `columnName: \"post_id\"`\n * beside a `belongsTo` on `post_id`.\n */\n columnOwnedByProperty?: boolean;\n /** Absent when the target collection is not part of this bundle. */\n foreignKey?: ForeignKeyPlan;\n /**\n * What this column would have been called before `generateForeignKeyName`\n * learned to singularize. Carried so the boot-time ensure can notice a\n * database provisioned under the old rule; never used to name anything.\n */\n legacyColumn?: string;\n}\n\n/** The table behind a many-to-many `through` relation. */\nexport interface JunctionTablePlan {\n schema: string;\n /** Bare table name, no schema prefix. */\n table: string;\n columns: { name: string; type: string; legacyName?: string }[];\n /** Both endpoint columns plus the composite primary key. */\n createTable: string;\n foreignKeys: ForeignKeyPlan[];\n}\n\nconst planOf = (collections: CollectionConfig[]): SchemaPlan => planSchema(collections);\n\n/**\n * The FK columns the declared collections own — one entry per `relation`\n * (`belongsTo` side) or `reference` property.\n *\n * A view over the schema plan. It was a second walk of the collections beside\n * the `CREATE TABLE` one, which is how boot-ensure came to add every relation\n * column bare while `db push` made the same column `NOT NULL`.\n */\nexport const planRelationalColumns = (allCollections: CollectionConfig[]): RelationalColumnPlan[] => {\n const plan = planOf(allCollections);\n const out: RelationalColumnPlan[] = [];\n for (const table of plan.tables) {\n if (table.kind !== \"collection\") continue;\n for (const column of table.columns) {\n if (column.source.kind !== \"relation\" && column.source.kind !== \"reference\") continue;\n out.push({\n schema: table.schema,\n table: table.table,\n column: column.column,\n type: renderPgType(column.type),\n required: !column.nullable,\n columnOwnedByProperty: column.columnOwnedByProperty,\n legacyColumn: column.legacyColumn,\n foreignKey: column.foreignKey\n });\n }\n }\n return out;\n};\n\n/**\n * The junction tables a bundle's many-to-many relations imply.\n *\n * Derived from the same plan the derived RLS comes from, so a table created\n * here always has policies planned for it — a junction with row-level security\n * left off is readable and writable by every signed-in user.\n */\nexport const planJunctionTables = (allCollections: CollectionConfig[]): JunctionTablePlan[] =>\n planOf(allCollections).tables\n .filter(table => table.kind === \"junction\")\n .map(table => ({\n schema: table.schema,\n table: table.table,\n columns: table.columns.map(c => ({\n name: c.column,\n type: renderPgType(c.type),\n legacyName: c.legacyColumn\n })),\n createTable:\n `CREATE TABLE IF NOT EXISTS \"${table.schema}\".\"${table.table}\" (` +\n table.columns.map(c => `\"${c.column}\" ${renderPgType(c.type)} NOT NULL`).join(\", \") +\n `, PRIMARY KEY (${table.primaryKey.map(c => `\"${c}\"`).join(\", \")}));`,\n foreignKeys: table.columns.map(c => c.foreignKey).filter((fk): fk is ForeignKeyPlan => fk !== undefined)\n }));\n\nexport interface CollectionPolicyPlan {\n /** The table's schema (e.g. `public`, `rebase`). */\n schema: string;\n /** The bare table name, no schema prefix. */\n table: string;\n /** `schema.table` — matches the keys `readExistingSchema` returns. */\n qualified: string;\n /** `ALTER TABLE … ENABLE ROW LEVEL SECURITY;` — locked by default. */\n enableRls: string;\n /** `DROP POLICY IF EXISTS` / `CREATE POLICY` statements, in order. */\n policyStatements: string[];\n}\n\n/**\n * The per-table RLS plan for the declared collections and their junctions, as\n * executable statements — what the managed runtime applies at boot so a freshly\n * provisioned tenant database serves data instead of 401ing every read.\n *\n * The same policies `policies.sql` carries, from the same plan, so boot and\n * `db push` produce identical RLS from identical collections.\n */\nexport const planCollectionPolicies = (allCollections: CollectionConfig[]): CollectionPolicyPlan[] => {\n const plan = planOf(allCollections);\n // Collections first, then the junctions no collection declares.\n const ordered = [\n ...plan.tables.filter(t => t.kind === \"collection\"),\n ...plan.tables.filter(t => t.kind === \"junction\")\n ];\n return ordered.map(table => ({\n schema: table.schema,\n table: table.table,\n qualified: table.qualified,\n enableRls: `ALTER TABLE \"${table.schema}\".\"${table.table}\" ENABLE ROW LEVEL SECURITY;`,\n policyStatements: table.policies.flatMap(policy => renderPolicyStatements(table, policy))\n }));\n};\n\n/**\n * The `generated column → column it reads` edges a project's `search` blocks\n * imply, derived from the collections alone.\n *\n * The same edges `queryGeneratedColumnDependencies` reads out of `pg_depend`,\n * but available where no database is: `rebase db generate` writes a migration\n * that will run somewhere else, later, against a database it cannot inspect.\n * A spec's `fields[].column` is the physical column each search field starts\n * at, which is exactly what the generated expression depends on.\n */\nexport const searchColumnDependencies = (\n allCollections: CollectionConfig[]\n): GeneratedColumnDependency[] => {\n const dependencies: GeneratedColumnDependency[] = [];\n for (const table of planOf(allCollections).tables) {\n const spec = table.search;\n if (!spec) continue;\n const generated = [spec.column, ...(spec.fuzzy ? [spec.fuzzy.column] : [])];\n const sources = new Set(spec.fields.map(field => field.column));\n for (const column of generated) {\n for (const dependsOn of sources) {\n dependencies.push({ schema: table.schema, table: table.table, column, dependsOn });\n }\n }\n }\n return dependencies;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEA,IAAa,uBACT,gBACA,UAA4B,CAAC,MACpB,kBAAkB,WAAW,cAAc,GAAG,OAAO;;AAGlE,IAAa,+BAA+B,mBACxC,kBAAkB,WAAW,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;AAqBhD,IAAa,6BAA6B,mBACtC,gBAAgB,WAAW,cAAc,CAAC;;;;;;;;AAS9C,IAAa,yBAAyB,mBAClC,wBAAwB,WAAW,cAAc,CAAC;;;;;;;;;;;;AAatD,IAAa,6BACT,gBACA,UAA8C,CAAC,MACtC,gBAAgB,WAAW,gBAAgB,EAAE,oBAAoB,QAAQ,WAAW,CAAC,CAAC;;AAGnG,IAAa,yBAAyB,mBAClC,wBAAwB,WAAW,cAAc,CAAC;;;;;;;;;AAUtD,IAAa,+BAA+B,mBACxC,kBAAkB,WAAW,cAAc,CAAC;;AAGhD,IAAa,0BAA0B,mBACnC,yBAAyB,WAAW,cAAc,CAAC;AAyCvD,IAAM,UAAU,gBAAgD,WAAW,WAAW;;;;;;;;AAuCtF,IAAa,sBAAsB,mBAC/B,OAAO,cAAc,CAAC,CAAC,OAClB,QAAO,UAAS,MAAM,SAAS,UAAU,CAAC,CAC1C,KAAI,WAAU;CACX,QAAQ,MAAM;CACd,OAAO,MAAM;CACb,SAAS,MAAM,QAAQ,KAAI,OAAM;EAC7B,MAAM,EAAE;EACR,MAAM,aAAa,EAAE,IAAI;EACzB,YAAY,EAAE;CAClB,EAAE;CACF,aACI,+BAA+B,MAAM,OAAO,KAAK,MAAM,MAAM,OAC7D,MAAM,QAAQ,KAAI,MAAK,IAAI,EAAE,OAAO,IAAI,aAAa,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,KAAK,IAAI,IAClF,kBAAkB,MAAM,WAAW,KAAI,MAAK,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE;CACrE,aAAa,MAAM,QAAQ,KAAI,MAAK,EAAE,UAAU,CAAC,CAAC,QAAQ,OAA6B,OAAO,KAAA,CAAS;AAC3G,EAAE;;;;;;;;;AAuBV,IAAa,0BAA0B,mBAA+D;CAClG,MAAM,OAAO,OAAO,cAAc;CAMlC,OAAO,CAHH,GAAG,KAAK,OAAO,QAAO,MAAK,EAAE,SAAS,YAAY,GAClD,GAAG,KAAK,OAAO,QAAO,MAAK,EAAE,SAAS,UAAU,CAE7C,CAAA,CAAQ,KAAI,WAAU;EACzB,QAAQ,MAAM;EACd,OAAO,MAAM;EACb,WAAW,MAAM;EACjB,WAAW,gBAAgB,MAAM,OAAO,KAAK,MAAM,MAAM;EACzD,kBAAkB,MAAM,SAAS,SAAQ,WAAU,uBAAuB,OAAO,MAAM,CAAC;CAC5F,EAAE;AACN;;;;;;;;;;;AAYA,IAAa,4BACT,mBAC8B;CAC9B,MAAM,eAA4C,CAAC;CACnD,KAAK,MAAM,SAAS,OAAO,cAAc,CAAC,CAAC,QAAQ;EAC/C,MAAM,OAAO,MAAM;EACnB,IAAI,CAAC,MAAM;EACX,MAAM,YAAY,CAAC,KAAK,QAAQ,GAAI,KAAK,QAAQ,CAAC,KAAK,MAAM,MAAM,IAAI,CAAC,CAAE;EAC1E,MAAM,UAAU,IAAI,IAAI,KAAK,OAAO,KAAI,UAAS,MAAM,MAAM,CAAC;EAC9D,KAAK,MAAM,UAAU,WACjB,KAAK,MAAM,aAAa,SACpB,aAAa,KAAK;GAAE,QAAQ,MAAM;GAAQ,OAAO,MAAM;GAAO;GAAQ;EAAU,CAAC;CAG7F;CACA,OAAO;AACX"}
|
|
1
|
+
{"version":3,"file":"generate-postgres-ddl-logic-DPl1oH_z.js","names":[],"sources":["../src/schema/generate-postgres-ddl-logic.ts"],"sourcesContent":["/**\n * The SQL half of `rebase db generate`, as a plan and a renderer.\n *\n * There is no interpretation left in this file. `schema/plan/plan-schema.ts`\n * reads the collections into a {@link SchemaPlan} and `schema/plan/render-ddl.ts`\n * writes the files; what remains here is the public entry points those two are\n * reached through, plus the smaller plans (`planRelationalColumns`,\n * `planJunctionTables`, `planCollectionPolicies`) that other modules ask for as\n * slices of the same plan.\n *\n * It used to hold `getSqlColumnType` and a `CREATE TABLE` walk with its own\n * reading of relations, junctions, enums and search — one of three such\n * readings. See `schema/plan/types.ts` for what that cost.\n */\nimport { CollectionConfig, SecurityRule, isPostgresCollectionConfig } from \"@rebasepro/types\";\nimport { getTableName } from \"@rebasepro/common\";\nimport { planSchema, compileSecurityRule, quoteSqlLiteral, defaultBelongsToOnDelete } from \"./plan/plan-schema\";\nimport {\n renderPgType,\n renderPoliciesDdl,\n renderPolicyStatements,\n renderPostgresDdl,\n renderSearchDdl,\n renderTriggersDdl,\n renderVectorDdl,\n searchExcludePatternsOf,\n triggerExcludePatternsOf,\n vectorExcludePatternsOf,\n type DdlRenderOptions\n} from \"./plan/render-ddl\";\nimport type { ForeignKeyPlan, SchemaPlan } from \"./plan/types\";\nimport type { GeneratedColumnDependency } from \"./generated-column-conflicts\";\n\nexport { quoteSqlLiteral, defaultBelongsToOnDelete };\nexport type { ForeignKeyPlan };\n\ntype ResolveCollection = (slug: string) => CollectionConfig | undefined;\n\n/**\n * The individual SQL statements a single security rule compiles to: a\n * `DROP POLICY IF EXISTS` / `CREATE POLICY` pair per operation, each a complete\n * statement (terminated by `;`, no trailing newline).\n *\n * This is the primitive the boot-time RLS applier runs one statement at a time\n * (the runtime's DB handle speaks the extended query protocol, which forbids\n * multiple commands in one execute), while `db push` writes the joined string.\n */\nexport const generatePolicyStatements = (\n collection: CollectionConfig,\n rule: SecurityRule,\n resolveCollection: ResolveCollection\n): string[] => {\n const table = {\n schema: isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\",\n table: getTableName(collection)\n };\n return compileSecurityRule(collection, rule, resolveCollection, false)\n .flatMap(policy => renderPolicyStatements(table, policy));\n};\n\n/**\n * `.rebase/sql/schema.sql` — the desired state Atlas diffs the database against.\n *\n * Synchronous. It was `async` with nothing to await, which made every caller\n * (the writer script, the doctor, the live editor, two gates) carry a promise\n * for no reason and made the one place that forgot to await it silently compare\n * `[object Promise]`.\n */\nexport const generatePostgresDdl = (\n allCollections: CollectionConfig[],\n options: DdlRenderOptions = {}\n): string => renderPostgresDdl(planSchema(allCollections), options);\n\n/** `.rebase/sql/policies.sql` — the RLS policies. Atlas has no model for a policy. */\nexport const generatePostgresPoliciesDdl = (allCollections: CollectionConfig[]): string =>\n renderPoliciesDdl(planSchema(allCollections));\n\n/**\n * `.rebase/sql/search.sql` — everything a `search` block needs, as a file Rebase\n * applies itself.\n *\n * Search is one of the parts of the schema Atlas does not own, for two\n * independent reasons and either alone would be enough:\n *\n * 1. Its free tier refuses to *parse* a desired-state file that so much as\n * contains a function — \"functions and procedures are available to\n * logged-in users only\". A generated `tsvector` column cannot avoid one:\n * `unaccent` is STABLE and jsonb flattening needs a set-returning function,\n * so both have to be wrapped in an IMMUTABLE helper to be legal in a\n * generated column at all.\n * 2. Even with the file accepted, Atlas *wipes* the dev database it diffs\n * against, so a helper seeded there beforehand is gone by the time the plan\n * is analysed. There is no hook to reinstate it.\n *\n * Empty when nothing opted in — the caller writes no file then.\n */\nexport const generatePostgresSearchDdl = (allCollections: CollectionConfig[]): string =>\n renderSearchDdl(planSchema(allCollections));\n\n/**\n * Glob patterns telling Atlas to leave the search column and its index alone.\n *\n * Without these, a desired state that omits search reads to Atlas as an\n * instruction to drop the column — taking the index and the whole search\n * feature with it on the next push.\n */\nexport const searchExcludePatterns = (allCollections: CollectionConfig[]): string[] =>\n searchExcludePatternsOf(planSchema(allCollections));\n\n/**\n * `.rebase/sql/vector.sql` — the `vector` columns, their ANN indexes and, when a\n * database declared it, the extension.\n *\n * Vector is out of Atlas's hands for a narrower reason than search: not that\n * Atlas refuses to parse the file, but that it cannot *execute* it. To compute\n * a desired state Atlas materialises `schema.sql` in a dev database, that\n * database is created empty, and Atlas empties it again at the start of every\n * run — so `VECTOR(384)` resolves against a database that structurally cannot\n * have pgvector, and every push dies with `type \"vector\" does not exist`.\n */\nexport const generatePostgresVectorDdl = (\n allCollections: CollectionConfig[],\n options: { extensions?: readonly string[] } = {}\n): string => renderVectorDdl(planSchema(allCollections, { databaseExtensions: options.extensions }));\n\n/** @see searchExcludePatterns */\nexport const vectorExcludePatterns = (allCollections: CollectionConfig[]): string[] =>\n vectorExcludePatternsOf(planSchema(allCollections));\n\n/**\n * `.rebase/sql/triggers.sql` — the `BEFORE UPDATE` triggers behind\n * `autoValue: \"on_update\"`, and the one function they share.\n *\n * A trigger is a function plus a binding, so it is carved out of Atlas's view\n * for the first of the two reasons search is. Empty when no property declares\n * `on_update` — the caller writes no file then, and removes a stale one.\n */\nexport const generatePostgresTriggersDdl = (allCollections: CollectionConfig[]): string =>\n renderTriggersDdl(planSchema(allCollections));\n\n/** @see searchExcludePatterns */\nexport const triggerExcludePatterns = (allCollections: CollectionConfig[]): string[] =>\n triggerExcludePatternsOf(planSchema(allCollections));\n\n// ── Slices of the plan other modules ask for ─────────────────────────────────\n\n/** A column a `relation` or `reference` property owns on its own table. */\nexport interface RelationalColumnPlan {\n schema: string;\n /** Bare table name, no schema prefix. */\n table: string;\n column: string;\n /** Postgres type, exactly as the DDL renderer declares it. */\n type: string;\n /** `validation.required` on the property that owns the link. */\n required: boolean;\n /**\n * True when a declared property emits this column and the relation\n * contributes only the constraint — `postId` with `columnName: \"post_id\"`\n * beside a `belongsTo` on `post_id`.\n */\n columnOwnedByProperty?: boolean;\n /** Absent when the target collection is not part of this bundle. */\n foreignKey?: ForeignKeyPlan;\n /**\n * What this column would have been called before `generateForeignKeyName`\n * learned to singularize. Carried so the boot-time ensure can notice a\n * database provisioned under the old rule; never used to name anything.\n */\n legacyColumn?: string;\n}\n\n/** The table behind a many-to-many `through` relation. */\nexport interface JunctionTablePlan {\n schema: string;\n /** Bare table name, no schema prefix. */\n table: string;\n columns: { name: string; type: string; legacyName?: string }[];\n /** Both endpoint columns plus the composite primary key. */\n createTable: string;\n foreignKeys: ForeignKeyPlan[];\n}\n\nconst planOf = (collections: CollectionConfig[]): SchemaPlan => planSchema(collections);\n\n/**\n * The FK columns the declared collections own — one entry per `relation`\n * (`belongsTo` side) or `reference` property.\n *\n * A view over the schema plan. It was a second walk of the collections beside\n * the `CREATE TABLE` one, which is how boot-ensure came to add every relation\n * column bare while `db push` made the same column `NOT NULL`.\n */\nexport const planRelationalColumns = (allCollections: CollectionConfig[]): RelationalColumnPlan[] => {\n const plan = planOf(allCollections);\n const out: RelationalColumnPlan[] = [];\n for (const table of plan.tables) {\n if (table.kind !== \"collection\") continue;\n for (const column of table.columns) {\n if (column.source.kind !== \"relation\" && column.source.kind !== \"reference\") continue;\n out.push({\n schema: table.schema,\n table: table.table,\n column: column.column,\n type: renderPgType(column.type),\n required: !column.nullable,\n columnOwnedByProperty: column.columnOwnedByProperty,\n legacyColumn: column.legacyColumn,\n foreignKey: column.foreignKey\n });\n }\n }\n return out;\n};\n\n/**\n * The junction tables a bundle's many-to-many relations imply.\n *\n * Derived from the same plan the derived RLS comes from, so a table created\n * here always has policies planned for it — a junction with row-level security\n * left off is readable and writable by every signed-in user.\n */\nexport const planJunctionTables = (allCollections: CollectionConfig[]): JunctionTablePlan[] =>\n planOf(allCollections).tables\n .filter(table => table.kind === \"junction\")\n .map(table => ({\n schema: table.schema,\n table: table.table,\n columns: table.columns.map(c => ({\n name: c.column,\n type: renderPgType(c.type),\n legacyName: c.legacyColumn\n })),\n createTable:\n `CREATE TABLE IF NOT EXISTS \"${table.schema}\".\"${table.table}\" (` +\n table.columns.map(c => `\"${c.column}\" ${renderPgType(c.type)} NOT NULL`).join(\", \") +\n `, PRIMARY KEY (${table.primaryKey.map(c => `\"${c}\"`).join(\", \")}));`,\n foreignKeys: table.columns.map(c => c.foreignKey).filter((fk): fk is ForeignKeyPlan => fk !== undefined)\n }));\n\nexport interface CollectionPolicyPlan {\n /** The table's schema (e.g. `public`, `rebase`). */\n schema: string;\n /** The bare table name, no schema prefix. */\n table: string;\n /** `schema.table` — matches the keys `readExistingSchema` returns. */\n qualified: string;\n /** `ALTER TABLE … ENABLE ROW LEVEL SECURITY;` — locked by default. */\n enableRls: string;\n /** `DROP POLICY IF EXISTS` / `CREATE POLICY` statements, in order. */\n policyStatements: string[];\n}\n\n/**\n * The per-table RLS plan for the declared collections and their junctions, as\n * executable statements — what the managed runtime applies at boot so a freshly\n * provisioned tenant database serves data instead of 401ing every read.\n *\n * The same policies `policies.sql` carries, from the same plan, so boot and\n * `db push` produce identical RLS from identical collections.\n */\nexport const planCollectionPolicies = (allCollections: CollectionConfig[]): CollectionPolicyPlan[] => {\n const plan = planOf(allCollections);\n // Collections first, then the junctions no collection declares.\n const ordered = [\n ...plan.tables.filter(t => t.kind === \"collection\"),\n ...plan.tables.filter(t => t.kind === \"junction\")\n ];\n return ordered.map(table => ({\n schema: table.schema,\n table: table.table,\n qualified: table.qualified,\n enableRls: `ALTER TABLE \"${table.schema}\".\"${table.table}\" ENABLE ROW LEVEL SECURITY;`,\n policyStatements: table.policies.flatMap(policy => renderPolicyStatements(table, policy))\n }));\n};\n\n/**\n * The `generated column → column it reads` edges a project's `search` blocks\n * imply, derived from the collections alone.\n *\n * The same edges `queryGeneratedColumnDependencies` reads out of `pg_depend`,\n * but available where no database is: `rebase db generate` writes a migration\n * that will run somewhere else, later, against a database it cannot inspect.\n * A spec's `fields[].column` is the physical column each search field starts\n * at, which is exactly what the generated expression depends on.\n */\nexport const searchColumnDependencies = (\n allCollections: CollectionConfig[]\n): GeneratedColumnDependency[] => {\n const dependencies: GeneratedColumnDependency[] = [];\n for (const table of planOf(allCollections).tables) {\n const spec = table.search;\n if (!spec) continue;\n const generated = [spec.column, ...(spec.fuzzy ? [spec.fuzzy.column] : [])];\n const sources = new Set(spec.fields.map(field => field.column));\n for (const column of generated) {\n for (const dependsOn of sources) {\n dependencies.push({ schema: table.schema, table: table.table, column, dependsOn });\n }\n }\n }\n return dependencies;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEA,IAAa,uBACT,gBACA,UAA4B,CAAC,MACpB,kBAAkB,WAAW,cAAc,GAAG,OAAO;;AAGlE,IAAa,+BAA+B,mBACxC,kBAAkB,WAAW,cAAc,CAAC;;;;;;;;;;;;;;;;;;;;AAqBhD,IAAa,6BAA6B,mBACtC,gBAAgB,WAAW,cAAc,CAAC;;;;;;;;AAS9C,IAAa,yBAAyB,mBAClC,wBAAwB,WAAW,cAAc,CAAC;;;;;;;;;;;;AAatD,IAAa,6BACT,gBACA,UAA8C,CAAC,MACtC,gBAAgB,WAAW,gBAAgB,EAAE,oBAAoB,QAAQ,WAAW,CAAC,CAAC;;AAGnG,IAAa,yBAAyB,mBAClC,wBAAwB,WAAW,cAAc,CAAC;;;;;;;;;AAUtD,IAAa,+BAA+B,mBACxC,kBAAkB,WAAW,cAAc,CAAC;;AAGhD,IAAa,0BAA0B,mBACnC,yBAAyB,WAAW,cAAc,CAAC;AAyCvD,IAAM,UAAU,gBAAgD,WAAW,WAAW;;;;;;;;AAuCtF,IAAa,sBAAsB,mBAC/B,OAAO,cAAc,CAAC,CAAC,OAClB,QAAO,UAAS,MAAM,SAAS,UAAU,CAAC,CAC1C,KAAI,WAAU;CACX,QAAQ,MAAM;CACd,OAAO,MAAM;CACb,SAAS,MAAM,QAAQ,KAAI,OAAM;EAC7B,MAAM,EAAE;EACR,MAAM,aAAa,EAAE,IAAI;EACzB,YAAY,EAAE;CAClB,EAAE;CACF,aACI,+BAA+B,MAAM,OAAO,KAAK,MAAM,MAAM,OAC7D,MAAM,QAAQ,KAAI,MAAK,IAAI,EAAE,OAAO,IAAI,aAAa,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,KAAK,IAAI,IAClF,kBAAkB,MAAM,WAAW,KAAI,MAAK,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE;CACrE,aAAa,MAAM,QAAQ,KAAI,MAAK,EAAE,UAAU,CAAC,CAAC,QAAQ,OAA6B,OAAO,KAAA,CAAS;AAC3G,EAAE;;;;;;;;;AAuBV,IAAa,0BAA0B,mBAA+D;CAClG,MAAM,OAAO,OAAO,cAAc;CAMlC,OAAO,CAHH,GAAG,KAAK,OAAO,QAAO,MAAK,EAAE,SAAS,YAAY,GAClD,GAAG,KAAK,OAAO,QAAO,MAAK,EAAE,SAAS,UAAU,CAE7C,CAAA,CAAQ,KAAI,WAAU;EACzB,QAAQ,MAAM;EACd,OAAO,MAAM;EACb,WAAW,MAAM;EACjB,WAAW,gBAAgB,MAAM,OAAO,KAAK,MAAM,MAAM;EACzD,kBAAkB,MAAM,SAAS,SAAQ,WAAU,uBAAuB,OAAO,MAAM,CAAC;CAC5F,EAAE;AACN;;;;;;;;;;;AAYA,IAAa,4BACT,mBAC8B;CAC9B,MAAM,eAA4C,CAAC;CACnD,KAAK,MAAM,SAAS,OAAO,cAAc,CAAC,CAAC,QAAQ;EAC/C,MAAM,OAAO,MAAM;EACnB,IAAI,CAAC,MAAM;EACX,MAAM,YAAY,CAAC,KAAK,QAAQ,GAAI,KAAK,QAAQ,CAAC,KAAK,MAAM,MAAM,IAAI,CAAC,CAAE;EAC1E,MAAM,UAAU,IAAI,IAAI,KAAK,OAAO,KAAI,UAAS,MAAM,MAAM,CAAC;EAC9D,KAAK,MAAM,UAAU,WACjB,KAAK,MAAM,aAAa,SACpB,aAAa,KAAK;GAAE,QAAQ,MAAM;GAAQ,OAAO,MAAM;GAAO;GAAQ;EAAU,CAAC;CAG7F;CACA,OAAO;AACX"}
|