import type { MetaField } from "@metaobjectsdev/metadata"; import type { Dialect, ColumnNamingStrategy } from "./metaobjects-config.js"; export type { Dialect }; /** * Discriminated union describing how a column default should be emitted. * - { kind: "now" } — dialect-aware: sql`CURRENT_TIMESTAMP` (sqlite) or .defaultNow() (postgres) * - { kind: "sqlExpr"; raw } — raw SQL expression wrapped in sql`...` (CURRENT_DATE, CURRENT_TIME, function calls) * - { kind: "literal"; value } — .default(JSON.stringify(value)) * - { kind: "arrayLiteral"; elements } — .default([...]) for an isArray field. Drizzle's * `.array().default(x)` (postgres) and `.$type().default(x)` (sqlite json) * both want a JS array, NOT the raw metadata string ("{}" / "[]" / "{a,b}"), * which would fail `tsc` (TS2345). The metadata @default MUST be a string (the * Java loader rejects a JSON array default), so the array literal is parsed out * of that string here and emitted as a real JS array. Elements are pre-rendered * TS literal source (numbers/booleans bare, strings quoted). */ export type DefaultExpr = { kind: "now"; } | { kind: "sqlExpr"; raw: string; } | { kind: "literal"; value: unknown; } | { kind: "arrayLiteral"; elements: string[]; }; /** * An int-backed `field.enum` column: a generated Drizzle `customType` whose * `toDriver`/`fromDriver` translate member symbol <-> stored integer, so the * codec lives in the COLUMN definition rather than in the query layer. * * This is the TS analogue of what every other port already does at its own * `MetaField` codec seam (EF Core `HasConversion`, OMDB `JdbcFieldCodec`, Exposed * `customEnumeration`, Python `ObjectManager` coercion) — which is why it was * chosen over a Zod write-transform plus a bespoke read-decode: TS's generated * queries hand back raw Drizzle rows and have no decode seam at all, so a * query-layer codec would have meant inventing one and wrapping every generated * read. Binding through the column type also makes filter values encode for free. */ export interface EnumIntCustomType { /** Local const name for the customType column helper, e.g. `orderStatusEnumCol`. */ fnConstName: string; /** Local const name for the symbol->int map, e.g. `ORDER_STATUS_TO_INT`. */ toIntConstName: string; /** Local const name for the int->symbol map, e.g. `ORDER_STATUS_FROM_INT`. */ fromIntConstName: string; /** Physical column type for `dataType()` — always integer for an int-backed enum. */ dataType: string; /** Member symbols, in `@values` order (the TS union and the map key order). */ members: string[]; /** Member symbol -> stored integer. */ intByMember: Record; } export interface ColumnSpec { /** Drizzle function name, e.g., "text", "integer", "varchar". */ fnName: string; /** * When set, `fnName` names a LOCAL generated const (this spec's customType * helper) rather than a Drizzle export — the renderer must NOT `imp()` it. */ enumIntCustomType?: EnumIntCustomType; /** DB column name (snake_case from field name, or @column override). */ dbName: string; /** Positional args after dbName (currently always empty; reserved). */ fnArgs: unknown[]; /** Object passed as second arg if non-empty (e.g., { length: 200 }, { mode: 'boolean' }). */ fnOptions?: Record; /** Method chain modifiers, e.g., [".notNull()", ".unique()"]. */ modifiers: string[]; /** Default expression for the column — dialect-specific emission handled by the template. */ defaultExpr?: DefaultExpr; /** Drizzle import module: "drizzle-orm/sqlite-core" or "drizzle-orm/pg-core". */ importModule: string; /** Optional leading line-comment for the generated column (e.g., type-fallback notice). */ leadingComment?: string; /** Optional CHECK constraint expression for the column (e.g., `status IN ('A', 'B')`). */ checkConstraint?: string; /** * Optional `.$type<...>()` chain target. Renderer (drizzle-schema.ts) emits * it ahead of the modifiers chain, using ts-poet `imp()` for objectRef * variants so the cross-module type import auto-hoists. `array` controls the * `[]` suffix: a single value (`VO`) vs a collection (`VO[]`). * `kind: "scalar"` covers string[]/number[]/boolean[] — no import needed. * `kind: "objectRef"` covers SourceLens/Dissent/etc. — only the bare VO `name` * is carried; the renderer resolves the import MODULE via the shared * `valueObjectModuleSpecifier` (layout/package/extStyle-aware, identical to the * field's TS type + Zod schema). A single Postgres jsonb object column * (`array: false`) gets `.$type()`; an array of VOs held in one jsonb * column gets `.$type()`. */ dollarTypeRef?: { kind: "scalar"; tsType: "string" | "number" | "boolean"; array: boolean; } | { kind: "objectRef"; name: string; array: boolean; } | { kind: "map"; value: { scalar: "string" | "number" | "boolean"; } | { objectRef: string; }; }; } /** Check for validator.required child OR @required attr. * Uses field.validators() (effective) so inherited validators are seen. * * Exported because it is load-bearing for FR-035: this predicate drives the * Drizzle column's `.notNull()` (below), and the SAME predicate must drive the * Zod UpdateSchema's `.nullable()` exclusion (zod-validators.ts) — a non-required * column is nullable in BOTH or the two disagree and `.set({field:null})` fails * the typecheck / NOT NULL. Sharing one function keeps them aligned by construction. */ export declare function isRequired(field: MetaField): boolean; /** * #195 — a field whose value is derived by an origin.aggregate `@agg:any|all|collect` * is COALESCE-guaranteed non-null in the synthesized view (any→false, all→true, * collect→[]), so its read type is non-null even when the field is not `@required`. * Drives `.notNull()` below so the Drizzle view column AND the Zod read schema agree * (projection-decl derives its `.nullable()` from these modifiers). origin.first is * deliberately NOT here — an empty related set selects no row (→ null); origin.computed * nullability is expression-dependent, so it stays the conservative nullable default. */ export declare function originGuaranteedNonNull(field: MetaField): boolean; export declare function mapColumnType(field: MetaField, dialect: Dialect, strategy?: ColumnNamingStrategy, timestampMode?: "date" | "string"): ColumnSpec; //# sourceMappingURL=column-mapper.d.ts.map