@rebasepro/server-postgres 0.20.1-canary.g4d882ca → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PostgresBackendDriver.d.ts +33 -3
- package/dist/auth/services.d.ts +19 -1
- package/dist/{backup-cli-DSpQyqcG.js → backup-cli-Bp-ou6o2.js} +2 -2
- package/dist/{backup-cli-DSpQyqcG.js.map → backup-cli-Bp-ou6o2.js.map} +1 -1
- package/dist/{backup-service-DA7a6SUV.js → backup-service-BNLwvxuy.js} +2 -2
- package/dist/{backup-service-DA7a6SUV.js.map → backup-service-BNLwvxuy.js.map} +1 -1
- package/dist/cli.js +7 -7
- package/dist/column-plan-helpers-CpILzHJS.js.map +1 -1
- package/dist/{doctor-D5SrGJ5P.js → doctor-ByFWL_ET.js} +25 -6
- package/dist/doctor-ByFWL_ET.js.map +1 -0
- package/dist/{ensure-collection-policies-CHO0moXQ.js → ensure-collection-policies-CQGHln-y.js} +2 -2
- package/dist/{ensure-collection-policies-CHO0moXQ.js.map → ensure-collection-policies-CQGHln-y.js.map} +1 -1
- package/dist/{ensure-tables-CYMtuuOd.js → ensure-tables-BnEvEJPr.js} +2 -2
- package/dist/ensure-tables-BnEvEJPr.js.map +1 -0
- package/dist/history/HistoryService.d.ts +16 -2
- package/dist/history/ensure-history-table.d.ts +7 -1
- package/dist/index.es.js +1580 -1085
- package/dist/index.es.js.map +1 -1
- package/dist/plan-schema-Hgl62S-w.js.map +1 -1
- package/dist/{rls-bootstrap-sql-BlzsOUtz.js → rls-bootstrap-sql-BHA6jLtj.js} +4 -3
- package/dist/{rls-bootstrap-sql-BlzsOUtz.js.map → rls-bootstrap-sql-BHA6jLtj.js.map} +1 -1
- package/dist/{rls-enforcement-BV12vBwQ.js → rls-enforcement-C6Xk0lA6.js} +20 -20
- package/dist/{rls-enforcement-BV12vBwQ.js.map → rls-enforcement-C6Xk0lA6.js.map} +1 -1
- package/dist/schema/doctor-cli.js +2 -2
- package/dist/services/PersistService.d.ts +11 -0
- package/dist/services/RelationService.d.ts +39 -0
- package/dist/services/RelationWriteService.d.ts +32 -1
- package/dist/services/dataService.d.ts +2 -0
- package/dist/services/junction-writes.d.ts +58 -2
- package/dist/services/write-transaction-scope.d.ts +68 -0
- package/dist/types.d.ts +18 -0
- package/package.json +7 -7
- package/dist/doctor-D5SrGJ5P.js.map +0 -1
- package/dist/ensure-tables-CYMtuuOd.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-schema-Hgl62S-w.js","names":[],"sources":["../src/schema/search-column.ts","../src/schema/relation-names.ts","../src/schema/plan/updated-at-trigger.ts","../src/schema/plan/plan-schema.ts"],"sourcesContent":["/**\n * The one place a collection's `search` block becomes SQL.\n *\n * Four things describe a Postgres table in this codebase — the DDL generator,\n * the Drizzle schema generator, the runtime table builder for BaaS mode, and\n * the boot-time schema ensure — and each of them has, at some point, described\n * a column differently from the others. The `varchar(255)` note in\n * `generate-postgres-ddl-logic` is one such scar: the same property produced a\n * capped column down one path and an uncapped one down the other, and nothing\n * failed until a user hit the cap.\n *\n * So the search column is not implemented four times. It is computed once,\n * here, and every generator renders the same {@link SearchColumnSpec}. There is\n * a test asserting exactly that (`search-column-contract.test.ts`); the point of\n * this module is that the test has something to assert *about*.\n *\n * ## Why the expressions look the way they do\n *\n * A `GENERATED ALWAYS AS … STORED` expression must be strictly IMMUTABLE, and\n * Postgres is stricter here than intuition. Verified against PostgreSQL 18:\n *\n * | expression | immutable |\n * |-----------------------------------------|-----------|\n * | `to_tsvector('spanish', col)` | yes |\n * | `to_tsvector(col)` (1-arg) | **no** — depends on `default_text_search_config` |\n * | `array_to_string(col, ' ')` | **no** |\n * | `col::text` on `text[]` | **no** |\n * | `to_jsonb(col)` | **no** |\n * | `unaccent(col)` | **no** — dictionary lookup is STABLE |\n * | `jsonb_to_tsvector('spanish', j, '[\"string\"]')` | yes |\n * | `setweight(...) || setweight(...)` | yes |\n *\n * Three of the four things a real search column needs are therefore unavailable\n * directly, which is why {@link searchHelperFunctions} exists: each wraps a\n * stable built-in in an SQL function declared IMMUTABLE. That declaration is a\n * promise, and it is a true one for these three — array joining, JSON string\n * extraction and accent folding are all deterministic for a given input; the\n * built-ins are marked stable only because they must account for element types\n * and dictionaries in general.\n *\n * The alternative was to skip `unaccent` and text arrays entirely. That is not\n * a real option in an accented language: Postgres stems `auditoría` to\n * `auditor` and `auditoria` to `auditori` — *different lexemes* — so a query\n * typed without accents misses every row that carries them.\n */\nimport {\n CollectionConfig,\n Property,\n StringProperty,\n ArrayProperty,\n MapProperty,\n SearchConfig,\n SearchField,\n SearchWeight,\n isPostgresCollectionConfig,\n DEFAULT_SEARCH_COLUMN,\n DEFAULT_SEARCH_LANGUAGE,\n DEFAULT_SEARCH_WEIGHT,\n DEFAULT_FUZZY_THRESHOLD\n} from \"@rebasepro/types\";\nimport { createHash } from \"node:crypto\";\nimport { getTableName } from \"@rebasepro/common\";\nimport { toSnakeCase, toPostgresIdentifier } from \"@rebasepro/utils\";\n\n/** Schema-qualified so a collection outside `public` still resolves them. */\nconst HELPER_SCHEMA = \"public\";\n\n/**\n * Names of the helper functions. Frozen: they are recorded in the stored\n * generation expression of every search column ever created, so renaming one\n * orphans every table that already has a search column.\n */\nexport const SEARCH_TEXT_FN = `${HELPER_SCHEMA}.rebase_search_text`;\nexport const SEARCH_UNACCENT_FN = `${HELPER_SCHEMA}.rebase_search_unaccent`;\n\n/** How a declared path reaches text, which decides the SQL that extracts it. */\ntype FieldKind = \"text\" | \"text_array\" | \"jsonb\";\n\n/** One resolved field: where it lives, how to read it, what it is worth. */\nexport interface ResolvedSearchField {\n /** The path exactly as the author wrote it, for error messages. */\n path: string;\n /** The physical column the path starts at. */\n column: string;\n /** Dotted remainder addressed inside a JSONB column, if any. */\n jsonPath: string[];\n kind: FieldKind;\n weight: SearchWeight;\n /** The `setweight(to_tsvector(…), 'X')` term this field contributes. */\n sql: string;\n /** The plain-text term this field contributes, for the fuzzy column. */\n textSql: string;\n}\n\n/** Everything the generators need to render one collection's search column. */\nexport interface SearchColumnSpec {\n schema: string;\n table: string;\n /** The generated `tsvector` column. */\n column: string;\n language: string;\n unaccent: boolean;\n fields: ResolvedSearchField[];\n /** Body of `GENERATED ALWAYS AS ( … ) STORED` for the tsvector column. */\n expression: string;\n indexName: string;\n /** Extensions that must exist before the column can be created. */\n extensions: string[];\n fuzzy?: {\n column: string;\n expression: string;\n indexName: string;\n threshold: number;\n };\n}\n\n/** Raised when a `search` block names something that cannot be searched. */\nexport class SearchConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SearchConfigError\";\n }\n}\n\n/** The `search` block of a collection, or undefined when it has none. */\nexport const getSearchConfig = (collection: CollectionConfig): SearchConfig | undefined =>\n isPostgresCollectionConfig(collection) ? collection.search : undefined;\n\n/**\n * Refuse a `search` block on a collection this engine does not store.\n *\n * The type only permits one on a `PostgresCollectionConfig`, so TypeScript\n * already stops the ordinary case. This catches the rest — a JS config, a cast,\n * a collection whose `engine` was changed after the block was written — because\n * the alternative is the exact failure the block exists to prevent: a developer\n * who declared what to index, saw no error, and got the substring fallback.\n *\n * Called with *every* collection, before the Postgres ones are filtered out.\n */\nexport const assertSearchIsPostgresOnly = (collections: CollectionConfig[]): void => {\n for (const collection of collections) {\n if (isPostgresCollectionConfig(collection)) continue;\n if (!(collection as { search?: unknown }).search) continue;\n const engine = (collection as { engine?: string }).engine ?? \"non-postgres\";\n throw new SearchConfigError(\n `${collection.slug}.search: full-text search is a Postgres feature, and this collection is served by \\`${engine}\\`. ` +\n \"Remove the block — it would otherwise look configured while `.search()` kept using the default substring match.\"\n );\n }\n};\n\nconst columnNameOf = (propName: string, prop?: Property | null): string =>\n prop && \"columnName\" in prop && typeof prop.columnName === \"string\" ? prop.columnName : toSnakeCase(propName);\n\n/**\n * Classify a property for search purposes.\n *\n * Deliberately narrower than the schema plan's `PgType`: search only cares\n * whether a value reaches text, and the mapping from property to *physical*\n * type is asserted against the plan in the contract test rather than duplicated\n * here.\n *\n * Returns null for anything that is not text-bearing, which the caller turns\n * into a boot error naming the property.\n */\nconst classify = (prop: Property): { kind: FieldKind; reason?: string } | null => {\n switch (prop.type) {\n case \"string\": {\n const sp = prop as StringProperty;\n if (sp.enum) {\n return { kind: \"text\", reason: \"enum\" };\n }\n if (sp.isId === \"uuid\" || sp.columnType === \"uuid\") {\n return { kind: \"text\", reason: \"uuid\" };\n }\n return { kind: \"text\" };\n }\n case \"map\": {\n const mp = prop as MapProperty;\n // A `json` column is not `jsonb`, and the cast between them is not\n // immutable. Declaring `columnType: \"json\"` puts the value out of\n // reach of a generated column.\n if (mp.columnType === \"json\") return { kind: \"jsonb\", reason: \"json\" };\n return { kind: \"jsonb\" };\n }\n case \"array\": {\n const ap = prop as ArrayProperty;\n let colType = ap.columnType;\n if (!colType && ap.of && !Array.isArray(ap.of)) {\n const of = ap.of as Property;\n if (of.type === \"string\") colType = \"text[]\";\n else if (of.type === \"number\") colType = of.validation?.integer ? \"integer[]\" : \"numeric[]\";\n else if (of.type === \"boolean\") colType = \"boolean[]\";\n }\n if (colType === \"text[]\") return { kind: \"text_array\" };\n if (colType === \"json\") return { kind: \"jsonb\", reason: \"json\" };\n if (colType === \"integer[]\" || colType === \"boolean[]\" || colType === \"numeric[]\") {\n return { kind: \"text_array\", reason: \"non_text_array\" };\n }\n // Everything else lands in JSONB, which the JSON extractor handles.\n return { kind: \"jsonb\" };\n }\n default:\n return null;\n }\n};\n\nconst normalize = (inner: string, unaccent: boolean): string =>\n unaccent ? `${SEARCH_UNACCENT_FN}(${inner})` : inner;\n\n/** SQL reading one field as plain text, before normalization. */\nconst rawTextSql = (field: { column: string; jsonPath: string[]; kind: FieldKind }): string => {\n const col = `\"${field.column}\"`;\n if (field.kind === \"text\") return `coalesce(${col}, '')`;\n if (field.kind === \"text_array\") return `${SEARCH_TEXT_FN}(coalesce(${col}, '{}'::text[]))`;\n // JSONB, optionally addressed at a path inside the document.\n const target = field.jsonPath.length === 0\n ? col\n : field.jsonPath.length === 1\n ? `${col} -> ${quote(field.jsonPath[0])}`\n : `${col} #> ${quote(`{${field.jsonPath.join(\",\")}}`)}`;\n return `${SEARCH_TEXT_FN}(coalesce(${target}, '{}'::jsonb))`;\n};\n\nconst quote = (v: string): string => `'${v.replace(/'/g, \"''\")}'`;\n\n/**\n * Resolve and validate one declared field path.\n *\n * A path that does not resolve throws. The whole point of an explicit block is\n * that the author knows what is indexed; a silently dropped field would make it\n * a guess again, and the failure — a search that returns nothing for content\n * that is plainly in the row — is invisible from the outside.\n */\nconst resolveField = (\n entry: string | SearchField,\n collection: CollectionConfig,\n cfg: SearchConfig\n): ResolvedSearchField => {\n const path = typeof entry === \"string\" ? entry : entry.path;\n const weight = (typeof entry === \"string\" ? undefined : entry.weight) ?? DEFAULT_SEARCH_WEIGHT;\n const where = `${collection.slug}.search`;\n\n if (!path || typeof path !== \"string\") {\n throw new SearchConfigError(`${where}: every entry in \\`fields\\` needs a property path.`);\n }\n\n const [head, ...rest] = path.split(\".\");\n const prop = collection.properties?.[head] as Property | undefined;\n if (!prop) {\n const known = Object.keys(collection.properties ?? {}).join(\", \");\n throw new SearchConfigError(\n `${where}: \"${path}\" starts at property \"${head}\", which this collection does not declare. Known properties: ${known}.`\n );\n }\n\n const classified = classify(prop);\n if (!classified) {\n throw new SearchConfigError(\n `${where}: \"${path}\" is a \\`${prop.type}\\` property, which holds no text to search. ` +\n `Searchable kinds are \\`string\\`, \\`string[]\\` and \\`map\\` (or a path inside one).`\n );\n }\n if (classified.reason === \"enum\") {\n throw new SearchConfigError(\n `${where}: \"${path}\" is an enum. Enums are a fixed vocabulary — filter on them with \\`where\\` instead, which is exact and uses an index.`\n );\n }\n if (classified.reason === \"uuid\") {\n throw new SearchConfigError(\n `${where}: \"${path}\" is a UUID column. Look it up by id rather than searching it.`\n );\n }\n if (classified.reason === \"json\") {\n throw new SearchConfigError(\n `${where}: \"${path}\" is a \\`json\\` column, and the cast from \\`json\\` to \\`jsonb\\` is not immutable, so it cannot feed a generated column. Declare the property as \\`jsonb\\` (the default) to search it.`\n );\n }\n if (classified.reason === \"non_text_array\") {\n throw new SearchConfigError(\n `${where}: \"${path}\" is an array of numbers or booleans. Only \\`string[]\\` carries text to search.`\n );\n }\n\n if (rest.length > 0 && classified.kind !== \"jsonb\") {\n throw new SearchConfigError(\n `${where}: \"${path}\" addresses a path inside \"${head}\", but \"${head}\" is a \\`${prop.type}\\` property, not a \\`map\\`. Only map properties have paths inside them.`\n );\n }\n\n const column = columnNameOf(head, prop);\n const field = { column, jsonPath: rest, kind: classified.kind };\n const textSql = normalize(rawTextSql(field), cfg.unaccent === true);\n const language = cfg.language ?? DEFAULT_SEARCH_LANGUAGE;\n\n return {\n path,\n column,\n jsonPath: rest,\n kind: classified.kind,\n weight,\n sql: `setweight(to_tsvector(${quote(language)}, ${textSql}), ${quote(weight)})`,\n textSql\n };\n};\n\n/**\n * Build the full spec for a collection, or undefined when it has not opted in.\n *\n * Throws {@link SearchConfigError} on a config that cannot be honoured. Callers\n * at boot surface that as a startup failure — a search block that half-works is\n * worse than one that refuses.\n */\nexport const buildSearchColumnSpec = (collection: CollectionConfig): SearchColumnSpec | undefined => {\n const cfg = getSearchConfig(collection);\n if (!cfg) return undefined;\n\n if (!Array.isArray(cfg.fields) || cfg.fields.length === 0) {\n throw new SearchConfigError(\n `${collection.slug}.search: \\`fields\\` is empty. Name the properties to index, or remove the \\`search\\` block to keep the default ILIKE behaviour.`\n );\n }\n\n const table = getTableName(collection);\n const schema = isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\";\n const column = cfg.column ?? DEFAULT_SEARCH_COLUMN;\n\n if (collection.properties?.[column]) {\n throw new SearchConfigError(\n `${collection.slug}.search: the generated column \"${column}\" collides with a declared property of the same name. Set \\`search.column\\` to something else.`\n );\n }\n\n const fields = cfg.fields.map(entry => resolveField(entry, collection, cfg));\n\n const seen = new Set<string>();\n for (const f of fields) {\n if (seen.has(f.path)) {\n throw new SearchConfigError(`${collection.slug}.search: \"${f.path}\" is listed twice.`);\n }\n seen.add(f.path);\n }\n\n const extensions: string[] = [];\n if (cfg.unaccent) extensions.push(\"unaccent\");\n if (cfg.fuzzy) extensions.push(\"pg_trgm\");\n\n const spec: SearchColumnSpec = {\n schema,\n table,\n column,\n language: cfg.language ?? DEFAULT_SEARCH_LANGUAGE,\n unaccent: cfg.unaccent === true,\n fields,\n expression: fields.map(f => f.sql).join(\" || \"),\n indexName: toPostgresIdentifier(`${table}_${column}_gin`),\n extensions\n };\n\n if (cfg.fuzzy) {\n const fuzzyColumn = `${column}_text`;\n if (collection.properties?.[fuzzyColumn]) {\n throw new SearchConfigError(\n `${collection.slug}.search: \\`fuzzy\\` needs the column \"${fuzzyColumn}\", which collides with a declared property. Set \\`search.column\\` to something else.`\n );\n }\n spec.fuzzy = {\n column: fuzzyColumn,\n // Concatenated with spaces so a trigram never spans two fields.\n expression: fields.map(f => f.textSql).join(\" || ' ' || \"),\n indexName: toPostgresIdentifier(`${table}_${fuzzyColumn}_trgm`),\n threshold: cfg.fuzzyThreshold ?? DEFAULT_FUZZY_THRESHOLD\n };\n }\n\n return spec;\n};\n\n/**\n * The IMMUTABLE wrappers the generated expressions call.\n *\n * `CREATE OR REPLACE` so a boot against an existing database is a no-op rather\n * than an error, and idempotent for the same reason every other boot-time DDL\n * statement here is.\n *\n * The bodies are stable built-ins wrapped in an immutable promise — see the\n * module comment for why that promise is sound. `STRICT` matters: it makes NULL\n * in mean NULL out without executing the body, which is what the `coalesce` at\n * each call site then absorbs.\n */\nexport const searchHelperFunctions = (spec: SearchColumnSpec): string[] => {\n const statements: string[] = [\n // text[] → \" \"-joined text.\n `CREATE OR REPLACE FUNCTION ${SEARCH_TEXT_FN}(text[]) RETURNS text\\n` +\n ` LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS\\n` +\n ` $$ SELECT array_to_string($1, ' ') $$;`,\n // jsonb → every string value at or below the node, space-joined. Keys\n // are not values: indexing them would make `certifications` itself a\n // search term on every row that has the field at all.\n `CREATE OR REPLACE FUNCTION ${SEARCH_TEXT_FN}(jsonb) RETURNS text\\n` +\n ` LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS\\n` +\n ` $$ SELECT coalesce(string_agg(v, ' '), '')\\n` +\n ` FROM jsonb_array_elements_text(jsonb_path_query_array($1, 'strict $.**?(@.type() == \"string\")')) AS v $$;`\n ];\n if (spec.unaccent) {\n // The two-argument form with an explicit dictionary is the one that can\n // honestly be called immutable: the single-argument form resolves the\n // dictionary through the current search_path at call time.\n statements.push(\n `CREATE OR REPLACE FUNCTION ${SEARCH_UNACCENT_FN}(text) RETURNS text\\n` +\n ` LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS\\n` +\n ` $$ SELECT ${HELPER_SCHEMA}.unaccent('${HELPER_SCHEMA}.unaccent'::regdictionary, $1) $$;`\n );\n }\n return statements;\n};\n\n/**\n * `CREATE EXTENSION` statements the spec's expressions depend on.\n *\n * `WITH SCHEMA public` is load-bearing, not tidiness. An unqualified\n * `CREATE EXTENSION` installs into the first schema on `search_path`, which\n * defaults to `\"$user\", public` — and the scaffold's database role is named\n * `rebase`, the same as the schema the generator creates one statement earlier.\n * So the moment that schema exists, `CREATE EXTENSION unaccent` puts the\n * dictionary in `rebase`, and every reference to `public.unaccent` below fails\n * with \"text search dictionary does not exist\". Observed, not theorised.\n */\nexport const searchExtensionStatements = (spec: SearchColumnSpec): string[] =>\n spec.extensions.map(e => `CREATE EXTENSION IF NOT EXISTS ${e} WITH SCHEMA ${HELPER_SCHEMA};`);\n\n/**\n * Everything after the column name — the type and the generation expression.\n *\n * Split out because four emitters need it and only two of them have a place to\n * put the name: `CREATE TABLE` and `ADD COLUMN` write `\"col\" <this>`, while the\n * schema plan carries it as the column's SQL definition and the boot-time\n * rebuild statement interpolates it on its own.\n */\nexport const searchColumnTypeSql = (expression: string, kind: \"tsvector\" | \"text\"): string =>\n `${kind} GENERATED ALWAYS AS (${expression}) STORED`;\n\n/** The column definition as it appears inside `CREATE TABLE`. */\nexport const searchColumnDefinition = (spec: SearchColumnSpec): string =>\n `\"${spec.column}\" ${searchColumnTypeSql(spec.expression, \"tsvector\")}`;\n\n/** The fuzzy column definition, when the spec asks for one. */\nexport const fuzzyColumnDefinition = (spec: SearchColumnSpec): string | undefined =>\n spec.fuzzy ? `\"${spec.fuzzy.column}\" ${searchColumnTypeSql(spec.fuzzy.expression, \"text\")}` : undefined;\n\n/**\n * Index statements for the spec.\n *\n * `CONCURRENTLY` is deliberately *not* used here. This form is emitted into a\n * SQL file replayed as one unit — a migration, or `search.sql` — where a\n * concurrent build is not allowed. The boot-time ensure path runs statement by\n * statement against tables that are live and populated, and uses the\n * concurrent form instead; see `ensureSearchColumns`.\n */\nexport const searchIndexStatements = (spec: SearchColumnSpec): string[] => {\n const statements = [\n `CREATE INDEX IF NOT EXISTS \"${spec.indexName}\" ON \"${spec.schema}\".\"${spec.table}\" USING GIN (\"${spec.column}\");`\n ];\n if (spec.fuzzy) {\n statements.push(\n // The operator class is resolved through `search_path` like any\n // other object, so it is qualified for the same reason the\n // extension is installed explicitly.\n `CREATE INDEX IF NOT EXISTS \"${spec.fuzzy.indexName}\" ON \"${spec.schema}\".\"${spec.table}\" USING GIN (\"${spec.fuzzy.column}\" ${HELPER_SCHEMA}.gin_trgm_ops);`\n );\n }\n return statements;\n};\n\n// ── Telling a changed `search` block from an unchanged one ──────────────────\n\n/**\n * Marker on the comment of every generated search column this module creates.\n *\n * Versioned because the fingerprint below is only comparable against itself: a\n * future change to how it is computed has to read as \"not stamped by this\n * version\" rather than as drift on every existing column.\n */\nexport const SEARCH_STAMP_PREFIX = \"rebase:search:v1:\";\n\n/**\n * A stable fingerprint of one generated column's expression.\n *\n * Why a stamp rather than reading the expression back: Postgres stores a\n * generated column's expression *parsed*, and hands it back deparsed — casts\n * made explicit, identifiers requoted, schema qualifications added or dropped\n * according to `search_path`. Comparing that text to the text we generated\n * would report drift on wording, and this comparison decides whether a boot\n * refuses, so a false positive is an outage. The stamp is written by the same\n * code that writes the column, so equality means what it says.\n */\nexport const searchExpressionFingerprint = (expression: string): string =>\n `${SEARCH_STAMP_PREFIX}${createHash(\"sha256\").update(expression).digest(\"hex\").slice(0, 16)}`;\n\n/** One generated column, with the fingerprint that identifies its expression. */\nexport interface SearchColumnStamp {\n column: string;\n /** The expression the column is generated from. */\n expression: string;\n fingerprint: string;\n /** `COMMENT ON COLUMN …`, which is where the fingerprint is recorded. */\n sql: string;\n}\n\n/**\n * The stamps for a spec's generated columns — one per column, never shared.\n *\n * Per column on purpose: turning `fuzzy` on adds a second column and changes\n * nothing about the first, and a spec-wide fingerprint would report the\n * untouched `tsvector` column as drifted and refuse a boot over a change that\n * is purely additive.\n */\nexport const searchColumnStamps = (spec: SearchColumnSpec): SearchColumnStamp[] => {\n const stamp = (column: string, expression: string): SearchColumnStamp => {\n const fingerprint = searchExpressionFingerprint(expression);\n return {\n column,\n expression,\n fingerprint,\n sql: `COMMENT ON COLUMN \"${spec.schema}\".\"${spec.table}\".\"${column}\" IS ${quote(fingerprint)};`\n };\n };\n const stamps = [stamp(spec.column, spec.expression)];\n if (spec.fuzzy) stamps.push(stamp(spec.fuzzy.column, spec.fuzzy.expression));\n return stamps;\n};\n\n/**\n * The same drift check as the boot ensure, for the SQL file.\n *\n * Needed because {@link searchColumnStamps} would otherwise *launder* drift on\n * the migration path: `ADD COLUMN IF NOT EXISTS` does nothing to a column that\n * exists, so a re-generated `search.sql` would stamp a stale column with the\n * new block's fingerprint and the next boot would find them in agreement.\n * Guarding first means the file refuses instead — `rebase db push` is attended,\n * and the operator reading the failure is the person who changed the block.\n */\nexport const searchStampGuards = (spec: SearchColumnSpec): string[] =>\n searchColumnStamps(spec).map(stamp => {\n const relation = quote(`\"${spec.schema}\".\"${spec.table}\"`);\n return `DO $rebase_search$\nDECLARE recorded text;\nBEGIN\n SELECT col_description(a.attrelid, a.attnum) INTO recorded\n FROM pg_attribute a\n WHERE a.attrelid = ${relation}::regclass AND a.attname = ${quote(stamp.column)} AND NOT a.attisdropped;\n IF recorded LIKE ${quote(`${SEARCH_STAMP_PREFIX}%`)} AND recorded <> ${quote(stamp.fingerprint)} THEN\n RAISE EXCEPTION 'Rebase: the search block for ${spec.schema}.${spec.table} changed after the generated column \"${stamp.column}\" was built (recorded %, expected ${stamp.fingerprint}). Postgres cannot alter a generated expression in place. Drop the column and re-apply this file — it rewrites the table and rebuilds the index: ALTER TABLE ${relation.slice(1, -1)} DROP COLUMN \"${stamp.column}\";', recorded;\n END IF;\nEND\n$rebase_search$;`;\n });\n\n/**\n * The index names the spec creates.\n *\n * Needed by name, not just by statement, so Atlas can be told to exclude them\n * from its diff — see `searchExcludePatterns`.\n */\nexport const searchIndexNames = (spec: SearchColumnSpec): string[] =>\n spec.fuzzy ? [spec.indexName, spec.fuzzy.indexName] : [spec.indexName];\n\n// ── Keeping the generated columns out of responses ──────────────────────────\n\n/**\n * The generated column names a collection's search block adds, if any.\n *\n * These are physical columns on the table, so `SELECT *` returns them. They are\n * an index in column form — a list of lexeme positions, or a concatenation of\n * every searchable field on the row — and nothing outside the query planner has\n * any use for them. Left in, every list response carries a second, larger copy\n * of the row's text.\n */\nexport const searchColumnNames = (collection: CollectionConfig): string[] => {\n let spec: SearchColumnSpec | undefined;\n try {\n spec = buildSearchColumnSpec(collection);\n } catch {\n // A malformed block is reported at boot, loudly. A read is the wrong\n // place to raise it a second time, and returning the row without the\n // exclusion would be worse than returning it with.\n return [];\n }\n if (!spec) return [];\n return spec.fuzzy ? [spec.column, spec.fuzzy.column] : [spec.column];\n};\n\n/**\n * True for a column whose type only ever holds a search index.\n *\n * Independent of any collection config on purpose: an introspected database\n * (BaaS mode) can carry a `tsvector` column this framework never created —\n * Pagila's `film.fulltext` is the canonical one — and it should not be returned\n * to callers either. `isDerivedIndexColumn` already keeps such a column out of\n * the *properties*; this keeps it out of the *rows*.\n */\nexport const isSearchIndexColumn = (column: { getSQLType?: () => string }): boolean => {\n const sqlType = typeof column?.getSQLType === \"function\" ? column.getSQLType().toLowerCase() : \"\";\n return sqlType === \"tsvector\" || sqlType === \"tsquery\";\n};\n\n/**\n * A drizzle select projection over `table` with the search columns dropped.\n *\n * Returns undefined when nothing needs dropping, so the common case keeps using\n * a plain `select()` and this stays invisible in the generated SQL.\n */\nexport const visibleColumnProjection = (\n tableColumns: Record<string, { getSQLType?: () => string }> | undefined,\n collection?: CollectionConfig\n): Record<string, unknown> | undefined => {\n const excluded = excludedColumnNames(tableColumns, collection);\n if (!tableColumns || excluded.length === 0) return undefined;\n const projection: Record<string, unknown> = {};\n for (const [name, column] of Object.entries(tableColumns)) {\n if (!excluded.includes(name)) projection[name] = column;\n }\n return projection;\n};\n\n/** The same exclusion as a drizzle `db.query` `columns` denylist. */\nexport const hiddenColumnsOption = (\n tableColumns: Record<string, { getSQLType?: () => string }> | undefined,\n collection?: CollectionConfig\n): Record<string, false> | undefined => {\n const excluded = excludedColumnNames(tableColumns, collection);\n if (excluded.length === 0) return undefined;\n return Object.fromEntries(excluded.map(name => [name, false as const]));\n};\n\n/**\n * The columns to keep out of a response, by name.\n *\n * `tableColumns` is whatever `getTableColumns` returned, which is `undefined`\n * for anything that is not a real drizzle table — a stub in a test, a derived\n * or nested path with no table behind it. Nothing to exclude is the right\n * answer there, and it has to be an answer rather than a throw: this runs on\n * the read path of every collection, opted in or not.\n */\nconst excludedColumnNames = (\n tableColumns: Record<string, { getSQLType?: () => string }> | undefined,\n collection?: CollectionConfig\n): string[] => {\n if (!tableColumns || typeof tableColumns !== \"object\") return [];\n const byName = new Set(collection ? searchColumnNames(collection) : []);\n return Object.keys(tableColumns).filter(\n name => byName.has(name) || isSearchIndexColumn(tableColumns[name])\n );\n};\n","import { CollectionConfig, ResolvedRelation } from \"@rebasepro/types\";\nimport { fieldKeyForColumn, getTableName } from \"@rebasepro/common\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\n\n/**\n * The `relationName` both sides of a link must agree on.\n *\n * Drizzle pairs an owning `one()` with its inverse `many()` by this string and\n * by nothing else, and the two sides are computed by different callers holding\n * different collections — so the rule has to be derivable from either end. It\n * is: name the link after the table that carries the foreign key and the field\n * key that column is served under.\n *\n * owning (belongsTo) → `{thisTable}_{fieldKey(localKey)}`\n * inverse (hasMany/hasOne) → `{targetTable}_{fieldKey(foreignKeyOnTarget)}`\n *\n * Both spellings of `jobs.company` produce `jobs_companyId`. A many-to-many is\n * named through its junction wiring and a `via` chain is not a Drizzle relation\n * at all, so both keep the local name.\n *\n * The field key rather than the column: the Drizzle object is keyed by the wire\n * name (`fieldKeyForColumn` is the one definition of it), and a name built from\n * the column would differ between a collection that declares `columnName` and\n * one that does not, for the same link.\n *\n * **This is the one definition.** `generate-drizzle-schema-logic.ts` holds a\n * private copy (`computeSharedRelationName`) that this was lifted from verbatim;\n * the generator should import this instead, so the file it writes and the\n * relations the runtime builds from the live catalogue cannot drift apart.\n */\nexport function sharedRelationName(\n relation: ResolvedRelation,\n sourceCollection: CollectionConfig\n): string {\n const fallback = (): string => {\n if (relation.relationName) return relation.relationName;\n try {\n return toSnakeCase(relation.target().slug);\n } catch {\n return relation.relationName;\n }\n };\n\n if (relation.kind === \"belongsTo\") {\n return `${getTableName(sourceCollection)}_${fieldKeyForColumn(sourceCollection, relation.localKey)}`;\n }\n\n if (relation.kind === \"hasMany\" || relation.kind === \"hasOne\") {\n try {\n const target = relation.target();\n return `${getTableName(target)}_${fieldKeyForColumn(target, relation.foreignKeyOnTarget)}`;\n } catch {\n return fallback();\n }\n }\n\n return fallback();\n}\n","/**\n * `autoValue: \"on_update\"`, as a database trigger.\n *\n * ## Why a trigger and not only the driver\n *\n * The driver stamps `updated_at` on every write it makes, and it has to keep\n * doing so: a `beforeSave` hook reads the row it is about to persist, and a\n * value that only appears once Postgres has written it is a value the hook\n * cannot see.\n *\n * But the driver is not the only writer. A seed script, a migration backfill, a\n * `psql` session, an admin fixing one row by hand — every one of those leaves\n * `updated_at` at whatever it was, and the column then says the row has not\n * changed since a date that is simply wrong. Anything reading it to decide what\n * to re-index, re-sync or re-send skips the row. The declaration says \"this\n * column tracks the last write\"; only the database can make that true.\n *\n * So both: the app stamps so hooks see the value, and a `BEFORE UPDATE` trigger\n * stamps so every other writer does too. They agree because they both mean\n * \"now\" — the trigger's `now()` is the transaction's start time, which is the\n * same instant the driver's own timestamp is written under.\n *\n * ## Why it is carved out of Atlas\n *\n * Atlas's free tier refuses to *parse* a desired-state file containing a\n * function (\"functions and procedures are available to logged-in users only\"),\n * and a trigger is a function plus a binding. This is the same arrangement the\n * search helpers already have: excluded from Atlas's view by pattern, written\n * to a file Rebase applies itself, applied at boot by the schema ensure, and\n * appended to migrations by the CLI so a replay is complete.\n *\n * ## Why one function for every column\n *\n * The column name arrives as a trigger argument (`TG_ARGV[0]`), so a project\n * with forty `updated_at` columns installs one function and forty bindings\n * rather than forty near-identical functions. `jsonb_populate_record(NEW, …)`\n * is the documented way to set a field of a row whose type is not known until\n * run time; it takes `NEW` as the row template, so the value is cast back to\n * whatever the column actually is — a `timestamptz`, or a `date` if that is\n * what the property declared.\n */\nimport { REBASE_SCHEMA } from \"@rebasepro/types\";\nimport type { TriggerPlan } from \"./types\";\n\n/** The one trigger function, qualified. A frozen derived name. */\nexport const SET_UPDATED_AT_FN = `${REBASE_SCHEMA}.set_updated_at`;\n\n/**\n * `CREATE OR REPLACE FUNCTION`, so replaying it against a database that already\n * has it is a no-op — this is emitted into a file that runs on every push and\n * is appended to migrations that run against databases at any stage of life.\n */\nexport const setUpdatedAtFunction = (): string =>\n `CREATE OR REPLACE FUNCTION ${SET_UPDATED_AT_FN}() RETURNS trigger\\n` +\n \"LANGUAGE plpgsql AS $$\\n\" +\n \"BEGIN\\n\" +\n \" NEW := jsonb_populate_record(NEW, jsonb_build_object(TG_ARGV[0], now()));\\n\" +\n \" RETURN NEW;\\n\" +\n \"END;\\n\" +\n \"$$;\";\n\n/**\n * `DROP TRIGGER IF EXISTS` before the `CREATE`.\n *\n * `CREATE OR REPLACE TRIGGER` exists only on Postgres 14+, and this runs\n * against whatever a self-hosted project points at. Two statements rather than\n * one is also what the boot-time applier needs: it issues DDL one statement at\n * a time over the extended query protocol, which forbids multiple commands in\n * one execute.\n */\nexport const dropTriggerStatement = (plan: TriggerPlan): string =>\n `DROP TRIGGER IF EXISTS \"${plan.name}\" ON \"${plan.schema}\".\"${plan.table}\";`;\n\nexport const createTriggerStatement = (plan: TriggerPlan): string =>\n `CREATE TRIGGER \"${plan.name}\" BEFORE UPDATE ON \"${plan.schema}\".\"${plan.table}\" ` +\n `FOR EACH ROW EXECUTE FUNCTION ${SET_UPDATED_AT_FN}('${plan.column.replace(/'/g, \"''\")}');`;\n\n/** Both statements, in the order they must run. */\nexport const triggerStatements = (plan: TriggerPlan): string[] =>\n [dropTriggerStatement(plan), createTriggerStatement(plan)];\n","/**\n * The only place a `Property` is read for schema purposes.\n *\n * Everything that used to `switch (prop.type)` — the Drizzle generator, the DDL\n * generator, the boot-time ensure — now renders the {@link SchemaPlan} this\n * produces. See `./types.ts` for why.\n *\n * Pure and synchronous. It reads collections and nothing else: no database, no\n * filesystem, no process-wide registry. The one fact about the world it needs\n * (which extensions a project's databases gave Rebase leave to install) is\n * passed in, because a plan that depended on whatever module happened to be\n * imported would not be comparable with itself.\n *\n * It **throws** rather than guessing. A configuration no renderer can honour —\n * an empty enum, two `isId` properties, `isId: \"cuid\"`, an unknown\n * `columnType`, a relation to a collection that is not in the bundle, a\n * `search` block on a collection Postgres does not store — used to fail\n * differently in each of the three emitters, or not at all, and the failure\n * landed at `CREATE TABLE` time on a managed tenant with nobody in the loop.\n * Here it lands once, with the collection and the property named.\n */\nimport {\n isPostgresCollectionConfig,\n isManyToMany,\n REBASE_SCHEMA,\n type ArrayProperty,\n type CollectionConfig,\n type DateProperty,\n type MapProperty,\n type NumberProperty,\n type Properties,\n type Property,\n type ReferenceProperty,\n type RelationProperty,\n type ResolvedRelation,\n type SecurityRule,\n type StringProperty,\n type VectorProperty,\n hasForeignKeyOnTarget\n} from \"@rebasepro/types\";\nimport {\n fieldKeyForColumn,\n findRelation,\n getEffectiveSecurityRules,\n getEnumVarName,\n getInjectedSecurityRules,\n getTenantConfig,\n TENANT_INDEX_REASON,\n getJunctionCollectionConfig,\n getJunctionSecurityRules,\n getTableName,\n getTableVarName,\n policyToPostgres,\n relationalCollections,\n resolveCollectionRelations,\n resolveJunctionSpecs,\n resolveStringColumnLength,\n securityRuleToConditions\n} from \"@rebasepro/common\";\nimport {\n generateForeignKeyName,\n getPolicyNamesForRule,\n legacyForeignKeyName,\n toPostgresIdentifier,\n toSnakeCase\n} from \"@rebasepro/utils\";\nimport {\n assertSinglePrimaryKey,\n declaresEnumType,\n enumLabelsOf,\n getPrimaryKeyName,\n getPrimaryKeyProp,\n idColumnDefault,\n isIdProperty,\n resolveColumnName\n} from \"../column-plan-helpers\";\nimport {\n AUTH_USERS_COLUMNS,\n authUsersColumnDefinition,\n authUsersColumnSql,\n isAuthCollection\n} from \"../auth-users-columns\";\nimport {\n assertSearchIsPostgresOnly,\n buildSearchColumnSpec,\n searchColumnTypeSql,\n searchExtensionStatements,\n searchHelperFunctions\n} from \"../search-column\";\nimport {\n buildVectorColumnSpecs,\n buildVectorIndexPlan,\n vectorExtensionDeclared,\n vectorExtensionStatement\n} from \"../vector-index\";\nimport { buildCollectionIndexSpecs, deriveIndexName, type CollectionIndexSpec } from \"../collection-index\";\nimport { sharedRelationName } from \"../relation-names\";\nimport { SET_UPDATED_AT_FN, setUpdatedAtFunction } from \"./updated-at-trigger\";\nimport type {\n ColumnDefault,\n ColumnPlan,\n EnumPlan,\n ForeignKeyPlan,\n PgType,\n PlanOptions,\n PolicyPlan,\n RelationPlan,\n SchemaPlan,\n TablePlan,\n TriggerPlan\n} from \"./types\";\n\ntype ResolveCollection = (slug: string) => CollectionConfig | undefined;\n\n/**\n * Single-quote escaping for a SQL string literal (PostgreSQL doubles the\n * quote). Enum labels and `defaultValue`s come straight from user-authored\n * collection config, so a value like `it's` closes the literal early and the\n * whole generated file stops parsing at that statement.\n */\nexport const quoteSqlLiteral = (value: string): string => `'${value.replace(/'/g, \"''\")}'`;\n\nconst schemaOf = (collection: CollectionConfig): string =>\n isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\";\n\nconst bareTableName = (name: string): string => (name.includes(\".\") ? name.split(\".\").pop()! : name);\n\nconst describe = (collection: CollectionConfig): string =>\n collection.slug ?? collection.name ?? \"(unnamed)\";\n\n/**\n * The `ON DELETE` a foreign key gets when the author did not say.\n *\n * One rule for every property that emits one — `belongsTo` relations and\n * `reference` properties alike. `reference` kept its own `CASCADE` default for\n * a release after this rule was written, which is exactly the split this\n * function exists to prevent: two spellings of the same link, two\n * data-retention behaviours, and only one of them documented.\n *\n * An optional link is `SET NULL`: the column can hold NULL, so dropping the\n * parent leaves the child row with an empty pointer, which is what \"optional\"\n * already means.\n *\n * A required link is **`RESTRICT`**, not `CASCADE`. `NOT NULL` says the child\n * cannot exist without a parent; it does not say deleting the parent should\n * take the child with it. That second claim is a data-retention decision, and\n * defaulting to it meant `onDelete` — a field nobody has to write — silently\n * turned every `DELETE FROM authors` into a cascade through posts, comments and\n * anything else that hung off them.\n */\nexport const defaultBelongsToOnDelete = (required: boolean | undefined): \"RESTRICT\" | \"SET NULL\" =>\n required ? \"RESTRICT\" : \"SET NULL\";\n\nconst foreignKeyPlan = (\n args: Omit<ForeignKeyPlan, \"constraintName\" | \"sql\">\n): ForeignKeyPlan => {\n const constraintName = toPostgresIdentifier(`${args.table}_${args.column}_fkey`);\n const onUpdate = args.onUpdate ? ` ON UPDATE ${args.onUpdate.toUpperCase()}` : \"\";\n return {\n ...args,\n constraintName,\n sql:\n `ALTER TABLE \"${args.schema}\".\"${args.table}\" ADD CONSTRAINT \"${constraintName}\" ` +\n `FOREIGN KEY (\"${args.column}\") REFERENCES \"${args.targetSchema}\".\"${args.targetTable}\" ` +\n `(\"${args.targetColumn}\") ON DELETE ${args.onDelete.toUpperCase()}${onUpdate}`\n };\n};\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\n/**\n * The type a column pointing at this collection's primary key must have — a\n * junction endpoint, a `belongsTo` foreign key, a `reference`.\n *\n * One function because it was three: the ladder was spelled inline in\n * `generatePostgresDdl`, again in `planJunctionTables` and a third time in the\n * Drizzle generator.\n */\nexport const primaryKeyPgType = (collection: CollectionConfig): PgType => {\n const pk = getPrimaryKeyProp(collection);\n if (pk.type === \"number\") return { kind: \"integer\" };\n return pk.isUuid ? { kind: \"uuid\" } : { kind: \"text\" };\n};\n\n/** The `columnType`s a `number` property may name, and what each one is. */\nconst NUMBER_COLUMN_TYPES: Record<string, PgType> = {\n \"integer\": { kind: \"integer\" },\n // `smallint` and `smallserial` are not in `NumberProperty[\"columnType\"]`'s\n // union, and both have always reached the database: the DDL generator\n // upper-cased whatever it was given. Accepted here for that reason and\n // listed on purpose — the point of this table is that an unrecognised\n // `columnType` is refused with the property named rather than passed\n // through to fail at `CREATE TABLE`.\n \"smallint\": { kind: \"smallint\" },\n \"smallserial\": { kind: \"smallserial\" },\n \"bigint\": { kind: \"bigint\" },\n \"serial\": { kind: \"serial\" },\n \"bigserial\": { kind: \"bigserial\" },\n \"real\": { kind: \"real\" },\n \"double precision\": { kind: \"doublePrecision\" },\n \"numeric\": { kind: \"numeric\" }\n};\n\nconst numberType = (propName: string, prop: NumberProperty, collection: CollectionConfig, isId: boolean): PgType => {\n // An identity column is INTEGER, and `columnType` is not read beside it, on\n // purpose. Every column that points at a numeric primary key is INTEGER\n // (`primaryKeyPgType`), so a BIGINT identity would be referenced by int4\n // foreign keys — the int8/int4 truncation this repo has already been bitten\n // by. The Drizzle generator used to honour `columnType` here while the DDL\n // one ignored it, so the same property was int8 in `schema.generated.ts` and\n // int4 in the database; with `columnType: \"bigserial\"` the emitted\n // `.generatedByDefaultAsIdentity()` is not a method that exists, so the file\n // did not compile at all.\n if (prop.isId === \"increment\") return { kind: \"integer\" };\n if (prop.columnType) {\n const mapped = NUMBER_COLUMN_TYPES[prop.columnType];\n if (!mapped) {\n throw new Error(\n `Property \"${propName}\" of collection \"${describe(collection)}\" declares ` +\n `\\`columnType: \"${prop.columnType}\"\\`, which is not a Postgres numeric type Rebase emits. ` +\n `Use one of: ${Object.keys(NUMBER_COLUMN_TYPES).join(\", \")}.`\n );\n }\n if (mapped.kind === \"numeric\") return numericType(prop);\n return mapped;\n }\n if (prop.validation?.integer || isId) return { kind: \"integer\" };\n return numericType(prop);\n};\n\n/** `NUMERIC`, with the precision and scale the property asks for. */\nconst numericType = (prop: NumberProperty): PgType => {\n const { precision, scale } = prop;\n if (precision === undefined) return { kind: \"numeric\" };\n return scale === undefined\n ? { kind: \"numeric\", precision }\n : { kind: \"numeric\", precision, scale };\n};\n\nconst stringType = (propName: string, prop: StringProperty, collection: CollectionConfig): PgType => {\n if (prop.enum) {\n // Throws on an empty list rather than naming a type nothing creates —\n // the column type and the `CREATE TYPE` come from one reading of `enum`.\n const labels = enumLabelsOf(propName, prop as Property, collection);\n const table = getTableName(collection);\n const column = resolveColumnName(propName, prop as Property);\n return {\n kind: \"enum\",\n schema: schemaOf(collection),\n name: `${table}_${column}`,\n varName: getEnumVarName(table, propName),\n labels\n };\n }\n if (prop.isId === \"uuid\" || prop.columnType === \"uuid\") return { kind: \"uuid\" };\n // The width comes from `validation.max` when the property states one. It\n // used to be a hardcoded 255 in the DDL generator and *absent* on the\n // Drizzle path, so the same property produced a bounded column down one\n // generator and an unbounded one down the other.\n if (prop.columnType === \"char\") return { kind: \"char\", length: resolveStringColumnLength(prop) };\n if (prop.columnType === \"varchar\") return { kind: \"varchar\", length: resolveStringColumnLength(prop) };\n if (prop.columnType !== undefined && prop.columnType !== \"text\") {\n throw new Error(\n `Property \"${propName}\" of collection \"${describe(collection)}\" declares ` +\n `\\`columnType: \"${String(prop.columnType)}\"\\`, which is not a Postgres string type Rebase emits. ` +\n \"Use one of: text, varchar, char, uuid.\"\n );\n }\n // `text` is the default, and the only length-unbounded choice.\n return { kind: \"text\" };\n};\n\nconst arrayElementType = (prop: ArrayProperty): PgType | undefined => {\n let colType = prop.columnType;\n if (!colType && prop.of && !Array.isArray(prop.of)) {\n const of = prop.of as Property;\n if (of.type === \"string\") colType = \"text[]\";\n else if (of.type === \"number\") colType = of.validation?.integer ? \"integer[]\" : \"numeric[]\";\n else if (of.type === \"boolean\") colType = \"boolean[]\";\n }\n switch (colType) {\n case \"text[]\": return { kind: \"text\" };\n case \"integer[]\": return { kind: \"integer\" };\n case \"boolean[]\": return { kind: \"boolean\" };\n case \"numeric[]\": return { kind: \"numeric\" };\n default: return undefined;\n }\n};\n\n/**\n * The Postgres type a property's column has.\n *\n * Every member of `DataType` has an arm. A type this does not know is a\n * generator that has not been taught it yet, and it says so — a silent `TEXT`\n * here is how `geopoint` ended up with a database column, no Drizzle key, and\n * every write to it discarded with a 201.\n */\nexport const columnPgType = (\n propName: string,\n prop: Property,\n collection: CollectionConfig,\n resolveCollection: ResolveCollection\n): PgType => {\n switch (prop.type) {\n case \"string\":\n return stringType(propName, prop as StringProperty, collection);\n case \"number\":\n return numberType(propName, prop as NumberProperty, collection, isIdProperty(propName, prop, collection));\n case \"boolean\":\n return { kind: \"boolean\" };\n case \"date\": {\n const dateProp = prop as DateProperty;\n if (dateProp.columnType === \"date\") return { kind: \"date\" };\n if (dateProp.columnType === \"time\") return { kind: \"time\" };\n return { kind: \"timestamptz\" };\n }\n case \"map\":\n return (prop as MapProperty).columnType === \"json\" ? { kind: \"json\" } : { kind: \"jsonb\" };\n // `{ latitude, longitude }` — a document, like `map`.\n case \"geopoint\":\n return { kind: \"jsonb\" };\n case \"array\": {\n const arrayProp = prop as ArrayProperty;\n if (arrayProp.columnType === \"json\") return { kind: \"json\" };\n const element = arrayElementType(arrayProp);\n return element ? { kind: \"array\", of: element } : { kind: \"jsonb\" };\n }\n case \"vector\":\n return { kind: \"vector\", dimensions: (prop as VectorProperty).dimensions };\n case \"binary\":\n return { kind: \"bytea\" };\n case \"relation\":\n case \"reference\": {\n const target = linkTarget(propName, prop, collection, resolveCollection);\n return target ? primaryKeyPgType(target) : { kind: \"text\" };\n }\n default:\n throw new Error(\n `No Postgres column type for property \"${propName}\" of type ` +\n `\"${(prop as Property).type}\" in collection \"${describe(collection)}\". ` +\n \"Add a case to `columnPgType` in schema/plan/plan-schema.ts.\"\n );\n }\n};\n\n/** The collection a `relation` or `reference` points at, when it resolves. */\nconst linkTarget = (\n propName: string,\n prop: Property,\n collection: CollectionConfig,\n resolveCollection: ResolveCollection\n): CollectionConfig | undefined => {\n if (prop.type === \"reference\") {\n const path = (prop as ReferenceProperty).path;\n return path ? resolveCollection(path) : undefined;\n }\n const relProp = prop as RelationProperty;\n const relation = findRelation(resolveCollectionRelations(collection), relProp.relation?.relationName ?? propName);\n if (relation?.kind !== \"belongsTo\") return undefined;\n try {\n return relation.target();\n } catch {\n return undefined;\n }\n};\n\n// ── Defaults ─────────────────────────────────────────────────────────────────\n\n/**\n * A `defaultValue` as a database DEFAULT.\n *\n * `defaultValue` reached neither the schema nor — until recently — the write\n * path, so a documented, type-checked field did nothing at all: the panel\n * pre-filled it, an insert from anywhere else (the REST API, a seed, psql) did\n * not, and the column came out NULL. A DEFAULT is the one place that binds\n * every writer.\n *\n * Only literals. A `reference`'s default is an `EntityReference` object and a\n * `vector`'s is an embedding — neither is a column literal, and inventing one\n * would be a value the write path and the database disagree about.\n * `undefined` for those, which is what \"no DEFAULT\" means everywhere here.\n */\nconst literalDefault = (prop: Property, type: PgType): ColumnDefault | undefined => {\n const value = (prop as { defaultValue?: unknown }).defaultValue;\n if (value === undefined || value === null) return undefined;\n\n switch (prop.type) {\n case \"string\":\n // An enum default is its id, which is exactly the label the type\n // carries — the same string the write path stores.\n return typeof value === \"string\" ? { kind: \"literal\", value, sql: quoteSqlLiteral(value) } : undefined;\n case \"number\":\n return typeof value === \"number\" && Number.isFinite(value)\n ? { kind: \"literal\", value, sql: String(value) }\n : undefined;\n case \"boolean\":\n return typeof value === \"boolean\"\n ? { kind: \"literal\", value, sql: value ? \"TRUE\" : \"FALSE\" }\n : undefined;\n case \"date\": {\n const iso = value instanceof Date\n ? (Number.isNaN(value.getTime()) ? undefined : value.toISOString())\n : typeof value === \"string\" ? value : undefined;\n return iso === undefined ? undefined : { kind: \"literal\", value: iso, sql: quoteSqlLiteral(iso) };\n }\n case \"map\":\n case \"array\":\n case \"geopoint\": {\n // A document default is a JSON literal cast to the column's own\n // type, so `jsonb` and `json` columns each get a value Postgres\n // accepts without an implicit cast.\n if (type.kind !== \"jsonb\" && type.kind !== \"json\") return undefined;\n let json: string;\n try {\n json = JSON.stringify(value);\n } catch {\n return undefined; // circular, or a BigInt: not a literal.\n }\n if (json === undefined) return undefined;\n return { kind: \"literal\", value, sql: `${quoteSqlLiteral(json)}::${type.kind}` };\n }\n default:\n return undefined;\n }\n};\n\n/** The DEFAULT a column carries: id strategy, then `autoValue`, then `defaultValue`. */\nconst columnDefault = (\n propName: string,\n prop: Property,\n collection: CollectionConfig,\n type: PgType\n): ColumnDefault | undefined => {\n const idDefault = idColumnDefault(propName, prop, collection);\n if (idDefault) {\n if (idDefault.kind === \"uuid\") return { kind: \"sql\", expression: \"gen_random_uuid()\" };\n if (idDefault.kind === \"identity\") return { kind: \"identity\" };\n return { kind: \"sql\", expression: idDefault.expression };\n }\n if (prop.type === \"date\") {\n const autoValue = (prop as DateProperty).autoValue;\n if (autoValue === \"on_create\" || autoValue === \"on_update\") {\n return { kind: \"sql\", expression: \"now()\" };\n }\n }\n return literalDefault(prop, type);\n};\n\n// ── Policies ─────────────────────────────────────────────────────────────────\n\n/**\n * One security rule, compiled to the clauses a policy is made of.\n *\n * The desugaring (`access` / `ownerField` / `roles` / structured condition /\n * raw SQL → `PolicyExpression`) and the SQL compilation are `@rebasepro/common`'s,\n * which is what the client-side evaluator uses too — so the UI, the DDL and the\n * database agree about who can read a row. What lives here is only the shape:\n * which operations a rule expands to, which clauses each operation takes, and\n * the deny-all fallback for a clause that compiled to nothing.\n */\nexport const compileSecurityRule = (\n collection: CollectionConfig,\n rule: SecurityRule,\n resolveCollection: ResolveCollection,\n injected: boolean,\n ruleKey = rule.name ?? \"(unnamed)\"\n): PolicyPlan[] => {\n const tableName = getTableName(collection);\n const ops = rule.operations && rule.operations.length > 0 ? rule.operations : [rule.operation ?? \"all\"];\n const policyNames = getPolicyNamesForRule(rule, tableName);\n const { usingExpr, withCheckExpr } = securityRuleToConditions(rule);\n\n return ops.map((operation, index) => {\n const needsUsing = operation !== \"insert\";\n const needsWithCheck = operation !== \"select\" && operation !== \"delete\";\n let using = needsUsing && usingExpr ? policyToPostgres(usingExpr, collection, { resolveCollection }) : null;\n let withCheck = needsWithCheck && withCheckExpr ? policyToPostgres(withCheckExpr, collection, { resolveCollection }) : null;\n // A clause that compiled to nothing denies rather than opens.\n if (!using && needsUsing) using = \"false\";\n if (!withCheck && needsWithCheck) withCheck = \"false\";\n return {\n name: policyNames[index],\n ruleKey,\n operation,\n mode: rule.mode ?? \"permissive\",\n roles: rule.pgRoles ? [...rule.pgRoles].sort() : [\"public\"],\n using,\n withCheck,\n injected\n };\n });\n};\n\n// ── The planner ──────────────────────────────────────────────────────────────\n\nexport function planSchema(allCollections: CollectionConfig[], options: PlanOptions = {}): SchemaPlan {\n // Before the filter, deliberately: a `search` block on a collection this\n // engine does not store would otherwise be dropped without a word.\n assertSearchIsPostgresOnly(allCollections);\n\n // A Firestore or MongoDB collection has no table here, and generating one\n // is not merely wasted output: `db push` would create it, and the doctor\n // would then report the store the collection actually reads from as drift.\n const collections = relationalCollections(allCollections);\n collections.forEach(assertSinglePrimaryKey);\n\n const resolveCollection: ResolveCollection = (slug) =>\n collections.find(c => c.slug === slug || getTableName(c) === slug);\n\n const declaredSchemas = Array.from(new Set(\n collections.map(c => (isPostgresCollectionConfig(c) ? c.schema : undefined)).filter(Boolean) as string[]\n ));\n const schemas = Array.from(new Set([REBASE_SCHEMA, ...declaredSchemas]));\n\n // ── Enum types ───────────────────────────────────────────────────────────\n // The name is derived from table + column, so two collections mapped onto\n // the same table — or two properties whose `columnName` resolves to the\n // same column — land on the same type. `CREATE TYPE` has no IF NOT EXISTS,\n // so emitting it twice aborts the whole file; deduplicated by name here,\n // once, for every renderer.\n const enums: EnumPlan[] = [];\n const seenEnums = new Set<string>();\n /**\n * Every enum type one table's properties declare, appended once.\n *\n * `declaredSchema` is passed rather than read off the config because a\n * junction's synthetic collection carries `schema: \"public\"` as a resolved\n * fact and not as a declaration — emitting it as one would make the\n * generated file reach for a `pgSchema(\"public\")` variable that exists for\n * nobody.\n */\n const collectEnumTypes = (\n collection: CollectionConfig,\n properties: Properties,\n declaredSchema: string | undefined\n ): void => {\n for (const [propName, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (!(\"enum\" in prop) || !prop.enum) continue;\n if (prop.type !== \"string\" && prop.type !== \"number\") continue;\n // Refuses an empty list whatever the property's type, then declares\n // a type only for the string ones: a `number` enum's column is\n // NUMERIC or INTEGER on every path, so the type used to be created\n // and referenced by nothing.\n const labels = enumLabelsOf(propName, prop, collection);\n if (!declaresEnumType(prop)) continue;\n const schema = schemaOf(collection);\n const name = `${getTableName(collection)}_${resolveColumnName(propName, prop)}`;\n const qualified = `${schema}.${name}`;\n if (seenEnums.has(qualified)) continue;\n seenEnums.add(qualified);\n enums.push({\n schema,\n name,\n qualified,\n declaredSchema,\n varName: getEnumVarName(getTableName(collection), propName),\n labels\n });\n }\n };\n for (const collection of collections) {\n collectEnumTypes(\n collection,\n (collection.properties ?? {}) as Properties,\n isPostgresCollectionConfig(collection) ? collection.schema : undefined\n );\n }\n\n // ── The table set ────────────────────────────────────────────────────────\n // Each collection's table, then any junction its relations imply, in the\n // order the collections arrive. `schema.sql` and `schema.generated.ts` are\n // compared against their committed copies, so this order is the file's.\n const tableEntries = new Map<string, {\n collection: CollectionConfig;\n junction?: { relation: ResolvedRelation; source: CollectionConfig };\n }>();\n for (const collection of collections) {\n const tableName = getTableName(collection);\n if (tableName) tableEntries.set(tableName, { collection });\n for (const relation of Object.values(resolveCollectionRelations(collection))) {\n if (!isManyToMany(relation)) continue;\n const junctionTable = relation.through.table;\n if (tableEntries.has(junctionTable)) continue;\n tableEntries.set(junctionTable, {\n collection: { table: junctionTable, properties: {} } as CollectionConfig,\n junction: { relation, source: collection }\n });\n }\n }\n\n const junctionSpecs = resolveJunctionSpecs(collections);\n\n // A junction's payload may declare an enum too, and the type it names is\n // created by nobody else: no collection owns the table, so the loop above\n // never sees the property. Appended after the collections' — the enum block\n // is compared byte-for-byte against its committed copy, and a junction\n // whose type interleaved with the collections' would reorder that file\n // whenever a `through.properties` block moved.\n for (const spec of junctionSpecs.values()) {\n if (Object.keys(spec.properties).length === 0) continue;\n collectEnumTypes(getJunctionCollectionConfig(spec), spec.properties, undefined);\n }\n\n const triggerFunctionNeeded = { value: false };\n\n const tables: TablePlan[] = [];\n for (const [tableName, entry] of tableEntries) {\n tables.push(entry.junction\n ? planJunctionTable(\n tableName, entry.junction.relation, entry.junction.source,\n junctionSpecs, resolveCollection, triggerFunctionNeeded)\n : planCollectionTable(entry.collection, resolveCollection, triggerFunctionNeeded));\n }\n\n // ── Extensions and functions ─────────────────────────────────────────────\n const extensions: string[] = [];\n const functions: string[] = [];\n const seenStatements = new Set<string>();\n const add = (into: string[], statement: string): void => {\n if (seenStatements.has(statement)) return;\n seenStatements.add(statement);\n into.push(statement);\n };\n for (const table of tables) {\n if (!table.search) continue;\n for (const statement of searchExtensionStatements(table.search)) add(extensions, statement);\n }\n // pgvector is a separate build behind an image, a grant and a provider\n // allow-list, so whether Rebase may install it is not Rebase's to decide.\n // See `DatabaseOptions.extensions`.\n if (vectorExtensionDeclared(options.databaseExtensions)\n && tables.some(t => t.vectorColumns.length > 0)) {\n add(extensions, vectorExtensionStatement());\n }\n for (const table of tables) {\n if (!table.search) continue;\n for (const statement of searchHelperFunctions(table.search)) add(functions, statement);\n }\n if (triggerFunctionNeeded.value) add(functions, setUpdatedAtFunction());\n\n return {\n schemas,\n declaredSchemas,\n enums,\n tables,\n relations: planRelations(collections, tableEntries),\n extensions,\n functions,\n collections,\n options\n };\n}\n\n// ── A collection's table ─────────────────────────────────────────────────────\n\nfunction planCollectionTable(\n collection: CollectionConfig,\n resolveCollection: ResolveCollection,\n triggerFunctionNeeded: { value: boolean }\n): TablePlan {\n const schema = schemaOf(collection);\n const table = bareTableName(getTableName(collection));\n const auth = isAuthCollection(collection);\n const columns: ColumnPlan[] = [];\n const triggers: TriggerPlan[] = [];\n const relations = resolveCollectionRelations(collection);\n\n for (const [propName, rawProp] of Object.entries(collection.properties ?? {})) {\n const prop = rawProp as Property;\n\n if (prop.type === \"relation\") {\n const column = planRelationColumn(propName, prop as RelationProperty, collection, relations, schema, table);\n if (column) columns.push(column);\n continue;\n }\n if (prop.type === \"reference\") {\n columns.push(planReferenceColumn(propName, prop as ReferenceProperty, collection, resolveCollection, schema, table));\n continue;\n }\n\n columns.push(planPropertyColumn(propName, prop, {\n collection,\n resolveCollection,\n schema,\n table,\n auth,\n triggers,\n triggerFunctionNeeded\n }));\n }\n\n // ── Auth columns the collection file never mentions ──────────────────────\n // `db push` is declarative: Atlas diffs the database against exactly what\n // is emitted and drops anything else. The scaffold's users collection\n // describes 12 columns while auth needs 14, so `is_anonymous` and\n // `tokens_valid_after` read as unmanaged drift and a push run after the\n // server had started once planned to DROP them.\n if (auth) {\n const declared = new Set(columns.map(c => c.column));\n for (const spec of AUTH_USERS_COLUMNS) {\n if (declared.has(spec.column)) continue;\n columns.push({\n key: spec.column,\n column: spec.column,\n type: authColumnType(spec.type),\n nullable: spec.notNull !== true,\n primaryKey: false,\n unique: false,\n sqlDefinition: authUsersColumnSql(spec),\n source: { kind: \"auth\", slug: collection.slug }\n });\n }\n }\n\n // ── The opt-in search column ─────────────────────────────────────────────\n // Last, so it reads as what it is: derived from the columns above it.\n // Postgres recomputes it on every write of a source column and rejects any\n // attempt to write it directly, which is what makes it impossible for the\n // index to drift from the row.\n const search = buildSearchColumnSpec(collection);\n if (search) {\n columns.push({\n key: search.column,\n column: search.column,\n type: { kind: \"tsvector\" },\n nullable: true,\n primaryKey: false,\n unique: false,\n generated: { expression: search.expression, stored: true },\n sqlDefinition: searchColumnTypeSql(search.expression, \"tsvector\"),\n source: { kind: \"search\", slug: collection.slug }\n });\n if (search.fuzzy) {\n columns.push({\n key: search.fuzzy.column,\n column: search.fuzzy.column,\n type: { kind: \"text\" },\n nullable: true,\n primaryKey: false,\n unique: false,\n generated: { expression: search.fuzzy.expression, stored: true },\n sqlDefinition: searchColumnTypeSql(search.fuzzy.expression, \"text\"),\n source: { kind: \"search\", slug: collection.slug }\n });\n }\n }\n\n // A collection that declares no primary key gets an implicit `id TEXT\n // PRIMARY KEY`, which is what `derivePrimaryKeys` reads back.\n if (!columns.some(c => c.primaryKey)) {\n columns.unshift({\n key: \"id\",\n column: \"id\",\n type: { kind: \"text\" },\n nullable: false,\n primaryKey: true,\n unique: false,\n source: { kind: \"implicit-id\", slug: collection.slug }\n });\n }\n\n const injected = new Set(getInjectedSecurityRules(collection).map(rule => rule.name));\n const policies = getEffectiveSecurityRules(collection).flatMap((rule, index) =>\n compileSecurityRule(\n collection,\n rule,\n resolveCollection,\n Boolean(rule.name && injected.has(rule.name)),\n rule.name ?? `#${index}`\n ));\n\n // ── Tenancy ──────────────────────────────────────────────────────────────\n // The policy is already above — `getEffectiveSecurityRules` injects it, so\n // it compiles, names and renders like every other rule. What is left is the\n // half a rule cannot express: the column is NOT NULL, and it is indexed.\n const tenantIndexes = applyTenantColumnEffects(collection, columns, schema, table);\n\n return {\n schema,\n table,\n qualified: `${schema}.${table}`,\n declaredSchema: isPostgresCollectionConfig(collection) ? collection.schema : undefined,\n varName: getTableVarName(getTableName(collection)),\n kind: \"collection\",\n slug: collection.slug,\n columns,\n primaryKey: columns.filter(c => c.primaryKey).map(c => c.column),\n indexes: [...buildCollectionIndexSpecs(collection, resolveColumnName), ...tenantIndexes],\n search,\n vector: buildVectorIndexPlan(collection, resolveColumnName),\n vectorColumns: buildVectorColumnSpecs(collection, resolveColumnName),\n policies,\n triggers,\n auth\n };\n}\n\n/** What {@link planPropertyColumn} needs about the table the column lands on. */\ninterface PropertyColumnContext {\n /** The collection, or the synthetic one standing in for a junction. */\n collection: CollectionConfig;\n resolveCollection: ResolveCollection;\n schema: string;\n /** Bare table name — the trigger name is derived from it. */\n table: string;\n /** Whether `auth-users-columns` owns this table's columns. */\n auth: boolean;\n /** Collected `BEFORE UPDATE` triggers, appended to. */\n triggers: TriggerPlan[];\n triggerFunctionNeeded: { value: boolean };\n}\n\n/**\n * One declared, non-linking property, as a column.\n *\n * Extracted from {@link planCollectionTable} the moment a second table needed\n * it: a `manyToMany`'s `through.properties` are columns on the junction, and\n * \"declared exactly like a collection's properties\" has to mean the same code\n * reads them, or it is a promise the next `columnType` or `defaultValue` change\n * quietly breaks. A junction payload column therefore gets the type, the\n * nullability, the default, the UNIQUE and the `on_update` trigger a collection\n * column with the same declaration gets — because it is the same function\n * answering.\n *\n * `relation` and `reference` do not come through here; they own a foreign key\n * and are planned by {@link planRelationColumn} / {@link planReferenceColumn}.\n * A junction payload may not declare either (config validation refuses it), so\n * this is the whole of a payload column.\n */\nfunction planPropertyColumn(\n propName: string,\n prop: Property,\n ctx: PropertyColumnContext\n): ColumnPlan {\n const { collection, resolveCollection, schema, table, auth } = ctx;\n\n const isId = isIdProperty(propName, prop, collection);\n const column = resolveColumnName(propName, prop);\n const type = columnPgType(propName, prop, collection, resolveCollection);\n const required = prop.validation?.required === true;\n const defaultValue = columnDefault(propName, prop, collection, type);\n // On an auth collection, the columns auth itself reads and writes have\n // exactly one definition wherever the table is created from — see\n // `auth-users-columns`. Anything else there is an ordinary field.\n const authDefinition = auth && !isId ? authUsersColumnDefinition(column) : undefined;\n\n const plan: ColumnPlan = {\n key: propName,\n column,\n type,\n // A primary key is NOT NULL whether or not anyone writes it.\n nullable: !(isId || required),\n primaryKey: isId,\n // `validation.unique` holds for every type. The Drizzle generator\n // honoured it on `string` and `number` only, so a unique `date` or\n // `map` was UNIQUE in the database and not in the file drizzle-kit\n // plans from. A primary key gets neither UNIQUE nor NOT NULL —\n // both are implied, and emitting them made drizzle-kit plan an\n // extra constraint against a database `db push` built without one.\n unique: !isId && prop.validation?.unique === true,\n default: defaultValue,\n sqlDefinition: authDefinition,\n source: { kind: \"property\", propName, slug: collection.slug }\n };\n if (prop.type === \"date\" && (prop as DateProperty).autoValue === \"on_update\") {\n plan.touchOnUpdate = true;\n ctx.triggerFunctionNeeded.value = true;\n ctx.triggers.push({\n schema,\n table,\n column,\n name: toPostgresIdentifier(`${table}_${column}_touch`)\n });\n }\n return plan;\n}\n\n/**\n * The two schema effects a `tenant` declaration has that a policy cannot state.\n *\n * The policy itself is injected as an ordinary `SecurityRule`\n * (`buildTenantSecurityRule`) and compiled above with every other rule, so\n * nothing here touches `policies`. What is left is the column:\n *\n * - **`NOT NULL`.** A row whose tenant is NULL belongs to nobody, and the\n * restrictive policy compares the column to the caller's tenant — a\n * comparison against NULL is never true. Such a row is therefore invisible to\n * every caller, forever, including the one who wrote it. The write path\n * refuses to create one (`TENANT_REQUIRED`); this is the same statement made\n * where a raw `INSERT`, a seed and a migration can also hear it.\n * - **An index.** The tenancy predicate is ANDed into *every* statement against\n * the table, so an unindexed tenant column turns every read into a sequential\n * scan filtered by tenant. It is the one index a tenant-scoped table cannot\n * be without, and the one people most reliably forget — which is the whole\n * argument for deriving it rather than documenting it.\n *\n * The index goes through `deriveIndexName` like every other index, so it is a\n * Rebase-managed name (`_ix_<fingerprint>`), it is in the Atlas diff, and the\n * doctor recognises it. Because that name is a fingerprint of the index's\n * semantics, an author who has *already* declared a plain btree index on the\n * tenant column derives byte-for-byte the same name — so the two are the same\n * object and this adds nothing rather than colliding.\n *\n * Throws when the field names no column. `planSchema` throws rather than\n * guessing, and the alternative here is a table with a tenancy policy over a\n * column that does not exist: `CREATE POLICY` fails, RLS stays enabled, and the\n * collection denies every row.\n */\nfunction applyTenantColumnEffects(\n collection: CollectionConfig,\n columns: ColumnPlan[],\n schema: string,\n table: string\n): CollectionIndexSpec[] {\n const tenant = getTenantConfig(collection);\n if (!tenant) return [];\n\n const column = findTenantColumn(collection, columns, tenant.field);\n if (!column) {\n throw new Error(\n `Collection \"${describe(collection)}\" declares \\`tenant: { field: \"${tenant.field}\" }\\`, and ` +\n \"no property of that name reaches a column. `tenant` says what a column means; it does not \" +\n \"create one. Declare the property, or name the one that holds the tenant id.\"\n );\n }\n\n // A primary key is already NOT NULL and already indexed by `<table>_pkey`.\n // Saying either again would make `db push` plan a constraint and an index\n // the database has no reason to hold.\n if (column.primaryKey) return [];\n\n column.nullable = false;\n\n const withoutName = {\n schema,\n table,\n method: \"btree\" as const,\n unique: false,\n keys: [{ column: column.column, direction: \"asc\" as const, nulls: \"last\" as const }],\n include: [],\n predicate: null,\n reason: TENANT_INDEX_REASON\n };\n return [{ ...withoutName, indexName: deriveIndexName(withoutName) }];\n}\n\n/**\n * The planned column a `tenant.field` names.\n *\n * Not simply `columns.find(c => c.key === field)`: a `belongsTo` relation\n * property emits its column under the *foreign key's* field name, so\n * `tenant: { field: \"org\" }` beside `org: { type: \"relation\", … }` has to\n * resolve through the relation to `org_id`. Matching only on `key` found\n * nothing there and the declaration would have been refused as naming no\n * column — for the shape the documentation recommends.\n */\nfunction findTenantColumn(\n collection: CollectionConfig,\n columns: ColumnPlan[],\n field: string\n): ColumnPlan | undefined {\n const direct = columns.find(c => c.key === field);\n if (direct) return direct;\n\n const prop = collection.properties?.[field] as Property | undefined;\n if (prop?.type === \"relation\") {\n const relations = resolveCollectionRelations(collection);\n const relation = findRelation(relations, (prop as RelationProperty).relation?.relationName ?? field);\n if (relation?.kind === \"belongsTo\" && relation.localKey) {\n return columns.find(c => c.column === relation.localKey);\n }\n return undefined;\n }\n if (!prop) return undefined;\n const columnName = resolveColumnName(field, prop);\n return columns.find(c => c.column === columnName);\n}\n\n/**\n * The column a `belongsTo` relation owns, or `undefined` when the relation puts\n * no column on this table.\n *\n * Every other kind is a column on the target, a junction row, or a join chain.\n */\nfunction planRelationColumn(\n propName: string,\n prop: RelationProperty,\n collection: CollectionConfig,\n relations: Record<string, ResolvedRelation>,\n schema: string,\n table: string\n): ColumnPlan | undefined {\n const relation = findRelation(relations, prop.relation?.relationName ?? propName);\n if (relation?.kind !== \"belongsTo\") return undefined;\n\n let target: CollectionConfig;\n try {\n target = relation.target();\n } catch {\n // A relation whose target is not in this bundle yields no column and no\n // constraint. Every emitter returned early here, and they must keep\n // agreeing: a column with no constraint down one path is a schema fork.\n return undefined;\n }\n if (!target) return undefined;\n\n const required = prop.validation?.required === true;\n // The relation and an explicit FK property can both be declared; the\n // explicit one owns the column, and the relation still owns the constraint.\n // Asked of the *field key* rather than the column — `properties[\"post_id\"]`\n // is not where a property declared `postId: { columnName: \"post_id\" }`\n // lives, so this used to emit the column twice and `CREATE TABLE` failed\n // with \"column specified more than once\".\n const fkFieldKey = fieldKeyForColumn(collection, relation.localKey);\n const columnOwnedByProperty = Boolean(collection.properties?.[fkFieldKey]) && propName !== fkFieldKey;\n\n // Only a *derived* column name can be affected by the change to\n // `generateForeignKeyName`; one the author wrote is theirs.\n const relationName = prop.relation?.relationName ?? propName;\n const legacyKey = legacyForeignKeyName(relationName);\n const derived = relation.localKey === generateForeignKeyName(relationName);\n\n return {\n key: fkFieldKey,\n column: relation.localKey,\n type: primaryKeyPgType(target),\n nullable: !required,\n primaryKey: false,\n // `validation.unique` on a link is a one-to-one, and all three emitters\n // dropped it: the property said unique, the column was not, and a\n // second row could point at the same parent.\n unique: prop.validation?.unique === true,\n columnOwnedByProperty: columnOwnedByProperty || undefined,\n legacyColumn: derived && legacyKey !== relation.localKey ? legacyKey : undefined,\n foreignKey: foreignKeyPlan({\n schema,\n table,\n column: relation.localKey,\n targetSchema: schemaOf(target),\n targetTable: bareTableName(getTableName(target)),\n targetColumn: getPrimaryKeyName(target),\n onDelete: relation.onDelete ?? defaultBelongsToOnDelete(required),\n onUpdate: relation.onUpdate\n }),\n source: { kind: \"relation\", propName, slug: collection.slug }\n };\n}\n\n/** The column a `reference` property owns — a foreign key like any other. */\nfunction planReferenceColumn(\n propName: string,\n prop: ReferenceProperty,\n collection: CollectionConfig,\n resolveCollection: ResolveCollection,\n schema: string,\n table: string\n): ColumnPlan {\n // A `reference` with no `path` names no collection, so it keeps its column\n // (the id is still a string) and gets no constraint — the same shape as one\n // whose target is not in this bundle.\n const target = prop.path ? resolveCollection(prop.path) : undefined;\n const column = resolveColumnName(propName, prop as unknown as Property);\n const required = prop.validation?.required === true;\n\n return {\n key: propName,\n column,\n // A `reference` whose target is not in the bundle keeps its column (the\n // id is still a string) and gets no constraint.\n type: target ? primaryKeyPgType(target) : { kind: \"text\" },\n nullable: !required,\n primaryKey: false,\n unique: prop.validation?.unique === true,\n foreignKey: target\n ? foreignKeyPlan({\n schema,\n table,\n column,\n targetSchema: schemaOf(target),\n targetTable: bareTableName(getTableName(target)),\n targetColumn: getPrimaryKeyName(target),\n // The same rule as `belongsTo`, from the same function. A\n // `reference` carries no `onDelete` of its own to override it.\n onDelete: defaultBelongsToOnDelete(required)\n })\n : undefined,\n source: { kind: \"reference\", propName, slug: collection.slug }\n };\n}\n\n/**\n * An auth-owned column's declared SQL type, as a tag.\n *\n * `auth-users-columns` states these as SQL because that is the grammar its three\n * creators write in; the plan needs the tag so nothing downstream has to parse a\n * type back apart. Deliberately exhaustive rather than a fallback: a column auth\n * adds with a type nobody mapped should say so here, not turn into `TEXT` in a\n * table the login flow reads.\n */\nconst AUTH_COLUMN_TYPES: Record<string, PgType> = {\n \"TEXT\": { kind: \"text\" },\n \"TEXT[]\": { kind: \"array\", of: { kind: \"text\" } },\n \"BOOLEAN\": { kind: \"boolean\" },\n \"JSONB\": { kind: \"jsonb\" },\n \"TIMESTAMP WITH TIME ZONE\": { kind: \"timestamptz\" }\n};\n\nconst authColumnType = (sqlType: string): PgType => {\n const mapped = AUTH_COLUMN_TYPES[sqlType];\n if (!mapped) {\n throw new Error(\n `\\`auth-users-columns\\` declares a column of type \"${sqlType}\", which the schema plan ` +\n \"has no tag for. Add it to `AUTH_COLUMN_TYPES` in schema/plan/plan-schema.ts.\"\n );\n }\n return mapped;\n};\n\n// ── A junction table ─────────────────────────────────────────────────────────\n\nfunction planJunctionTable(\n tableName: string,\n relation: ResolvedRelation,\n source: CollectionConfig,\n junctionSpecs: ReturnType<typeof resolveJunctionSpecs>,\n resolveCollection: ResolveCollection,\n triggerFunctionNeeded: { value: boolean }\n): TablePlan {\n if (!isManyToMany(relation)) {\n throw new Error(`Internal: junction table \"${tableName}\" was reached from a ${relation.kind} relation.`);\n }\n const table = bareTableName(tableName);\n // Junctions live in `public`, full stop — `resolveJunctionSpecs` hardcodes\n // that, so it is where the tables are created and where the derived RLS\n // policies are applied. Inheriting the endpoint's schema put the junction\n // of any m2m onto `users` in `rebase` while its policies were still created\n // against `public.<junction>`: RLS enabled on one table, rows in another.\n const schema = \"public\";\n const target = relation.target();\n const { sourceColumn, targetColumn } = relation.through;\n // Every declaring side agrees on the edge's lifetime; the first one wins.\n const onDelete = relation.onDelete ?? \"CASCADE\";\n\n const spec = junctionSpecs.get(table);\n const legacyFor = (collection: CollectionConfig, column: string): string | undefined => {\n // A junction column's default name is derived from the endpoint\n // collection's slug — normally plural, so this is where the\n // singularization change lands: a `categories` endpoint used to give\n // `categorie_id` and now gives `category_id`. Recorded, not used.\n const slug = toSnakeCase(collection.slug ?? collection.name ?? \"\");\n const legacy = legacyForeignKeyName(slug);\n return column === generateForeignKeyName(slug) && legacy !== column ? legacy : undefined;\n };\n\n const endpoint = (collection: CollectionConfig, column: string): ColumnPlan => ({\n key: column,\n column,\n type: primaryKeyPgType(collection),\n nullable: false,\n // Part of the composite key on {@link TablePlan.primaryKey}, not a\n // key of its own — an inline `PRIMARY KEY` here would be a second one.\n primaryKey: false,\n unique: false,\n legacyColumn: legacyFor(collection, column),\n foreignKey: foreignKeyPlan({\n schema,\n table,\n column,\n targetSchema: schemaOf(collection),\n targetTable: bareTableName(getTableName(collection)),\n targetColumn: getPrimaryKeyName(collection),\n onDelete\n }),\n source: { kind: \"junction-key\" }\n });\n\n const columns = [endpoint(source, sourceColumn), endpoint(target, targetColumn)];\n\n // ── The junction's own columns ───────────────────────────────────────────\n // `through.properties`: the `role` on a membership, the `position` on a\n // tag. Planned by the same {@link planPropertyColumn} the collection tables\n // use, against the synthetic collection {@link getJunctionCollectionConfig}\n // builds — so a payload property gets exactly the type, nullability,\n // default, UNIQUE and enum type it would get on a table, and there is no\n // second `switch (prop.type)` anywhere to disagree with the first.\n //\n // Not supported here, and deliberately: `indexes` (declared per collection,\n // and no collection declares a junction), `search`, `vector`, and any\n // linking property — a payload cannot be a `relation`, a `reference` or a\n // `vector`. Config validation refuses those with the relation named; this\n // would otherwise reach `columnPgType` and produce a column no writer knows\n // how to fill.\n const triggers: TriggerPlan[] = [];\n if (spec && Object.keys(spec.properties).length > 0) {\n const junctionCollection = getJunctionCollectionConfig(spec);\n const keyColumns = new Set([sourceColumn, targetColumn]);\n for (const [propName, rawProp] of Object.entries(spec.properties)) {\n const prop = rawProp as Property;\n const plan = planPropertyColumn(propName, prop, {\n collection: junctionCollection,\n resolveCollection,\n schema,\n table,\n auth: false,\n triggers,\n triggerFunctionNeeded\n });\n // A payload column that resolves onto a key column would be a\n // second definition of it — refused by `checkJunctionPayload` at\n // boot, and skipped here so a config that got past it still\n // produces a table rather than a duplicate-column CREATE.\n if (keyColumns.has(plan.column)) continue;\n columns.push(plan);\n }\n }\n\n // Junction tables are generated tables like any other: locked by default,\n // with derived policies — reads follow the endpoints' visibility, writes\n // follow the declaring side's update rules. Without them they were the one\n // kind of generated table with no RLS at all.\n const policies = spec\n ? getJunctionSecurityRules(spec).flatMap((rule, index) =>\n compileSecurityRule(\n getJunctionCollectionConfig(spec), rule, resolveCollection, false, rule.name ?? `#${index}`))\n : [];\n\n return {\n schema,\n table,\n qualified: `${schema}.${table}`,\n varName: getTableVarName(tableName),\n kind: \"junction\",\n declaringSlugs: spec?.declaringSides.map(side => side.collection.slug) ?? [],\n columns,\n primaryKey: [sourceColumn, targetColumn],\n indexes: [],\n vectorColumns: [],\n policies,\n triggers,\n auth: false\n };\n}\n\n// ── Drizzle relations ────────────────────────────────────────────────────────\n\n/**\n * The `relations(...)` entries the generated file carries.\n *\n * Both sides of a link must share a `relationName` or Drizzle cannot pair them;\n * `sharedRelationName` is the rule, and each side derives it independently.\n */\nfunction planRelations(\n collections: CollectionConfig[],\n tableEntries: Map<string, { collection: CollectionConfig; junction?: { relation: ResolvedRelation; source: CollectionConfig } }>\n): RelationPlan[] {\n const plans: RelationPlan[] = [];\n\n for (const [tableName, entry] of tableEntries) {\n const tableVar = getTableVarName(tableName);\n\n if (entry.junction) {\n const { relation, source } = entry.junction;\n if (!isManyToMany(relation)) continue;\n const target = relation.target();\n // The owning relation's name, shared with the source table's\n // `many(junction, { relationName })`.\n const owningRelationName = relation.relationName ?? toSnakeCase(getTableName(target));\n let inverseRelationName: string | undefined;\n try {\n for (const targetRel of Object.values(resolveCollectionRelations(target))) {\n if (targetRel.kind !== \"belongsTo\"\n && targetRel.cardinality === \"many\"\n && targetRel.relationName === owningRelationName) {\n inverseRelationName = targetRel.relationName;\n break;\n }\n }\n } catch {\n // The inverse side may not exist; the synthesized name below is\n // then what keeps the two `one()`s from colliding.\n }\n plans.push({\n tableVar,\n key: relation.through.sourceColumn,\n kind: \"one\",\n targetVar: getTableVarName(getTableName(source)),\n relationName: owningRelationName,\n fields: [relation.through.sourceColumn],\n references: [getPrimaryKeyName(source)]\n });\n plans.push({\n tableVar,\n key: relation.through.targetColumn,\n kind: \"one\",\n targetVar: getTableVarName(getTableName(target)),\n relationName: inverseRelationName ?? `${tableName}_${relation.through.targetColumn}`,\n fields: [relation.through.targetColumn],\n references: [getPrimaryKeyName(target)]\n });\n continue;\n }\n\n const collection = entry.collection;\n // `resolveCollectionRelations` already deduplicates, but an alias entry\n // for the same foreign key would otherwise emit twice.\n const emitted = new Set<string>();\n for (const [key, relation] of Object.entries(resolveCollectionRelations(collection))) {\n let target: CollectionConfig;\n try {\n target = relation.target();\n } catch {\n // A link to a collection outside this bundle is not a relation\n // this file can describe.\n continue;\n }\n const targetVar = getTableVarName(getTableName(target));\n const relationName = sharedRelationName(relation, collection);\n const dedupe = `${relationName}::${relation.kind}`;\n if (emitted.has(dedupe)) continue;\n emitted.add(dedupe);\n\n switch (relation.kind) {\n case \"belongsTo\":\n plans.push({\n tableVar,\n key,\n kind: \"one\",\n targetVar,\n relationName,\n // `localKey` is a COLUMN and the generated object is\n // keyed by PROPERTY: `user_id` is exposed as `userId`,\n // and emitting the column produces a file that does not\n // compile.\n fields: [fieldKeyForColumn(collection, relation.localKey)],\n references: [getPrimaryKeyName(target)]\n });\n break;\n case \"hasOne\":\n // The foreign key lives on the TARGET table, so this side\n // has no `fields`/`references` to give — and Drizzle has no\n // third form. `one(target, { relationName })` is not a\n // `RelationConfig` (TS2345) *and* not something the runtime\n // survives: `createOne` reads `config.fields.reduce(...)`\n // unconditionally. A bare `one(target)` is the documented\n // FK-less form.\n plans.push({ tableVar, key, kind: \"one\", targetVar });\n break;\n case \"hasMany\":\n plans.push({ tableVar, key, kind: \"many\", targetVar, relationName });\n break;\n case \"manyToMany\":\n plans.push({\n tableVar,\n key,\n kind: \"many\",\n targetVar: getTableVarName(relation.through.table),\n relationName\n });\n break;\n case \"via\":\n // A join chain is resolved at query time, not modelled as a\n // Drizzle relation.\n break;\n }\n }\n\n // Reciprocals the far side declares and this one does not. Drizzle\n // needs both halves of a pair to exist for a relational query to work.\n for (const other of collections) {\n if (other.slug === collection.slug) continue;\n for (const otherRel of Object.values(resolveCollectionRelations(other))) {\n if (!hasForeignKeyOnTarget(otherRel)) continue;\n let otherTarget: CollectionConfig;\n try {\n otherTarget = otherRel.target();\n } catch {\n continue;\n }\n if (otherTarget.slug !== collection.slug) continue;\n const relationName = sharedRelationName(otherRel, other);\n const dedupe = `${relationName}::belongsTo`;\n if (emitted.has(dedupe)) continue;\n emitted.add(dedupe);\n const otherVar = getTableVarName(getTableName(other));\n const fieldKey = fieldKeyForColumn(collection, otherRel.foreignKeyOnTarget);\n plans.push({\n tableVar,\n key: `_synth_${otherVar}_${fieldKey}`,\n kind: \"one\",\n targetVar: otherVar,\n relationName,\n fields: [fieldKey],\n // The column the far side points at: its primary key,\n // unless the link names another one with `sourceKey`.\n references: [otherRel.sourceKey\n ? fieldKeyForColumn(other, otherRel.sourceKey)\n : getPrimaryKeyName(other)]\n });\n }\n }\n }\n\n return plans;\n}\n\nexport { SET_UPDATED_AT_FN };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plan-schema-Hgl62S-w.js","names":[],"sources":["../src/schema/search-column.ts","../src/schema/relation-names.ts","../src/schema/plan/updated-at-trigger.ts","../src/schema/plan/plan-schema.ts"],"sourcesContent":["/**\n * The one place a collection's `search` block becomes SQL.\n *\n * Four things describe a Postgres table in this codebase — the DDL generator,\n * the Drizzle schema generator, the runtime table builder for BaaS mode, and\n * the boot-time schema ensure — and each of them has, at some point, described\n * a column differently from the others. The `varchar(255)` note in\n * `generate-postgres-ddl-logic` is one such scar: the same property produced a\n * capped column down one path and an uncapped one down the other, and nothing\n * failed until a user hit the cap.\n *\n * So the search column is not implemented four times. It is computed once,\n * here, and every generator renders the same {@link SearchColumnSpec}. There is\n * a test asserting exactly that (`search-column-contract.test.ts`); the point of\n * this module is that the test has something to assert *about*.\n *\n * ## Why the expressions look the way they do\n *\n * A `GENERATED ALWAYS AS … STORED` expression must be strictly IMMUTABLE, and\n * Postgres is stricter here than intuition. Verified against PostgreSQL 18:\n *\n * | expression | immutable |\n * |-----------------------------------------|-----------|\n * | `to_tsvector('spanish', col)` | yes |\n * | `to_tsvector(col)` (1-arg) | **no** — depends on `default_text_search_config` |\n * | `array_to_string(col, ' ')` | **no** |\n * | `col::text` on `text[]` | **no** |\n * | `to_jsonb(col)` | **no** |\n * | `unaccent(col)` | **no** — dictionary lookup is STABLE |\n * | `jsonb_to_tsvector('spanish', j, '[\"string\"]')` | yes |\n * | `setweight(...) || setweight(...)` | yes |\n *\n * Three of the four things a real search column needs are therefore unavailable\n * directly, which is why {@link searchHelperFunctions} exists: each wraps a\n * stable built-in in an SQL function declared IMMUTABLE. That declaration is a\n * promise, and it is a true one for these three — array joining, JSON string\n * extraction and accent folding are all deterministic for a given input; the\n * built-ins are marked stable only because they must account for element types\n * and dictionaries in general.\n *\n * The alternative was to skip `unaccent` and text arrays entirely. That is not\n * a real option in an accented language: Postgres stems `auditoría` to\n * `auditor` and `auditoria` to `auditori` — *different lexemes* — so a query\n * typed without accents misses every row that carries them.\n */\nimport {\n CollectionConfig,\n Property,\n StringProperty,\n ArrayProperty,\n MapProperty,\n SearchConfig,\n SearchField,\n SearchWeight,\n isPostgresCollectionConfig,\n DEFAULT_SEARCH_COLUMN,\n DEFAULT_SEARCH_LANGUAGE,\n DEFAULT_SEARCH_WEIGHT,\n DEFAULT_FUZZY_THRESHOLD\n} from \"@rebasepro/types\";\nimport { createHash } from \"node:crypto\";\nimport { getTableName } from \"@rebasepro/common\";\nimport { toSnakeCase, toPostgresIdentifier } from \"@rebasepro/utils\";\n\n/** Schema-qualified so a collection outside `public` still resolves them. */\nconst HELPER_SCHEMA = \"public\";\n\n/**\n * Names of the helper functions. Frozen: they are recorded in the stored\n * generation expression of every search column ever created, so renaming one\n * orphans every table that already has a search column.\n */\nexport const SEARCH_TEXT_FN = `${HELPER_SCHEMA}.rebase_search_text`;\nexport const SEARCH_UNACCENT_FN = `${HELPER_SCHEMA}.rebase_search_unaccent`;\n\n/** How a declared path reaches text, which decides the SQL that extracts it. */\ntype FieldKind = \"text\" | \"text_array\" | \"jsonb\";\n\n/** One resolved field: where it lives, how to read it, what it is worth. */\nexport interface ResolvedSearchField {\n /** The path exactly as the author wrote it, for error messages. */\n path: string;\n /** The physical column the path starts at. */\n column: string;\n /** Dotted remainder addressed inside a JSONB column, if any. */\n jsonPath: string[];\n kind: FieldKind;\n weight: SearchWeight;\n /** The `setweight(to_tsvector(…), 'X')` term this field contributes. */\n sql: string;\n /** The plain-text term this field contributes, for the fuzzy column. */\n textSql: string;\n}\n\n/** Everything the generators need to render one collection's search column. */\nexport interface SearchColumnSpec {\n schema: string;\n table: string;\n /** The generated `tsvector` column. */\n column: string;\n language: string;\n unaccent: boolean;\n fields: ResolvedSearchField[];\n /** Body of `GENERATED ALWAYS AS ( … ) STORED` for the tsvector column. */\n expression: string;\n indexName: string;\n /** Extensions that must exist before the column can be created. */\n extensions: string[];\n fuzzy?: {\n column: string;\n expression: string;\n indexName: string;\n threshold: number;\n };\n}\n\n/** Raised when a `search` block names something that cannot be searched. */\nexport class SearchConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SearchConfigError\";\n }\n}\n\n/** The `search` block of a collection, or undefined when it has none. */\nexport const getSearchConfig = (collection: CollectionConfig): SearchConfig | undefined =>\n isPostgresCollectionConfig(collection) ? collection.search : undefined;\n\n/**\n * Refuse a `search` block on a collection this engine does not store.\n *\n * The type only permits one on a `PostgresCollectionConfig`, so TypeScript\n * already stops the ordinary case. This catches the rest — a JS config, a cast,\n * a collection whose `engine` was changed after the block was written — because\n * the alternative is the exact failure the block exists to prevent: a developer\n * who declared what to index, saw no error, and got the substring fallback.\n *\n * Called with *every* collection, before the Postgres ones are filtered out.\n */\nexport const assertSearchIsPostgresOnly = (collections: CollectionConfig[]): void => {\n for (const collection of collections) {\n if (isPostgresCollectionConfig(collection)) continue;\n if (!(collection as { search?: unknown }).search) continue;\n const engine = (collection as { engine?: string }).engine ?? \"non-postgres\";\n throw new SearchConfigError(\n `${collection.slug}.search: full-text search is a Postgres feature, and this collection is served by \\`${engine}\\`. ` +\n \"Remove the block — it would otherwise look configured while `.search()` kept using the default substring match.\"\n );\n }\n};\n\nconst columnNameOf = (propName: string, prop?: Property | null): string =>\n prop && \"columnName\" in prop && typeof prop.columnName === \"string\" ? prop.columnName : toSnakeCase(propName);\n\n/**\n * Classify a property for search purposes.\n *\n * Deliberately narrower than the schema plan's `PgType`: search only cares\n * whether a value reaches text, and the mapping from property to *physical*\n * type is asserted against the plan in the contract test rather than duplicated\n * here.\n *\n * Returns null for anything that is not text-bearing, which the caller turns\n * into a boot error naming the property.\n */\nconst classify = (prop: Property): { kind: FieldKind; reason?: string } | null => {\n switch (prop.type) {\n case \"string\": {\n const sp = prop as StringProperty;\n if (sp.enum) {\n return { kind: \"text\", reason: \"enum\" };\n }\n if (sp.isId === \"uuid\" || sp.columnType === \"uuid\") {\n return { kind: \"text\", reason: \"uuid\" };\n }\n return { kind: \"text\" };\n }\n case \"map\": {\n const mp = prop as MapProperty;\n // A `json` column is not `jsonb`, and the cast between them is not\n // immutable. Declaring `columnType: \"json\"` puts the value out of\n // reach of a generated column.\n if (mp.columnType === \"json\") return { kind: \"jsonb\", reason: \"json\" };\n return { kind: \"jsonb\" };\n }\n case \"array\": {\n const ap = prop as ArrayProperty;\n let colType = ap.columnType;\n if (!colType && ap.of && !Array.isArray(ap.of)) {\n const of = ap.of as Property;\n if (of.type === \"string\") colType = \"text[]\";\n else if (of.type === \"number\") colType = of.validation?.integer ? \"integer[]\" : \"numeric[]\";\n else if (of.type === \"boolean\") colType = \"boolean[]\";\n }\n if (colType === \"text[]\") return { kind: \"text_array\" };\n if (colType === \"json\") return { kind: \"jsonb\", reason: \"json\" };\n if (colType === \"integer[]\" || colType === \"boolean[]\" || colType === \"numeric[]\") {\n return { kind: \"text_array\", reason: \"non_text_array\" };\n }\n // Everything else lands in JSONB, which the JSON extractor handles.\n return { kind: \"jsonb\" };\n }\n default:\n return null;\n }\n};\n\nconst normalize = (inner: string, unaccent: boolean): string =>\n unaccent ? `${SEARCH_UNACCENT_FN}(${inner})` : inner;\n\n/** SQL reading one field as plain text, before normalization. */\nconst rawTextSql = (field: { column: string; jsonPath: string[]; kind: FieldKind }): string => {\n const col = `\"${field.column}\"`;\n if (field.kind === \"text\") return `coalesce(${col}, '')`;\n if (field.kind === \"text_array\") return `${SEARCH_TEXT_FN}(coalesce(${col}, '{}'::text[]))`;\n // JSONB, optionally addressed at a path inside the document.\n const target = field.jsonPath.length === 0\n ? col\n : field.jsonPath.length === 1\n ? `${col} -> ${quote(field.jsonPath[0])}`\n : `${col} #> ${quote(`{${field.jsonPath.join(\",\")}}`)}`;\n return `${SEARCH_TEXT_FN}(coalesce(${target}, '{}'::jsonb))`;\n};\n\nconst quote = (v: string): string => `'${v.replace(/'/g, \"''\")}'`;\n\n/**\n * Resolve and validate one declared field path.\n *\n * A path that does not resolve throws. The whole point of an explicit block is\n * that the author knows what is indexed; a silently dropped field would make it\n * a guess again, and the failure — a search that returns nothing for content\n * that is plainly in the row — is invisible from the outside.\n */\nconst resolveField = (\n entry: string | SearchField,\n collection: CollectionConfig,\n cfg: SearchConfig\n): ResolvedSearchField => {\n const path = typeof entry === \"string\" ? entry : entry.path;\n const weight = (typeof entry === \"string\" ? undefined : entry.weight) ?? DEFAULT_SEARCH_WEIGHT;\n const where = `${collection.slug}.search`;\n\n if (!path || typeof path !== \"string\") {\n throw new SearchConfigError(`${where}: every entry in \\`fields\\` needs a property path.`);\n }\n\n const [head, ...rest] = path.split(\".\");\n const prop = collection.properties?.[head] as Property | undefined;\n if (!prop) {\n const known = Object.keys(collection.properties ?? {}).join(\", \");\n throw new SearchConfigError(\n `${where}: \"${path}\" starts at property \"${head}\", which this collection does not declare. Known properties: ${known}.`\n );\n }\n\n const classified = classify(prop);\n if (!classified) {\n throw new SearchConfigError(\n `${where}: \"${path}\" is a \\`${prop.type}\\` property, which holds no text to search. ` +\n `Searchable kinds are \\`string\\`, \\`string[]\\` and \\`map\\` (or a path inside one).`\n );\n }\n if (classified.reason === \"enum\") {\n throw new SearchConfigError(\n `${where}: \"${path}\" is an enum. Enums are a fixed vocabulary — filter on them with \\`where\\` instead, which is exact and uses an index.`\n );\n }\n if (classified.reason === \"uuid\") {\n throw new SearchConfigError(\n `${where}: \"${path}\" is a UUID column. Look it up by id rather than searching it.`\n );\n }\n if (classified.reason === \"json\") {\n throw new SearchConfigError(\n `${where}: \"${path}\" is a \\`json\\` column, and the cast from \\`json\\` to \\`jsonb\\` is not immutable, so it cannot feed a generated column. Declare the property as \\`jsonb\\` (the default) to search it.`\n );\n }\n if (classified.reason === \"non_text_array\") {\n throw new SearchConfigError(\n `${where}: \"${path}\" is an array of numbers or booleans. Only \\`string[]\\` carries text to search.`\n );\n }\n\n if (rest.length > 0 && classified.kind !== \"jsonb\") {\n throw new SearchConfigError(\n `${where}: \"${path}\" addresses a path inside \"${head}\", but \"${head}\" is a \\`${prop.type}\\` property, not a \\`map\\`. Only map properties have paths inside them.`\n );\n }\n\n const column = columnNameOf(head, prop);\n const field = { column, jsonPath: rest, kind: classified.kind };\n const textSql = normalize(rawTextSql(field), cfg.unaccent === true);\n const language = cfg.language ?? DEFAULT_SEARCH_LANGUAGE;\n\n return {\n path,\n column,\n jsonPath: rest,\n kind: classified.kind,\n weight,\n sql: `setweight(to_tsvector(${quote(language)}, ${textSql}), ${quote(weight)})`,\n textSql\n };\n};\n\n/**\n * Build the full spec for a collection, or undefined when it has not opted in.\n *\n * Throws {@link SearchConfigError} on a config that cannot be honoured. Callers\n * at boot surface that as a startup failure — a search block that half-works is\n * worse than one that refuses.\n */\nexport const buildSearchColumnSpec = (collection: CollectionConfig): SearchColumnSpec | undefined => {\n const cfg = getSearchConfig(collection);\n if (!cfg) return undefined;\n\n if (!Array.isArray(cfg.fields) || cfg.fields.length === 0) {\n throw new SearchConfigError(\n `${collection.slug}.search: \\`fields\\` is empty. Name the properties to index, or remove the \\`search\\` block to keep the default ILIKE behaviour.`\n );\n }\n\n const table = getTableName(collection);\n const schema = isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\";\n const column = cfg.column ?? DEFAULT_SEARCH_COLUMN;\n\n if (collection.properties?.[column]) {\n throw new SearchConfigError(\n `${collection.slug}.search: the generated column \"${column}\" collides with a declared property of the same name. Set \\`search.column\\` to something else.`\n );\n }\n\n const fields = cfg.fields.map(entry => resolveField(entry, collection, cfg));\n\n const seen = new Set<string>();\n for (const f of fields) {\n if (seen.has(f.path)) {\n throw new SearchConfigError(`${collection.slug}.search: \"${f.path}\" is listed twice.`);\n }\n seen.add(f.path);\n }\n\n const extensions: string[] = [];\n if (cfg.unaccent) extensions.push(\"unaccent\");\n if (cfg.fuzzy) extensions.push(\"pg_trgm\");\n\n const spec: SearchColumnSpec = {\n schema,\n table,\n column,\n language: cfg.language ?? DEFAULT_SEARCH_LANGUAGE,\n unaccent: cfg.unaccent === true,\n fields,\n expression: fields.map(f => f.sql).join(\" || \"),\n indexName: toPostgresIdentifier(`${table}_${column}_gin`),\n extensions\n };\n\n if (cfg.fuzzy) {\n const fuzzyColumn = `${column}_text`;\n if (collection.properties?.[fuzzyColumn]) {\n throw new SearchConfigError(\n `${collection.slug}.search: \\`fuzzy\\` needs the column \"${fuzzyColumn}\", which collides with a declared property. Set \\`search.column\\` to something else.`\n );\n }\n spec.fuzzy = {\n column: fuzzyColumn,\n // Concatenated with spaces so a trigram never spans two fields.\n expression: fields.map(f => f.textSql).join(\" || ' ' || \"),\n indexName: toPostgresIdentifier(`${table}_${fuzzyColumn}_trgm`),\n threshold: cfg.fuzzyThreshold ?? DEFAULT_FUZZY_THRESHOLD\n };\n }\n\n return spec;\n};\n\n/**\n * The IMMUTABLE wrappers the generated expressions call.\n *\n * `CREATE OR REPLACE` so a boot against an existing database is a no-op rather\n * than an error, and idempotent for the same reason every other boot-time DDL\n * statement here is.\n *\n * The bodies are stable built-ins wrapped in an immutable promise — see the\n * module comment for why that promise is sound. `STRICT` matters: it makes NULL\n * in mean NULL out without executing the body, which is what the `coalesce` at\n * each call site then absorbs.\n */\nexport const searchHelperFunctions = (spec: SearchColumnSpec): string[] => {\n const statements: string[] = [\n // text[] → \" \"-joined text.\n `CREATE OR REPLACE FUNCTION ${SEARCH_TEXT_FN}(text[]) RETURNS text\\n` +\n ` LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS\\n` +\n ` $$ SELECT array_to_string($1, ' ') $$;`,\n // jsonb → every string value at or below the node, space-joined. Keys\n // are not values: indexing them would make `certifications` itself a\n // search term on every row that has the field at all.\n `CREATE OR REPLACE FUNCTION ${SEARCH_TEXT_FN}(jsonb) RETURNS text\\n` +\n ` LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS\\n` +\n ` $$ SELECT coalesce(string_agg(v, ' '), '')\\n` +\n ` FROM jsonb_array_elements_text(jsonb_path_query_array($1, 'strict $.**?(@.type() == \"string\")')) AS v $$;`\n ];\n if (spec.unaccent) {\n // The two-argument form with an explicit dictionary is the one that can\n // honestly be called immutable: the single-argument form resolves the\n // dictionary through the current search_path at call time.\n statements.push(\n `CREATE OR REPLACE FUNCTION ${SEARCH_UNACCENT_FN}(text) RETURNS text\\n` +\n ` LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS\\n` +\n ` $$ SELECT ${HELPER_SCHEMA}.unaccent('${HELPER_SCHEMA}.unaccent'::regdictionary, $1) $$;`\n );\n }\n return statements;\n};\n\n/**\n * `CREATE EXTENSION` statements the spec's expressions depend on.\n *\n * `WITH SCHEMA public` is load-bearing, not tidiness. An unqualified\n * `CREATE EXTENSION` installs into the first schema on `search_path`, which\n * defaults to `\"$user\", public` — and the scaffold's database role is named\n * `rebase`, the same as the schema the generator creates one statement earlier.\n * So the moment that schema exists, `CREATE EXTENSION unaccent` puts the\n * dictionary in `rebase`, and every reference to `public.unaccent` below fails\n * with \"text search dictionary does not exist\". Observed, not theorised.\n */\nexport const searchExtensionStatements = (spec: SearchColumnSpec): string[] =>\n spec.extensions.map(e => `CREATE EXTENSION IF NOT EXISTS ${e} WITH SCHEMA ${HELPER_SCHEMA};`);\n\n/**\n * Everything after the column name — the type and the generation expression.\n *\n * Split out because four emitters need it and only two of them have a place to\n * put the name: `CREATE TABLE` and `ADD COLUMN` write `\"col\" <this>`, while the\n * schema plan carries it as the column's SQL definition and the boot-time\n * rebuild statement interpolates it on its own.\n */\nexport const searchColumnTypeSql = (expression: string, kind: \"tsvector\" | \"text\"): string =>\n `${kind} GENERATED ALWAYS AS (${expression}) STORED`;\n\n/** The column definition as it appears inside `CREATE TABLE`. */\nexport const searchColumnDefinition = (spec: SearchColumnSpec): string =>\n `\"${spec.column}\" ${searchColumnTypeSql(spec.expression, \"tsvector\")}`;\n\n/** The fuzzy column definition, when the spec asks for one. */\nexport const fuzzyColumnDefinition = (spec: SearchColumnSpec): string | undefined =>\n spec.fuzzy ? `\"${spec.fuzzy.column}\" ${searchColumnTypeSql(spec.fuzzy.expression, \"text\")}` : undefined;\n\n/**\n * Index statements for the spec.\n *\n * `CONCURRENTLY` is deliberately *not* used here. This form is emitted into a\n * SQL file replayed as one unit — a migration, or `search.sql` — where a\n * concurrent build is not allowed. The boot-time ensure path runs statement by\n * statement against tables that are live and populated, and uses the\n * concurrent form instead; see `ensureSearchColumns`.\n */\nexport const searchIndexStatements = (spec: SearchColumnSpec): string[] => {\n const statements = [\n `CREATE INDEX IF NOT EXISTS \"${spec.indexName}\" ON \"${spec.schema}\".\"${spec.table}\" USING GIN (\"${spec.column}\");`\n ];\n if (spec.fuzzy) {\n statements.push(\n // The operator class is resolved through `search_path` like any\n // other object, so it is qualified for the same reason the\n // extension is installed explicitly.\n `CREATE INDEX IF NOT EXISTS \"${spec.fuzzy.indexName}\" ON \"${spec.schema}\".\"${spec.table}\" USING GIN (\"${spec.fuzzy.column}\" ${HELPER_SCHEMA}.gin_trgm_ops);`\n );\n }\n return statements;\n};\n\n// ── Telling a changed `search` block from an unchanged one ──────────────────\n\n/**\n * Marker on the comment of every generated search column this module creates.\n *\n * Versioned because the fingerprint below is only comparable against itself: a\n * future change to how it is computed has to read as \"not stamped by this\n * version\" rather than as drift on every existing column.\n */\nexport const SEARCH_STAMP_PREFIX = \"rebase:search:v1:\";\n\n/**\n * A stable fingerprint of one generated column's expression.\n *\n * Why a stamp rather than reading the expression back: Postgres stores a\n * generated column's expression *parsed*, and hands it back deparsed — casts\n * made explicit, identifiers requoted, schema qualifications added or dropped\n * according to `search_path`. Comparing that text to the text we generated\n * would report drift on wording, and this comparison decides whether a boot\n * refuses, so a false positive is an outage. The stamp is written by the same\n * code that writes the column, so equality means what it says.\n */\nexport const searchExpressionFingerprint = (expression: string): string =>\n `${SEARCH_STAMP_PREFIX}${createHash(\"sha256\").update(expression).digest(\"hex\").slice(0, 16)}`;\n\n/** One generated column, with the fingerprint that identifies its expression. */\nexport interface SearchColumnStamp {\n column: string;\n /** The expression the column is generated from. */\n expression: string;\n fingerprint: string;\n /** `COMMENT ON COLUMN …`, which is where the fingerprint is recorded. */\n sql: string;\n}\n\n/**\n * The stamps for a spec's generated columns — one per column, never shared.\n *\n * Per column on purpose: turning `fuzzy` on adds a second column and changes\n * nothing about the first, and a spec-wide fingerprint would report the\n * untouched `tsvector` column as drifted and refuse a boot over a change that\n * is purely additive.\n */\nexport const searchColumnStamps = (spec: SearchColumnSpec): SearchColumnStamp[] => {\n const stamp = (column: string, expression: string): SearchColumnStamp => {\n const fingerprint = searchExpressionFingerprint(expression);\n return {\n column,\n expression,\n fingerprint,\n sql: `COMMENT ON COLUMN \"${spec.schema}\".\"${spec.table}\".\"${column}\" IS ${quote(fingerprint)};`\n };\n };\n const stamps = [stamp(spec.column, spec.expression)];\n if (spec.fuzzy) stamps.push(stamp(spec.fuzzy.column, spec.fuzzy.expression));\n return stamps;\n};\n\n/**\n * The same drift check as the boot ensure, for the SQL file.\n *\n * Needed because {@link searchColumnStamps} would otherwise *launder* drift on\n * the migration path: `ADD COLUMN IF NOT EXISTS` does nothing to a column that\n * exists, so a re-generated `search.sql` would stamp a stale column with the\n * new block's fingerprint and the next boot would find them in agreement.\n * Guarding first means the file refuses instead — `rebase db push` is attended,\n * and the operator reading the failure is the person who changed the block.\n */\nexport const searchStampGuards = (spec: SearchColumnSpec): string[] =>\n searchColumnStamps(spec).map(stamp => {\n const relation = quote(`\"${spec.schema}\".\"${spec.table}\"`);\n return `DO $rebase_search$\nDECLARE recorded text;\nBEGIN\n SELECT col_description(a.attrelid, a.attnum) INTO recorded\n FROM pg_attribute a\n WHERE a.attrelid = ${relation}::regclass AND a.attname = ${quote(stamp.column)} AND NOT a.attisdropped;\n IF recorded LIKE ${quote(`${SEARCH_STAMP_PREFIX}%`)} AND recorded <> ${quote(stamp.fingerprint)} THEN\n RAISE EXCEPTION 'Rebase: the search block for ${spec.schema}.${spec.table} changed after the generated column \"${stamp.column}\" was built (recorded %, expected ${stamp.fingerprint}). Postgres cannot alter a generated expression in place. Drop the column and re-apply this file — it rewrites the table and rebuilds the index: ALTER TABLE ${relation.slice(1, -1)} DROP COLUMN \"${stamp.column}\";', recorded;\n END IF;\nEND\n$rebase_search$;`;\n });\n\n/**\n * The index names the spec creates.\n *\n * Needed by name, not just by statement, so Atlas can be told to exclude them\n * from its diff — see `searchExcludePatterns`.\n */\nexport const searchIndexNames = (spec: SearchColumnSpec): string[] =>\n spec.fuzzy ? [spec.indexName, spec.fuzzy.indexName] : [spec.indexName];\n\n// ── Keeping the generated columns out of responses ──────────────────────────\n\n/**\n * The generated column names a collection's search block adds, if any.\n *\n * These are physical columns on the table, so `SELECT *` returns them. They are\n * an index in column form — a list of lexeme positions, or a concatenation of\n * every searchable field on the row — and nothing outside the query planner has\n * any use for them. Left in, every list response carries a second, larger copy\n * of the row's text.\n */\nexport const searchColumnNames = (collection: CollectionConfig): string[] => {\n let spec: SearchColumnSpec | undefined;\n try {\n spec = buildSearchColumnSpec(collection);\n } catch {\n // A malformed block is reported at boot, loudly. A read is the wrong\n // place to raise it a second time, and returning the row without the\n // exclusion would be worse than returning it with.\n return [];\n }\n if (!spec) return [];\n return spec.fuzzy ? [spec.column, spec.fuzzy.column] : [spec.column];\n};\n\n/**\n * True for a column whose type only ever holds a search index.\n *\n * Independent of any collection config on purpose: an introspected database\n * (BaaS mode) can carry a `tsvector` column this framework never created —\n * Pagila's `film.fulltext` is the canonical one — and it should not be returned\n * to callers either. `isDerivedIndexColumn` already keeps such a column out of\n * the *properties*; this keeps it out of the *rows*.\n */\nexport const isSearchIndexColumn = (column: { getSQLType?: () => string }): boolean => {\n const sqlType = typeof column?.getSQLType === \"function\" ? column.getSQLType().toLowerCase() : \"\";\n return sqlType === \"tsvector\" || sqlType === \"tsquery\";\n};\n\n/**\n * A drizzle select projection over `table` with the search columns dropped.\n *\n * Returns undefined when nothing needs dropping, so the common case keeps using\n * a plain `select()` and this stays invisible in the generated SQL.\n */\nexport const visibleColumnProjection = (\n tableColumns: Record<string, { getSQLType?: () => string }> | undefined,\n collection?: CollectionConfig\n): Record<string, unknown> | undefined => {\n const excluded = excludedColumnNames(tableColumns, collection);\n if (!tableColumns || excluded.length === 0) return undefined;\n const projection: Record<string, unknown> = {};\n for (const [name, column] of Object.entries(tableColumns)) {\n if (!excluded.includes(name)) projection[name] = column;\n }\n return projection;\n};\n\n/** The same exclusion as a drizzle `db.query` `columns` denylist. */\nexport const hiddenColumnsOption = (\n tableColumns: Record<string, { getSQLType?: () => string }> | undefined,\n collection?: CollectionConfig\n): Record<string, false> | undefined => {\n const excluded = excludedColumnNames(tableColumns, collection);\n if (excluded.length === 0) return undefined;\n return Object.fromEntries(excluded.map(name => [name, false as const]));\n};\n\n/**\n * The columns to keep out of a response, by name.\n *\n * `tableColumns` is whatever `getTableColumns` returned, which is `undefined`\n * for anything that is not a real drizzle table — a stub in a test, a derived\n * or nested path with no table behind it. Nothing to exclude is the right\n * answer there, and it has to be an answer rather than a throw: this runs on\n * the read path of every collection, opted in or not.\n */\nconst excludedColumnNames = (\n tableColumns: Record<string, { getSQLType?: () => string }> | undefined,\n collection?: CollectionConfig\n): string[] => {\n if (!tableColumns || typeof tableColumns !== \"object\") return [];\n const byName = new Set(collection ? searchColumnNames(collection) : []);\n return Object.keys(tableColumns).filter(\n name => byName.has(name) || isSearchIndexColumn(tableColumns[name])\n );\n};\n","import { CollectionConfig, ResolvedRelation } from \"@rebasepro/types\";\nimport { fieldKeyForColumn, getTableName } from \"@rebasepro/common\";\nimport { toSnakeCase } from \"@rebasepro/utils\";\n\n/**\n * The `relationName` both sides of a link must agree on.\n *\n * Drizzle pairs an owning `one()` with its inverse `many()` by this string and\n * by nothing else, and the two sides are computed by different callers holding\n * different collections — so the rule has to be derivable from either end. It\n * is: name the link after the table that carries the foreign key and the field\n * key that column is served under.\n *\n * owning (belongsTo) → `{thisTable}_{fieldKey(localKey)}`\n * inverse (hasMany/hasOne) → `{targetTable}_{fieldKey(foreignKeyOnTarget)}`\n *\n * Both spellings of `jobs.company` produce `jobs_companyId`. A many-to-many is\n * named through its junction wiring and a `via` chain is not a Drizzle relation\n * at all, so both keep the local name.\n *\n * The field key rather than the column: the Drizzle object is keyed by the wire\n * name (`fieldKeyForColumn` is the one definition of it), and a name built from\n * the column would differ between a collection that declares `columnName` and\n * one that does not, for the same link.\n *\n * **This is the one definition.** `generate-drizzle-schema-logic.ts` holds a\n * private copy (`computeSharedRelationName`) that this was lifted from verbatim;\n * the generator should import this instead, so the file it writes and the\n * relations the runtime builds from the live catalogue cannot drift apart.\n */\nexport function sharedRelationName(\n relation: ResolvedRelation,\n sourceCollection: CollectionConfig\n): string {\n const fallback = (): string => {\n if (relation.relationName) return relation.relationName;\n try {\n return toSnakeCase(relation.target().slug);\n } catch {\n return relation.relationName;\n }\n };\n\n if (relation.kind === \"belongsTo\") {\n return `${getTableName(sourceCollection)}_${fieldKeyForColumn(sourceCollection, relation.localKey)}`;\n }\n\n if (relation.kind === \"hasMany\" || relation.kind === \"hasOne\") {\n try {\n const target = relation.target();\n return `${getTableName(target)}_${fieldKeyForColumn(target, relation.foreignKeyOnTarget)}`;\n } catch {\n return fallback();\n }\n }\n\n return fallback();\n}\n","/**\n * `autoValue: \"on_update\"`, as a database trigger.\n *\n * ## Why a trigger and not only the driver\n *\n * The driver stamps `updated_at` on every write it makes, and it has to keep\n * doing so: a `beforeSave` hook reads the row it is about to persist, and a\n * value that only appears once Postgres has written it is a value the hook\n * cannot see.\n *\n * But the driver is not the only writer. A seed script, a migration backfill, a\n * `psql` session, an admin fixing one row by hand — every one of those leaves\n * `updated_at` at whatever it was, and the column then says the row has not\n * changed since a date that is simply wrong. Anything reading it to decide what\n * to re-index, re-sync or re-send skips the row. The declaration says \"this\n * column tracks the last write\"; only the database can make that true.\n *\n * So both: the app stamps so hooks see the value, and a `BEFORE UPDATE` trigger\n * stamps so every other writer does too. They agree because they both mean\n * \"now\" — the trigger's `now()` is the transaction's start time, which is the\n * same instant the driver's own timestamp is written under.\n *\n * ## Why it is carved out of Atlas\n *\n * Atlas's free tier refuses to *parse* a desired-state file containing a\n * function (\"functions and procedures are available to logged-in users only\"),\n * and a trigger is a function plus a binding. This is the same arrangement the\n * search helpers already have: excluded from Atlas's view by pattern, written\n * to a file Rebase applies itself, applied at boot by the schema ensure, and\n * appended to migrations by the CLI so a replay is complete.\n *\n * ## Why one function for every column\n *\n * The column name arrives as a trigger argument (`TG_ARGV[0]`), so a project\n * with forty `updated_at` columns installs one function and forty bindings\n * rather than forty near-identical functions. `jsonb_populate_record(NEW, …)`\n * is the documented way to set a field of a row whose type is not known until\n * run time; it takes `NEW` as the row template, so the value is cast back to\n * whatever the column actually is — a `timestamptz`, or a `date` if that is\n * what the property declared.\n */\nimport { REBASE_SCHEMA } from \"@rebasepro/types\";\nimport type { TriggerPlan } from \"./types\";\n\n/** The one trigger function, qualified. A frozen derived name. */\nexport const SET_UPDATED_AT_FN = `${REBASE_SCHEMA}.set_updated_at`;\n\n/**\n * `CREATE OR REPLACE FUNCTION`, so replaying it against a database that already\n * has it is a no-op — this is emitted into a file that runs on every push and\n * is appended to migrations that run against databases at any stage of life.\n */\nexport const setUpdatedAtFunction = (): string =>\n `CREATE OR REPLACE FUNCTION ${SET_UPDATED_AT_FN}() RETURNS trigger\\n` +\n \"LANGUAGE plpgsql AS $$\\n\" +\n \"BEGIN\\n\" +\n \" NEW := jsonb_populate_record(NEW, jsonb_build_object(TG_ARGV[0], now()));\\n\" +\n \" RETURN NEW;\\n\" +\n \"END;\\n\" +\n \"$$;\";\n\n/**\n * `DROP TRIGGER IF EXISTS` before the `CREATE`.\n *\n * `CREATE OR REPLACE TRIGGER` exists only on Postgres 14+, and this runs\n * against whatever a self-hosted project points at. Two statements rather than\n * one is also what the boot-time applier needs: it issues DDL one statement at\n * a time over the extended query protocol, which forbids multiple commands in\n * one execute.\n */\nexport const dropTriggerStatement = (plan: TriggerPlan): string =>\n `DROP TRIGGER IF EXISTS \"${plan.name}\" ON \"${plan.schema}\".\"${plan.table}\";`;\n\nexport const createTriggerStatement = (plan: TriggerPlan): string =>\n `CREATE TRIGGER \"${plan.name}\" BEFORE UPDATE ON \"${plan.schema}\".\"${plan.table}\" ` +\n `FOR EACH ROW EXECUTE FUNCTION ${SET_UPDATED_AT_FN}('${plan.column.replace(/'/g, \"''\")}');`;\n\n/** Both statements, in the order they must run. */\nexport const triggerStatements = (plan: TriggerPlan): string[] =>\n [dropTriggerStatement(plan), createTriggerStatement(plan)];\n","/**\n * The only place a `Property` is read for schema purposes.\n *\n * Everything that used to `switch (prop.type)` — the Drizzle generator, the DDL\n * generator, the boot-time ensure — now renders the {@link SchemaPlan} this\n * produces. See `./types.ts` for why.\n *\n * Pure and synchronous. It reads collections and nothing else: no database, no\n * filesystem, no process-wide registry. The one fact about the world it needs\n * (which extensions a project's databases gave Rebase leave to install) is\n * passed in, because a plan that depended on whatever module happened to be\n * imported would not be comparable with itself.\n *\n * It **throws** rather than guessing. A configuration no renderer can honour —\n * an empty enum, two `isId` properties, `isId: \"cuid\"`, an unknown\n * `columnType`, a relation to a collection that is not in the bundle, a\n * `search` block on a collection Postgres does not store — used to fail\n * differently in each of the three emitters, or not at all, and the failure\n * landed at `CREATE TABLE` time on a managed tenant with nobody in the loop.\n * Here it lands once, with the collection and the property named.\n */\nimport {\n isPostgresCollectionConfig,\n isManyToMany,\n REBASE_SCHEMA,\n type ArrayProperty,\n type CollectionConfig,\n type DateProperty,\n type MapProperty,\n type NumberProperty,\n type Properties,\n type Property,\n type ReferenceProperty,\n type RelationProperty,\n type ResolvedRelation,\n type SecurityRule,\n type StringProperty,\n type VectorProperty,\n hasForeignKeyOnTarget\n} from \"@rebasepro/types\";\nimport {\n fieldKeyForColumn,\n findRelation,\n getEffectiveSecurityRules,\n getEnumVarName,\n getInjectedSecurityRules,\n getTenantConfig,\n TENANT_INDEX_REASON,\n getJunctionCollectionConfig,\n getJunctionSecurityRules,\n getTableName,\n getTableVarName,\n policyToPostgres,\n relationalCollections,\n resolveCollectionRelations,\n resolveJunctionSpecs,\n resolveStringColumnLength,\n securityRuleToConditions\n} from \"@rebasepro/common\";\nimport {\n generateForeignKeyName,\n getPolicyNamesForRule,\n legacyForeignKeyName,\n toPostgresIdentifier,\n toSnakeCase\n} from \"@rebasepro/utils\";\nimport {\n assertSinglePrimaryKey,\n declaresEnumType,\n enumLabelsOf,\n getPrimaryKeyName,\n getPrimaryKeyProp,\n idColumnDefault,\n isIdProperty,\n resolveColumnName\n} from \"../column-plan-helpers\";\nimport {\n AUTH_USERS_COLUMNS,\n authUsersColumnDefinition,\n authUsersColumnSql,\n isAuthCollection\n} from \"../auth-users-columns\";\nimport {\n assertSearchIsPostgresOnly,\n buildSearchColumnSpec,\n searchColumnTypeSql,\n searchExtensionStatements,\n searchHelperFunctions\n} from \"../search-column\";\nimport {\n buildVectorColumnSpecs,\n buildVectorIndexPlan,\n vectorExtensionDeclared,\n vectorExtensionStatement\n} from \"../vector-index\";\nimport { buildCollectionIndexSpecs, deriveIndexName, type CollectionIndexSpec } from \"../collection-index\";\nimport { sharedRelationName } from \"../relation-names\";\nimport { SET_UPDATED_AT_FN, setUpdatedAtFunction } from \"./updated-at-trigger\";\nimport type {\n ColumnDefault,\n ColumnPlan,\n EnumPlan,\n ForeignKeyPlan,\n PgType,\n PlanOptions,\n PolicyPlan,\n RelationPlan,\n SchemaPlan,\n TablePlan,\n TriggerPlan\n} from \"./types\";\n\ntype ResolveCollection = (slug: string) => CollectionConfig | undefined;\n\n/**\n * Single-quote escaping for a SQL string literal (PostgreSQL doubles the\n * quote). Enum labels and `defaultValue`s come straight from user-authored\n * collection config, so a value like `it's` closes the literal early and the\n * whole generated file stops parsing at that statement.\n */\nexport const quoteSqlLiteral = (value: string): string => `'${value.replace(/'/g, \"''\")}'`;\n\nconst schemaOf = (collection: CollectionConfig): string =>\n isPostgresCollectionConfig(collection) && collection.schema ? collection.schema : \"public\";\n\nconst bareTableName = (name: string): string => (name.includes(\".\") ? name.split(\".\").pop()! : name);\n\nconst describe = (collection: CollectionConfig): string =>\n collection.slug ?? collection.name ?? \"(unnamed)\";\n\n/**\n * The `ON DELETE` a foreign key gets when the author did not say.\n *\n * One rule for every property that emits one — `belongsTo` relations and\n * `reference` properties alike. `reference` kept its own `CASCADE` default for\n * a release after this rule was written, which is exactly the split this\n * function exists to prevent: two spellings of the same link, two\n * data-retention behaviours, and only one of them documented.\n *\n * An optional link is `SET NULL`: the column can hold NULL, so dropping the\n * parent leaves the child row with an empty pointer, which is what \"optional\"\n * already means.\n *\n * A required link is **`RESTRICT`**, not `CASCADE`. `NOT NULL` says the child\n * cannot exist without a parent; it does not say deleting the parent should\n * take the child with it. That second claim is a data-retention decision, and\n * defaulting to it meant `onDelete` — a field nobody has to write — silently\n * turned every `DELETE FROM authors` into a cascade through posts, comments and\n * anything else that hung off them.\n */\nexport const defaultBelongsToOnDelete = (required: boolean | undefined): \"RESTRICT\" | \"SET NULL\" =>\n required ? \"RESTRICT\" : \"SET NULL\";\n\nconst foreignKeyPlan = (\n args: Omit<ForeignKeyPlan, \"constraintName\" | \"sql\">\n): ForeignKeyPlan => {\n const constraintName = toPostgresIdentifier(`${args.table}_${args.column}_fkey`);\n const onUpdate = args.onUpdate ? ` ON UPDATE ${args.onUpdate.toUpperCase()}` : \"\";\n return {\n ...args,\n constraintName,\n sql:\n `ALTER TABLE \"${args.schema}\".\"${args.table}\" ADD CONSTRAINT \"${constraintName}\" ` +\n `FOREIGN KEY (\"${args.column}\") REFERENCES \"${args.targetSchema}\".\"${args.targetTable}\" ` +\n `(\"${args.targetColumn}\") ON DELETE ${args.onDelete.toUpperCase()}${onUpdate}`\n };\n};\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\n/**\n * The type a column pointing at this collection's primary key must have — a\n * junction endpoint, a `belongsTo` foreign key, a `reference`.\n *\n * One function because it was three: the ladder was spelled inline in\n * `generatePostgresDdl`, again in `planJunctionTables` and a third time in the\n * Drizzle generator.\n */\nexport const primaryKeyPgType = (collection: CollectionConfig): PgType => {\n const pk = getPrimaryKeyProp(collection);\n if (pk.type === \"number\") return { kind: \"integer\" };\n return pk.isUuid ? { kind: \"uuid\" } : { kind: \"text\" };\n};\n\n/** The `columnType`s a `number` property may name, and what each one is. */\nconst NUMBER_COLUMN_TYPES: Record<string, PgType> = {\n \"integer\": { kind: \"integer\" },\n // `smallint` and `smallserial` are not in `NumberProperty[\"columnType\"]`'s\n // union, and both have always reached the database: the DDL generator\n // upper-cased whatever it was given. Accepted here for that reason and\n // listed on purpose — the point of this table is that an unrecognised\n // `columnType` is refused with the property named rather than passed\n // through to fail at `CREATE TABLE`.\n \"smallint\": { kind: \"smallint\" },\n \"smallserial\": { kind: \"smallserial\" },\n \"bigint\": { kind: \"bigint\" },\n \"serial\": { kind: \"serial\" },\n \"bigserial\": { kind: \"bigserial\" },\n \"real\": { kind: \"real\" },\n \"double precision\": { kind: \"doublePrecision\" },\n \"numeric\": { kind: \"numeric\" }\n};\n\nconst numberType = (propName: string, prop: NumberProperty, collection: CollectionConfig, isId: boolean): PgType => {\n // An identity column is INTEGER, and `columnType` is not read beside it, on\n // purpose. Every column that points at a numeric primary key is INTEGER\n // (`primaryKeyPgType`), so a BIGINT identity would be referenced by int4\n // foreign keys — the int8/int4 truncation this repo has already been bitten\n // by. The Drizzle generator used to honour `columnType` here while the DDL\n // one ignored it, so the same property was int8 in `schema.generated.ts` and\n // int4 in the database; with `columnType: \"bigserial\"` the emitted\n // `.generatedByDefaultAsIdentity()` is not a method that exists, so the file\n // did not compile at all.\n if (prop.isId === \"increment\") return { kind: \"integer\" };\n if (prop.columnType) {\n const mapped = NUMBER_COLUMN_TYPES[prop.columnType];\n if (!mapped) {\n throw new Error(\n `Property \"${propName}\" of collection \"${describe(collection)}\" declares ` +\n `\\`columnType: \"${prop.columnType}\"\\`, which is not a Postgres numeric type Rebase emits. ` +\n `Use one of: ${Object.keys(NUMBER_COLUMN_TYPES).join(\", \")}.`\n );\n }\n if (mapped.kind === \"numeric\") return numericType(prop);\n return mapped;\n }\n if (prop.validation?.integer || isId) return { kind: \"integer\" };\n return numericType(prop);\n};\n\n/** `NUMERIC`, with the precision and scale the property asks for. */\nconst numericType = (prop: NumberProperty): PgType => {\n const { precision, scale } = prop;\n if (precision === undefined) return { kind: \"numeric\" };\n return scale === undefined\n ? { kind: \"numeric\", precision }\n : { kind: \"numeric\", precision, scale };\n};\n\nconst stringType = (propName: string, prop: StringProperty, collection: CollectionConfig): PgType => {\n if (prop.enum) {\n // Throws on an empty list rather than naming a type nothing creates —\n // the column type and the `CREATE TYPE` come from one reading of `enum`.\n const labels = enumLabelsOf(propName, prop as Property, collection);\n const table = getTableName(collection);\n const column = resolveColumnName(propName, prop as Property);\n return {\n kind: \"enum\",\n schema: schemaOf(collection),\n name: `${table}_${column}`,\n varName: getEnumVarName(table, propName),\n labels\n };\n }\n if (prop.isId === \"uuid\" || prop.columnType === \"uuid\") return { kind: \"uuid\" };\n // The width comes from `validation.max` when the property states one. It\n // used to be a hardcoded 255 in the DDL generator and *absent* on the\n // Drizzle path, so the same property produced a bounded column down one\n // generator and an unbounded one down the other.\n if (prop.columnType === \"char\") return { kind: \"char\", length: resolveStringColumnLength(prop) };\n if (prop.columnType === \"varchar\") return { kind: \"varchar\", length: resolveStringColumnLength(prop) };\n if (prop.columnType !== undefined && prop.columnType !== \"text\") {\n throw new Error(\n `Property \"${propName}\" of collection \"${describe(collection)}\" declares ` +\n `\\`columnType: \"${String(prop.columnType)}\"\\`, which is not a Postgres string type Rebase emits. ` +\n \"Use one of: text, varchar, char, uuid.\"\n );\n }\n // `text` is the default, and the only length-unbounded choice.\n return { kind: \"text\" };\n};\n\nconst arrayElementType = (prop: ArrayProperty): PgType | undefined => {\n let colType = prop.columnType;\n if (!colType && prop.of && !Array.isArray(prop.of)) {\n const of = prop.of as Property;\n if (of.type === \"string\") colType = \"text[]\";\n else if (of.type === \"number\") colType = of.validation?.integer ? \"integer[]\" : \"numeric[]\";\n else if (of.type === \"boolean\") colType = \"boolean[]\";\n }\n switch (colType) {\n case \"text[]\": return { kind: \"text\" };\n case \"integer[]\": return { kind: \"integer\" };\n case \"boolean[]\": return { kind: \"boolean\" };\n case \"numeric[]\": return { kind: \"numeric\" };\n default: return undefined;\n }\n};\n\n/**\n * The Postgres type a property's column has.\n *\n * Every member of `DataType` has an arm. A type this does not know is a\n * generator that has not been taught it yet, and it says so — a silent `TEXT`\n * here is how `geopoint` ended up with a database column, no Drizzle key, and\n * every write to it discarded with a 201.\n */\nexport const columnPgType = (\n propName: string,\n prop: Property,\n collection: CollectionConfig,\n resolveCollection: ResolveCollection\n): PgType => {\n switch (prop.type) {\n case \"string\":\n return stringType(propName, prop as StringProperty, collection);\n case \"number\":\n return numberType(propName, prop as NumberProperty, collection, isIdProperty(propName, prop, collection));\n case \"boolean\":\n return { kind: \"boolean\" };\n case \"date\": {\n const dateProp = prop as DateProperty;\n if (dateProp.columnType === \"date\") return { kind: \"date\" };\n if (dateProp.columnType === \"time\") return { kind: \"time\" };\n return { kind: \"timestamptz\" };\n }\n case \"map\":\n return (prop as MapProperty).columnType === \"json\" ? { kind: \"json\" } : { kind: \"jsonb\" };\n // `{ latitude, longitude }` — a document, like `map`.\n case \"geopoint\":\n return { kind: \"jsonb\" };\n case \"array\": {\n const arrayProp = prop as ArrayProperty;\n if (arrayProp.columnType === \"json\") return { kind: \"json\" };\n const element = arrayElementType(arrayProp);\n return element ? { kind: \"array\", of: element } : { kind: \"jsonb\" };\n }\n case \"vector\":\n return { kind: \"vector\", dimensions: (prop as VectorProperty).dimensions };\n case \"binary\":\n return { kind: \"bytea\" };\n case \"relation\":\n case \"reference\": {\n const target = linkTarget(propName, prop, collection, resolveCollection);\n return target ? primaryKeyPgType(target) : { kind: \"text\" };\n }\n default:\n throw new Error(\n `No Postgres column type for property \"${propName}\" of type ` +\n `\"${(prop as Property).type}\" in collection \"${describe(collection)}\". ` +\n \"Add a case to `columnPgType` in schema/plan/plan-schema.ts.\"\n );\n }\n};\n\n/** The collection a `relation` or `reference` points at, when it resolves. */\nconst linkTarget = (\n propName: string,\n prop: Property,\n collection: CollectionConfig,\n resolveCollection: ResolveCollection\n): CollectionConfig | undefined => {\n if (prop.type === \"reference\") {\n const path = (prop as ReferenceProperty).path;\n return path ? resolveCollection(path) : undefined;\n }\n const relProp = prop as RelationProperty;\n const relation = findRelation(resolveCollectionRelations(collection), relProp.relation?.relationName ?? propName);\n if (relation?.kind !== \"belongsTo\") return undefined;\n try {\n return relation.target();\n } catch {\n return undefined;\n }\n};\n\n// ── Defaults ─────────────────────────────────────────────────────────────────\n\n/**\n * A `defaultValue` as a database DEFAULT.\n *\n * `defaultValue` reached neither the schema nor — until recently — the write\n * path, so a documented, type-checked field did nothing at all: the panel\n * pre-filled it, an insert from anywhere else (the REST API, a seed, psql) did\n * not, and the column came out NULL. A DEFAULT is the one place that binds\n * every writer.\n *\n * Only literals. A `reference`'s default is an `EntityReference` object and a\n * `vector`'s is an embedding — neither is a column literal, and inventing one\n * would be a value the write path and the database disagree about.\n * `undefined` for those, which is what \"no DEFAULT\" means everywhere here.\n */\nconst literalDefault = (prop: Property, type: PgType): ColumnDefault | undefined => {\n const value = (prop as { defaultValue?: unknown }).defaultValue;\n if (value === undefined || value === null) return undefined;\n\n switch (prop.type) {\n case \"string\":\n // An enum default is its id, which is exactly the label the type\n // carries — the same string the write path stores.\n return typeof value === \"string\" ? { kind: \"literal\", value, sql: quoteSqlLiteral(value) } : undefined;\n case \"number\":\n return typeof value === \"number\" && Number.isFinite(value)\n ? { kind: \"literal\", value, sql: String(value) }\n : undefined;\n case \"boolean\":\n return typeof value === \"boolean\"\n ? { kind: \"literal\", value, sql: value ? \"TRUE\" : \"FALSE\" }\n : undefined;\n case \"date\": {\n const iso = value instanceof Date\n ? (Number.isNaN(value.getTime()) ? undefined : value.toISOString())\n : typeof value === \"string\" ? value : undefined;\n return iso === undefined ? undefined : { kind: \"literal\", value: iso, sql: quoteSqlLiteral(iso) };\n }\n case \"map\":\n case \"array\":\n case \"geopoint\": {\n // A document default is a JSON literal cast to the column's own\n // type, so `jsonb` and `json` columns each get a value Postgres\n // accepts without an implicit cast.\n if (type.kind !== \"jsonb\" && type.kind !== \"json\") return undefined;\n let json: string;\n try {\n json = JSON.stringify(value);\n } catch {\n return undefined; // circular, or a BigInt: not a literal.\n }\n if (json === undefined) return undefined;\n return { kind: \"literal\", value, sql: `${quoteSqlLiteral(json)}::${type.kind}` };\n }\n default:\n return undefined;\n }\n};\n\n/** The DEFAULT a column carries: id strategy, then `autoValue`, then `defaultValue`. */\nconst columnDefault = (\n propName: string,\n prop: Property,\n collection: CollectionConfig,\n type: PgType\n): ColumnDefault | undefined => {\n const idDefault = idColumnDefault(propName, prop, collection);\n if (idDefault) {\n if (idDefault.kind === \"uuid\") return { kind: \"sql\", expression: \"gen_random_uuid()\" };\n if (idDefault.kind === \"identity\") return { kind: \"identity\" };\n return { kind: \"sql\", expression: idDefault.expression };\n }\n if (prop.type === \"date\") {\n const autoValue = (prop as DateProperty).autoValue;\n if (autoValue === \"on_create\" || autoValue === \"on_update\") {\n return { kind: \"sql\", expression: \"now()\" };\n }\n }\n return literalDefault(prop, type);\n};\n\n// ── Policies ─────────────────────────────────────────────────────────────────\n\n/**\n * One security rule, compiled to the clauses a policy is made of.\n *\n * The desugaring (`access` / `ownerField` / `roles` / structured condition /\n * raw SQL → `PolicyExpression`) and the SQL compilation are `@rebasepro/common`'s,\n * which is what the client-side evaluator uses too — so the UI, the DDL and the\n * database agree about who can read a row. What lives here is only the shape:\n * which operations a rule expands to, which clauses each operation takes, and\n * the deny-all fallback for a clause that compiled to nothing.\n */\nexport const compileSecurityRule = (\n collection: CollectionConfig,\n rule: SecurityRule,\n resolveCollection: ResolveCollection,\n injected: boolean,\n ruleKey = rule.name ?? \"(unnamed)\"\n): PolicyPlan[] => {\n const tableName = getTableName(collection);\n const ops = rule.operations && rule.operations.length > 0 ? rule.operations : [rule.operation ?? \"all\"];\n const policyNames = getPolicyNamesForRule(rule, tableName);\n const { usingExpr, withCheckExpr } = securityRuleToConditions(rule);\n\n return ops.map((operation, index) => {\n const needsUsing = operation !== \"insert\";\n const needsWithCheck = operation !== \"select\" && operation !== \"delete\";\n let using = needsUsing && usingExpr ? policyToPostgres(usingExpr, collection, { resolveCollection }) : null;\n let withCheck = needsWithCheck && withCheckExpr ? policyToPostgres(withCheckExpr, collection, { resolveCollection }) : null;\n // A clause that compiled to nothing denies rather than opens.\n if (!using && needsUsing) using = \"false\";\n if (!withCheck && needsWithCheck) withCheck = \"false\";\n return {\n name: policyNames[index],\n ruleKey,\n operation,\n mode: rule.mode ?? \"permissive\",\n roles: rule.pgRoles ? [...rule.pgRoles].sort() : [\"public\"],\n using,\n withCheck,\n injected\n };\n });\n};\n\n// ── The planner ──────────────────────────────────────────────────────────────\n\nexport function planSchema(allCollections: CollectionConfig[], options: PlanOptions = {}): SchemaPlan {\n // Before the filter, deliberately: a `search` block on a collection this\n // engine does not store would otherwise be dropped without a word.\n assertSearchIsPostgresOnly(allCollections);\n\n // A Firestore or MongoDB collection has no table here, and generating one\n // is not merely wasted output: `db push` would create it, and the doctor\n // would then report the store the collection actually reads from as drift.\n const collections = relationalCollections(allCollections);\n collections.forEach(assertSinglePrimaryKey);\n\n const resolveCollection: ResolveCollection = (slug) =>\n collections.find(c => c.slug === slug || getTableName(c) === slug);\n\n const declaredSchemas = Array.from(new Set(\n collections.map(c => (isPostgresCollectionConfig(c) ? c.schema : undefined)).filter(Boolean) as string[]\n ));\n const schemas = Array.from(new Set([REBASE_SCHEMA, ...declaredSchemas]));\n\n // ── Enum types ───────────────────────────────────────────────────────────\n // The name is derived from table + column, so two collections mapped onto\n // the same table — or two properties whose `columnName` resolves to the\n // same column — land on the same type. `CREATE TYPE` has no IF NOT EXISTS,\n // so emitting it twice aborts the whole file; deduplicated by name here,\n // once, for every renderer.\n const enums: EnumPlan[] = [];\n const seenEnums = new Set<string>();\n /**\n * Every enum type one table's properties declare, appended once.\n *\n * `declaredSchema` is passed rather than read off the config because a\n * junction's synthetic collection carries `schema: \"public\"` as a resolved\n * fact and not as a declaration — emitting it as one would make the\n * generated file reach for a `pgSchema(\"public\")` variable that exists for\n * nobody.\n */\n const collectEnumTypes = (\n collection: CollectionConfig,\n properties: Properties,\n declaredSchema: string | undefined\n ): void => {\n for (const [propName, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (!(\"enum\" in prop) || !prop.enum) continue;\n if (prop.type !== \"string\" && prop.type !== \"number\") continue;\n // Refuses an empty list whatever the property's type, then declares\n // a type only for the string ones: a `number` enum's column is\n // NUMERIC or INTEGER on every path, so the type used to be created\n // and referenced by nothing.\n const labels = enumLabelsOf(propName, prop, collection);\n if (!declaresEnumType(prop)) continue;\n const schema = schemaOf(collection);\n const name = `${getTableName(collection)}_${resolveColumnName(propName, prop)}`;\n const qualified = `${schema}.${name}`;\n if (seenEnums.has(qualified)) continue;\n seenEnums.add(qualified);\n enums.push({\n schema,\n name,\n qualified,\n declaredSchema,\n varName: getEnumVarName(getTableName(collection), propName),\n labels\n });\n }\n };\n for (const collection of collections) {\n collectEnumTypes(\n collection,\n (collection.properties ?? {}) as Properties,\n isPostgresCollectionConfig(collection) ? collection.schema : undefined\n );\n }\n\n // ── The table set ────────────────────────────────────────────────────────\n // Each collection's table, then any junction its relations imply, in the\n // order the collections arrive. `schema.sql` and `schema.generated.ts` are\n // compared against their committed copies, so this order is the file's.\n const tableEntries = new Map<string, {\n collection: CollectionConfig;\n junction?: { relation: ResolvedRelation; source: CollectionConfig };\n }>();\n for (const collection of collections) {\n const tableName = getTableName(collection);\n if (tableName) tableEntries.set(tableName, { collection });\n for (const relation of Object.values(resolveCollectionRelations(collection))) {\n if (!isManyToMany(relation)) continue;\n const junctionTable = relation.through.table;\n if (tableEntries.has(junctionTable)) continue;\n tableEntries.set(junctionTable, {\n collection: { table: junctionTable, properties: {} } as CollectionConfig,\n junction: { relation, source: collection }\n });\n }\n }\n\n const junctionSpecs = resolveJunctionSpecs(collections);\n\n // A junction's payload may declare an enum too, and the type it names is\n // created by nobody else: no collection owns the table, so the loop above\n // never sees the property. Appended after the collections' — the enum block\n // is compared byte-for-byte against its committed copy, and a junction\n // whose type interleaved with the collections' would reorder that file\n // whenever a `through.properties` block moved.\n for (const spec of junctionSpecs.values()) {\n if (Object.keys(spec.properties).length === 0) continue;\n collectEnumTypes(getJunctionCollectionConfig(spec), spec.properties, undefined);\n }\n\n const triggerFunctionNeeded = { value: false };\n\n const tables: TablePlan[] = [];\n for (const [tableName, entry] of tableEntries) {\n tables.push(entry.junction\n ? planJunctionTable(\n tableName, entry.junction.relation, entry.junction.source,\n junctionSpecs, resolveCollection, triggerFunctionNeeded)\n : planCollectionTable(entry.collection, resolveCollection, triggerFunctionNeeded));\n }\n\n // ── Extensions and functions ─────────────────────────────────────────────\n const extensions: string[] = [];\n const functions: string[] = [];\n const seenStatements = new Set<string>();\n const add = (into: string[], statement: string): void => {\n if (seenStatements.has(statement)) return;\n seenStatements.add(statement);\n into.push(statement);\n };\n for (const table of tables) {\n if (!table.search) continue;\n for (const statement of searchExtensionStatements(table.search)) add(extensions, statement);\n }\n // pgvector is a separate build behind an image, a grant and a provider\n // allow-list, so whether Rebase may install it is not Rebase's to decide.\n // See `DatabaseOptions.extensions`.\n if (vectorExtensionDeclared(options.databaseExtensions)\n && tables.some(t => t.vectorColumns.length > 0)) {\n add(extensions, vectorExtensionStatement());\n }\n for (const table of tables) {\n if (!table.search) continue;\n for (const statement of searchHelperFunctions(table.search)) add(functions, statement);\n }\n if (triggerFunctionNeeded.value) add(functions, setUpdatedAtFunction());\n\n return {\n schemas,\n declaredSchemas,\n enums,\n tables,\n relations: planRelations(collections, tableEntries),\n extensions,\n functions,\n collections,\n options\n };\n}\n\n// ── A collection's table ─────────────────────────────────────────────────────\n\nfunction planCollectionTable(\n collection: CollectionConfig,\n resolveCollection: ResolveCollection,\n triggerFunctionNeeded: { value: boolean }\n): TablePlan {\n const schema = schemaOf(collection);\n const table = bareTableName(getTableName(collection));\n const auth = isAuthCollection(collection);\n const columns: ColumnPlan[] = [];\n const triggers: TriggerPlan[] = [];\n const relations = resolveCollectionRelations(collection);\n\n for (const [propName, rawProp] of Object.entries(collection.properties ?? {})) {\n const prop = rawProp as Property;\n\n if (prop.type === \"relation\") {\n const column = planRelationColumn(propName, prop as RelationProperty, collection, relations, schema, table);\n if (column) columns.push(column);\n continue;\n }\n if (prop.type === \"reference\") {\n columns.push(planReferenceColumn(propName, prop as ReferenceProperty, collection, resolveCollection, schema, table));\n continue;\n }\n\n columns.push(planPropertyColumn(propName, prop, {\n collection,\n resolveCollection,\n schema,\n table,\n auth,\n triggers,\n triggerFunctionNeeded\n }));\n }\n\n // ── Auth columns the collection file never mentions ──────────────────────\n // `db push` is declarative: Atlas diffs the database against exactly what\n // is emitted and drops anything else. The scaffold's users collection\n // describes 12 columns while auth needs 14, so `is_anonymous` and\n // `tokens_valid_after` read as unmanaged drift and a push run after the\n // server had started once planned to DROP them.\n if (auth) {\n const declared = new Set(columns.map(c => c.column));\n for (const spec of AUTH_USERS_COLUMNS) {\n if (declared.has(spec.column)) continue;\n columns.push({\n key: spec.column,\n column: spec.column,\n type: authColumnType(spec.type),\n nullable: spec.notNull !== true,\n primaryKey: false,\n unique: false,\n sqlDefinition: authUsersColumnSql(spec),\n source: { kind: \"auth\", slug: collection.slug }\n });\n }\n }\n\n // ── The opt-in search column ─────────────────────────────────────────────\n // Last, so it reads as what it is: derived from the columns above it.\n // Postgres recomputes it on every write of a source column and rejects any\n // attempt to write it directly, which is what makes it impossible for the\n // index to drift from the row.\n const search = buildSearchColumnSpec(collection);\n if (search) {\n columns.push({\n key: search.column,\n column: search.column,\n type: { kind: \"tsvector\" },\n nullable: true,\n primaryKey: false,\n unique: false,\n generated: { expression: search.expression, stored: true },\n sqlDefinition: searchColumnTypeSql(search.expression, \"tsvector\"),\n source: { kind: \"search\", slug: collection.slug }\n });\n if (search.fuzzy) {\n columns.push({\n key: search.fuzzy.column,\n column: search.fuzzy.column,\n type: { kind: \"text\" },\n nullable: true,\n primaryKey: false,\n unique: false,\n generated: { expression: search.fuzzy.expression, stored: true },\n sqlDefinition: searchColumnTypeSql(search.fuzzy.expression, \"text\"),\n source: { kind: \"search\", slug: collection.slug }\n });\n }\n }\n\n // A collection that declares no primary key gets an implicit `id TEXT\n // PRIMARY KEY`, which is what `derivePrimaryKeys` reads back.\n if (!columns.some(c => c.primaryKey)) {\n columns.unshift({\n key: \"id\",\n column: \"id\",\n type: { kind: \"text\" },\n nullable: false,\n primaryKey: true,\n unique: false,\n source: { kind: \"implicit-id\", slug: collection.slug }\n });\n }\n\n const injected = new Set(getInjectedSecurityRules(collection).map(rule => rule.name));\n const policies = getEffectiveSecurityRules(collection).flatMap((rule, index) =>\n compileSecurityRule(\n collection,\n rule,\n resolveCollection,\n Boolean(rule.name && injected.has(rule.name)),\n rule.name ?? `#${index}`\n ));\n\n // ── Tenancy ──────────────────────────────────────────────────────────────\n // The policy is already above — `getEffectiveSecurityRules` injects it, so\n // it compiles, names and renders like every other rule. What is left is the\n // half a rule cannot express: the column is NOT NULL, and it is indexed.\n const tenantIndexes = applyTenantColumnEffects(collection, columns, schema, table);\n\n return {\n schema,\n table,\n qualified: `${schema}.${table}`,\n declaredSchema: isPostgresCollectionConfig(collection) ? collection.schema : undefined,\n varName: getTableVarName(getTableName(collection)),\n kind: \"collection\",\n slug: collection.slug,\n columns,\n primaryKey: columns.filter(c => c.primaryKey).map(c => c.column),\n indexes: [...buildCollectionIndexSpecs(collection, resolveColumnName), ...tenantIndexes],\n search,\n vector: buildVectorIndexPlan(collection, resolveColumnName),\n vectorColumns: buildVectorColumnSpecs(collection, resolveColumnName),\n policies,\n triggers,\n auth\n };\n}\n\n/** What {@link planPropertyColumn} needs about the table the column lands on. */\ninterface PropertyColumnContext {\n /** The collection, or the synthetic one standing in for a junction. */\n collection: CollectionConfig;\n resolveCollection: ResolveCollection;\n schema: string;\n /** Bare table name — the trigger name is derived from it. */\n table: string;\n /** Whether `auth-users-columns` owns this table's columns. */\n auth: boolean;\n /** Collected `BEFORE UPDATE` triggers, appended to. */\n triggers: TriggerPlan[];\n triggerFunctionNeeded: { value: boolean };\n}\n\n/**\n * One declared, non-linking property, as a column.\n *\n * Extracted from {@link planCollectionTable} the moment a second table needed\n * it: a `manyToMany`'s `through.properties` are columns on the junction, and\n * \"declared exactly like a collection's properties\" has to mean the same code\n * reads them, or it is a promise the next `columnType` or `defaultValue` change\n * quietly breaks. A junction payload column therefore gets the type, the\n * nullability, the default, the UNIQUE and the `on_update` trigger a collection\n * column with the same declaration gets — because it is the same function\n * answering.\n *\n * `relation` and `reference` do not come through here; they own a foreign key\n * and are planned by {@link planRelationColumn} / {@link planReferenceColumn}.\n * A junction payload may not declare either (config validation refuses it), so\n * this is the whole of a payload column.\n */\nfunction planPropertyColumn(\n propName: string,\n prop: Property,\n ctx: PropertyColumnContext\n): ColumnPlan {\n const { collection, resolveCollection, schema, table, auth } = ctx;\n\n const isId = isIdProperty(propName, prop, collection);\n const column = resolveColumnName(propName, prop);\n const type = columnPgType(propName, prop, collection, resolveCollection);\n const required = prop.validation?.required === true;\n const defaultValue = columnDefault(propName, prop, collection, type);\n // On an auth collection, the columns auth itself reads and writes have\n // exactly one definition wherever the table is created from — see\n // `auth-users-columns`. Anything else there is an ordinary field.\n const authDefinition = auth && !isId ? authUsersColumnDefinition(column) : undefined;\n\n const plan: ColumnPlan = {\n key: propName,\n column,\n type,\n // A primary key is NOT NULL whether or not anyone writes it.\n nullable: !(isId || required),\n primaryKey: isId,\n // `validation.unique` holds for every type. The Drizzle generator\n // honoured it on `string` and `number` only, so a unique `date` or\n // `map` was UNIQUE in the database and not in the file drizzle-kit\n // plans from. A primary key gets neither UNIQUE nor NOT NULL —\n // both are implied, and emitting them made drizzle-kit plan an\n // extra constraint against a database `db push` built without one.\n unique: !isId && prop.validation?.unique === true,\n default: defaultValue,\n sqlDefinition: authDefinition,\n source: { kind: \"property\", propName, slug: collection.slug }\n };\n if (prop.type === \"date\" && (prop as DateProperty).autoValue === \"on_update\") {\n plan.touchOnUpdate = true;\n ctx.triggerFunctionNeeded.value = true;\n ctx.triggers.push({\n schema,\n table,\n column,\n name: toPostgresIdentifier(`${table}_${column}_touch`)\n });\n }\n return plan;\n}\n\n/**\n * The two schema effects a `tenant` declaration has that a policy cannot state.\n *\n * The policy itself is injected as an ordinary `SecurityRule`\n * (`buildTenantSecurityRule`) and compiled above with every other rule, so\n * nothing here touches `policies`. What is left is the column:\n *\n * - **`NOT NULL`.** A row whose tenant is NULL belongs to nobody, and the\n * restrictive policy compares the column to the caller's tenant — a\n * comparison against NULL is never true. Such a row is therefore invisible to\n * every caller, forever, including the one who wrote it. The write path\n * refuses to create one (`TENANT_REQUIRED`); this is the same statement made\n * where a raw `INSERT`, a seed and a migration can also hear it.\n * - **An index.** The tenancy predicate is ANDed into *every* statement against\n * the table, so an unindexed tenant column turns every read into a sequential\n * scan filtered by tenant. It is the one index a tenant-scoped table cannot\n * be without, and the one people most reliably forget — which is the whole\n * argument for deriving it rather than documenting it.\n *\n * The index goes through `deriveIndexName` like every other index, so it is a\n * Rebase-managed name (`_ix_<fingerprint>`), it is in the Atlas diff, and the\n * doctor recognises it. Because that name is a fingerprint of the index's\n * semantics, an author who has *already* declared a plain btree index on the\n * tenant column derives byte-for-byte the same name — so the two are the same\n * object and this adds nothing rather than colliding.\n *\n * Throws when the field names no column. `planSchema` throws rather than\n * guessing, and the alternative here is a table with a tenancy policy over a\n * column that does not exist: `CREATE POLICY` fails, RLS stays enabled, and the\n * collection denies every row.\n */\nfunction applyTenantColumnEffects(\n collection: CollectionConfig,\n columns: ColumnPlan[],\n schema: string,\n table: string\n): CollectionIndexSpec[] {\n const tenant = getTenantConfig(collection);\n if (!tenant) return [];\n\n const column = findTenantColumn(collection, columns, tenant.field);\n if (!column) {\n throw new Error(\n `Collection \"${describe(collection)}\" declares \\`tenant: { field: \"${tenant.field}\" }\\`, and ` +\n \"no property of that name reaches a column. `tenant` says what a column means; it does not \" +\n \"create one. Declare the property, or name the one that holds the tenant id.\"\n );\n }\n\n // A primary key is already NOT NULL and already indexed by `<table>_pkey`.\n // Saying either again would make `db push` plan a constraint and an index\n // the database has no reason to hold.\n if (column.primaryKey) return [];\n\n column.nullable = false;\n\n const withoutName = {\n schema,\n table,\n method: \"btree\" as const,\n unique: false,\n keys: [{ column: column.column, direction: \"asc\" as const, nulls: \"last\" as const }],\n include: [],\n predicate: null,\n reason: TENANT_INDEX_REASON\n };\n return [{ ...withoutName, indexName: deriveIndexName(withoutName) }];\n}\n\n/**\n * The planned column a `tenant.field` names.\n *\n * Not simply `columns.find(c => c.key === field)`: a `belongsTo` relation\n * property emits its column under the *foreign key's* field name, so\n * `tenant: { field: \"org\" }` beside `org: { type: \"relation\", … }` has to\n * resolve through the relation to `org_id`. Matching only on `key` found\n * nothing there and the declaration would have been refused as naming no\n * column — for the shape the documentation recommends.\n */\nfunction findTenantColumn(\n collection: CollectionConfig,\n columns: ColumnPlan[],\n field: string\n): ColumnPlan | undefined {\n const direct = columns.find(c => c.key === field);\n if (direct) return direct;\n\n const prop = collection.properties?.[field] as Property | undefined;\n if (prop?.type === \"relation\") {\n const relations = resolveCollectionRelations(collection);\n const relation = findRelation(relations, (prop as RelationProperty).relation?.relationName ?? field);\n if (relation?.kind === \"belongsTo\" && relation.localKey) {\n return columns.find(c => c.column === relation.localKey);\n }\n return undefined;\n }\n if (!prop) return undefined;\n const columnName = resolveColumnName(field, prop);\n return columns.find(c => c.column === columnName);\n}\n\n/**\n * The column a `belongsTo` relation owns, or `undefined` when the relation puts\n * no column on this table.\n *\n * Every other kind is a column on the target, a junction row, or a join chain.\n */\nfunction planRelationColumn(\n propName: string,\n prop: RelationProperty,\n collection: CollectionConfig,\n relations: Record<string, ResolvedRelation>,\n schema: string,\n table: string\n): ColumnPlan | undefined {\n const relation = findRelation(relations, prop.relation?.relationName ?? propName);\n if (relation?.kind !== \"belongsTo\") return undefined;\n\n let target: CollectionConfig;\n try {\n target = relation.target();\n } catch {\n // A relation whose target is not in this bundle yields no column and no\n // constraint. Every emitter returned early here, and they must keep\n // agreeing: a column with no constraint down one path is a schema fork.\n return undefined;\n }\n if (!target) return undefined;\n\n const required = prop.validation?.required === true;\n // The relation and an explicit FK property can both be declared; the\n // explicit one owns the column, and the relation still owns the constraint.\n // Asked of the *field key* rather than the column — `properties[\"post_id\"]`\n // is not where a property declared `postId: { columnName: \"post_id\" }`\n // lives, so this used to emit the column twice and `CREATE TABLE` failed\n // with \"column specified more than once\".\n const fkFieldKey = fieldKeyForColumn(collection, relation.localKey);\n const columnOwnedByProperty = Boolean(collection.properties?.[fkFieldKey]) && propName !== fkFieldKey;\n\n // Only a *derived* column name can be affected by the change to\n // `generateForeignKeyName`; one the author wrote is theirs.\n const relationName = prop.relation?.relationName ?? propName;\n const legacyKey = legacyForeignKeyName(relationName);\n const derived = relation.localKey === generateForeignKeyName(relationName);\n\n return {\n key: fkFieldKey,\n column: relation.localKey,\n type: primaryKeyPgType(target),\n nullable: !required,\n primaryKey: false,\n // `validation.unique` on a link is a one-to-one, and all three emitters\n // dropped it: the property said unique, the column was not, and a\n // second row could point at the same parent.\n unique: prop.validation?.unique === true,\n columnOwnedByProperty: columnOwnedByProperty || undefined,\n legacyColumn: derived && legacyKey !== relation.localKey ? legacyKey : undefined,\n foreignKey: foreignKeyPlan({\n schema,\n table,\n column: relation.localKey,\n targetSchema: schemaOf(target),\n targetTable: bareTableName(getTableName(target)),\n targetColumn: getPrimaryKeyName(target),\n onDelete: relation.onDelete ?? defaultBelongsToOnDelete(required),\n onUpdate: relation.onUpdate\n }),\n source: { kind: \"relation\", propName, slug: collection.slug }\n };\n}\n\n/** The column a `reference` property owns — a foreign key like any other. */\nfunction planReferenceColumn(\n propName: string,\n prop: ReferenceProperty,\n collection: CollectionConfig,\n resolveCollection: ResolveCollection,\n schema: string,\n table: string\n): ColumnPlan {\n // A `reference` with no `path` names no collection, so it keeps its column\n // (the id is still a string) and gets no constraint — the same shape as one\n // whose target is not in this bundle.\n const target = prop.path ? resolveCollection(prop.path) : undefined;\n const column = resolveColumnName(propName, prop as Property);\n const required = prop.validation?.required === true;\n\n return {\n key: propName,\n column,\n // A `reference` whose target is not in the bundle keeps its column (the\n // id is still a string) and gets no constraint.\n type: target ? primaryKeyPgType(target) : { kind: \"text\" },\n nullable: !required,\n primaryKey: false,\n unique: prop.validation?.unique === true,\n foreignKey: target\n ? foreignKeyPlan({\n schema,\n table,\n column,\n targetSchema: schemaOf(target),\n targetTable: bareTableName(getTableName(target)),\n targetColumn: getPrimaryKeyName(target),\n // The same rule as `belongsTo`, from the same function. A\n // `reference` carries no `onDelete` of its own to override it.\n onDelete: defaultBelongsToOnDelete(required)\n })\n : undefined,\n source: { kind: \"reference\", propName, slug: collection.slug }\n };\n}\n\n/**\n * An auth-owned column's declared SQL type, as a tag.\n *\n * `auth-users-columns` states these as SQL because that is the grammar its three\n * creators write in; the plan needs the tag so nothing downstream has to parse a\n * type back apart. Deliberately exhaustive rather than a fallback: a column auth\n * adds with a type nobody mapped should say so here, not turn into `TEXT` in a\n * table the login flow reads.\n */\nconst AUTH_COLUMN_TYPES: Record<string, PgType> = {\n \"TEXT\": { kind: \"text\" },\n \"TEXT[]\": { kind: \"array\", of: { kind: \"text\" } },\n \"BOOLEAN\": { kind: \"boolean\" },\n \"JSONB\": { kind: \"jsonb\" },\n \"TIMESTAMP WITH TIME ZONE\": { kind: \"timestamptz\" }\n};\n\nconst authColumnType = (sqlType: string): PgType => {\n const mapped = AUTH_COLUMN_TYPES[sqlType];\n if (!mapped) {\n throw new Error(\n `\\`auth-users-columns\\` declares a column of type \"${sqlType}\", which the schema plan ` +\n \"has no tag for. Add it to `AUTH_COLUMN_TYPES` in schema/plan/plan-schema.ts.\"\n );\n }\n return mapped;\n};\n\n// ── A junction table ─────────────────────────────────────────────────────────\n\nfunction planJunctionTable(\n tableName: string,\n relation: ResolvedRelation,\n source: CollectionConfig,\n junctionSpecs: ReturnType<typeof resolveJunctionSpecs>,\n resolveCollection: ResolveCollection,\n triggerFunctionNeeded: { value: boolean }\n): TablePlan {\n if (!isManyToMany(relation)) {\n throw new Error(`Internal: junction table \"${tableName}\" was reached from a ${relation.kind} relation.`);\n }\n const table = bareTableName(tableName);\n // Junctions live in `public`, full stop — `resolveJunctionSpecs` hardcodes\n // that, so it is where the tables are created and where the derived RLS\n // policies are applied. Inheriting the endpoint's schema put the junction\n // of any m2m onto `users` in `rebase` while its policies were still created\n // against `public.<junction>`: RLS enabled on one table, rows in another.\n const schema = \"public\";\n const target = relation.target();\n const { sourceColumn, targetColumn } = relation.through;\n // Every declaring side agrees on the edge's lifetime; the first one wins.\n const onDelete = relation.onDelete ?? \"CASCADE\";\n\n const spec = junctionSpecs.get(table);\n const legacyFor = (collection: CollectionConfig, column: string): string | undefined => {\n // A junction column's default name is derived from the endpoint\n // collection's slug — normally plural, so this is where the\n // singularization change lands: a `categories` endpoint used to give\n // `categorie_id` and now gives `category_id`. Recorded, not used.\n const slug = toSnakeCase(collection.slug ?? collection.name ?? \"\");\n const legacy = legacyForeignKeyName(slug);\n return column === generateForeignKeyName(slug) && legacy !== column ? legacy : undefined;\n };\n\n const endpoint = (collection: CollectionConfig, column: string): ColumnPlan => ({\n key: column,\n column,\n type: primaryKeyPgType(collection),\n nullable: false,\n // Part of the composite key on {@link TablePlan.primaryKey}, not a\n // key of its own — an inline `PRIMARY KEY` here would be a second one.\n primaryKey: false,\n unique: false,\n legacyColumn: legacyFor(collection, column),\n foreignKey: foreignKeyPlan({\n schema,\n table,\n column,\n targetSchema: schemaOf(collection),\n targetTable: bareTableName(getTableName(collection)),\n targetColumn: getPrimaryKeyName(collection),\n onDelete\n }),\n source: { kind: \"junction-key\" }\n });\n\n const columns = [endpoint(source, sourceColumn), endpoint(target, targetColumn)];\n\n // ── The junction's own columns ───────────────────────────────────────────\n // `through.properties`: the `role` on a membership, the `position` on a\n // tag. Planned by the same {@link planPropertyColumn} the collection tables\n // use, against the synthetic collection {@link getJunctionCollectionConfig}\n // builds — so a payload property gets exactly the type, nullability,\n // default, UNIQUE and enum type it would get on a table, and there is no\n // second `switch (prop.type)` anywhere to disagree with the first.\n //\n // Not supported here, and deliberately: `indexes` (declared per collection,\n // and no collection declares a junction), `search`, `vector`, and any\n // linking property — a payload cannot be a `relation`, a `reference` or a\n // `vector`. Config validation refuses those with the relation named; this\n // would otherwise reach `columnPgType` and produce a column no writer knows\n // how to fill.\n const triggers: TriggerPlan[] = [];\n if (spec && Object.keys(spec.properties).length > 0) {\n const junctionCollection = getJunctionCollectionConfig(spec);\n const keyColumns = new Set([sourceColumn, targetColumn]);\n for (const [propName, rawProp] of Object.entries(spec.properties)) {\n const prop = rawProp as Property;\n const plan = planPropertyColumn(propName, prop, {\n collection: junctionCollection,\n resolveCollection,\n schema,\n table,\n auth: false,\n triggers,\n triggerFunctionNeeded\n });\n // A payload column that resolves onto a key column would be a\n // second definition of it — refused by `checkJunctionPayload` at\n // boot, and skipped here so a config that got past it still\n // produces a table rather than a duplicate-column CREATE.\n if (keyColumns.has(plan.column)) continue;\n columns.push(plan);\n }\n }\n\n // Junction tables are generated tables like any other: locked by default,\n // with derived policies — reads follow the endpoints' visibility, writes\n // follow the declaring side's update rules. Without them they were the one\n // kind of generated table with no RLS at all.\n const policies = spec\n ? getJunctionSecurityRules(spec).flatMap((rule, index) =>\n compileSecurityRule(\n getJunctionCollectionConfig(spec), rule, resolveCollection, false, rule.name ?? `#${index}`))\n : [];\n\n return {\n schema,\n table,\n qualified: `${schema}.${table}`,\n varName: getTableVarName(tableName),\n kind: \"junction\",\n declaringSlugs: spec?.declaringSides.map(side => side.collection.slug) ?? [],\n columns,\n primaryKey: [sourceColumn, targetColumn],\n indexes: [],\n vectorColumns: [],\n policies,\n triggers,\n auth: false\n };\n}\n\n// ── Drizzle relations ────────────────────────────────────────────────────────\n\n/**\n * The `relations(...)` entries the generated file carries.\n *\n * Both sides of a link must share a `relationName` or Drizzle cannot pair them;\n * `sharedRelationName` is the rule, and each side derives it independently.\n */\nfunction planRelations(\n collections: CollectionConfig[],\n tableEntries: Map<string, { collection: CollectionConfig; junction?: { relation: ResolvedRelation; source: CollectionConfig } }>\n): RelationPlan[] {\n const plans: RelationPlan[] = [];\n\n for (const [tableName, entry] of tableEntries) {\n const tableVar = getTableVarName(tableName);\n\n if (entry.junction) {\n const { relation, source } = entry.junction;\n if (!isManyToMany(relation)) continue;\n const target = relation.target();\n // The owning relation's name, shared with the source table's\n // `many(junction, { relationName })`.\n const owningRelationName = relation.relationName ?? toSnakeCase(getTableName(target));\n let inverseRelationName: string | undefined;\n try {\n for (const targetRel of Object.values(resolveCollectionRelations(target))) {\n if (targetRel.kind !== \"belongsTo\"\n && targetRel.cardinality === \"many\"\n && targetRel.relationName === owningRelationName) {\n inverseRelationName = targetRel.relationName;\n break;\n }\n }\n } catch {\n // The inverse side may not exist; the synthesized name below is\n // then what keeps the two `one()`s from colliding.\n }\n plans.push({\n tableVar,\n key: relation.through.sourceColumn,\n kind: \"one\",\n targetVar: getTableVarName(getTableName(source)),\n relationName: owningRelationName,\n fields: [relation.through.sourceColumn],\n references: [getPrimaryKeyName(source)]\n });\n plans.push({\n tableVar,\n key: relation.through.targetColumn,\n kind: \"one\",\n targetVar: getTableVarName(getTableName(target)),\n relationName: inverseRelationName ?? `${tableName}_${relation.through.targetColumn}`,\n fields: [relation.through.targetColumn],\n references: [getPrimaryKeyName(target)]\n });\n continue;\n }\n\n const collection = entry.collection;\n // `resolveCollectionRelations` already deduplicates, but an alias entry\n // for the same foreign key would otherwise emit twice.\n const emitted = new Set<string>();\n for (const [key, relation] of Object.entries(resolveCollectionRelations(collection))) {\n let target: CollectionConfig;\n try {\n target = relation.target();\n } catch {\n // A link to a collection outside this bundle is not a relation\n // this file can describe.\n continue;\n }\n const targetVar = getTableVarName(getTableName(target));\n const relationName = sharedRelationName(relation, collection);\n const dedupe = `${relationName}::${relation.kind}`;\n if (emitted.has(dedupe)) continue;\n emitted.add(dedupe);\n\n switch (relation.kind) {\n case \"belongsTo\":\n plans.push({\n tableVar,\n key,\n kind: \"one\",\n targetVar,\n relationName,\n // `localKey` is a COLUMN and the generated object is\n // keyed by PROPERTY: `user_id` is exposed as `userId`,\n // and emitting the column produces a file that does not\n // compile.\n fields: [fieldKeyForColumn(collection, relation.localKey)],\n references: [getPrimaryKeyName(target)]\n });\n break;\n case \"hasOne\":\n // The foreign key lives on the TARGET table, so this side\n // has no `fields`/`references` to give — and Drizzle has no\n // third form. `one(target, { relationName })` is not a\n // `RelationConfig` (TS2345) *and* not something the runtime\n // survives: `createOne` reads `config.fields.reduce(...)`\n // unconditionally. A bare `one(target)` is the documented\n // FK-less form.\n plans.push({ tableVar, key, kind: \"one\", targetVar });\n break;\n case \"hasMany\":\n plans.push({ tableVar, key, kind: \"many\", targetVar, relationName });\n break;\n case \"manyToMany\":\n plans.push({\n tableVar,\n key,\n kind: \"many\",\n targetVar: getTableVarName(relation.through.table),\n relationName\n });\n break;\n case \"via\":\n // A join chain is resolved at query time, not modelled as a\n // Drizzle relation.\n break;\n }\n }\n\n // Reciprocals the far side declares and this one does not. Drizzle\n // needs both halves of a pair to exist for a relational query to work.\n for (const other of collections) {\n if (other.slug === collection.slug) continue;\n for (const otherRel of Object.values(resolveCollectionRelations(other))) {\n if (!hasForeignKeyOnTarget(otherRel)) continue;\n let otherTarget: CollectionConfig;\n try {\n otherTarget = otherRel.target();\n } catch {\n continue;\n }\n if (otherTarget.slug !== collection.slug) continue;\n const relationName = sharedRelationName(otherRel, other);\n const dedupe = `${relationName}::belongsTo`;\n if (emitted.has(dedupe)) continue;\n emitted.add(dedupe);\n const otherVar = getTableVarName(getTableName(other));\n const fieldKey = fieldKeyForColumn(collection, otherRel.foreignKeyOnTarget);\n plans.push({\n tableVar,\n key: `_synth_${otherVar}_${fieldKey}`,\n kind: \"one\",\n targetVar: otherVar,\n relationName,\n fields: [fieldKey],\n // The column the far side points at: its primary key,\n // unless the link names another one with `sourceKey`.\n references: [otherRel.sourceKey\n ? fieldKeyForColumn(other, otherRel.sourceKey)\n : getPrimaryKeyName(other)]\n });\n }\n }\n }\n\n return plans;\n}\n\nexport { SET_UPDATED_AT_FN };\n"],"mappings":"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"}
|