@cleverbrush/knex-schema 3.1.0 → 4.1.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/README.md +156 -1
- package/dist/SchemaQueryBuilder.d.ts +72 -575
- package/dist/chunk-V4TH6K42.js +2 -0
- package/dist/chunk-V4TH6K42.js.map +1 -0
- package/dist/columns.d.ts +73 -4
- package/dist/ddl.d.ts +66 -0
- package/dist/entity.d.ts +264 -0
- package/dist/extension.d.ts +1176 -1
- package/dist/extension.js +2 -0
- package/dist/extension.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +60 -1
- package/dist/index.js.map +1 -1
- package/dist/migration.d.ts +160 -0
- package/dist/operations/delete.d.ts +6 -0
- package/dist/operations/helpers.d.ts +57 -0
- package/dist/operations/insert.d.ts +26 -0
- package/dist/operations/join.d.ts +6 -0
- package/dist/operations/pagination.d.ts +15 -0
- package/dist/operations/select.d.ts +15 -0
- package/dist/operations/state.d.ts +39 -0
- package/dist/operations/update.d.ts +7 -0
- package/dist/operations/where.d.ts +29 -0
- package/dist/raw.d.ts +35 -0
- package/dist/snapshot.d.ts +39 -0
- package/dist/types.d.ts +269 -0
- package/package.json +6 -2
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{arrayExtensions as p,defineExtension as d,NumberSchemaBuilder as x,numberExtensions as S,ObjectSchemaBuilder as g,StringSchemaBuilder as b,SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR as E,stringExtensions as B,withExtensions as f}from"@cleverbrush/schema";import{EXTRA_TYPE_BRAND as Q,METHOD_LITERAL_BRAND as W}from"@cleverbrush/schema";var D=Symbol.for("@cleverbrush/knex-schema:primaryKey"),P=Symbol.for("@cleverbrush/knex-schema:compositePrimaryKey"),v=Symbol.for("@cleverbrush/knex-schema:polymorphicType");function r(n){return this.withExtension("columnName",n)}function T(n){return this.withExtension("tableName",n)}var w=d({string:{hasColumnName(n){return r.call(this,n)}},number:{hasColumnName(n){return r.call(this,n)}},boolean:{hasColumnName(n){return r.call(this,n)}},date:{hasColumnName(n){return r.call(this,n)}},any:{hasColumnName(n){return r.call(this,n)}},func:{hasColumnName(n){return r.call(this,n)}},array:{hasColumnName(n){return r.call(this,n)}},union:{hasColumnName(n){return r.call(this,n)}},generic:{hasColumnName(n){return r.call(this,n)}},object:{hasTableName(n){return T.call(this,n)}}}),R=d({number:{references(n,e="id"){return this.withExtension("references",{table:n,column:e})},onDelete(n){return this.withExtension("onDelete",n)},onUpdate(n){return this.withExtension("onUpdate",n)},defaultTo(n){return this.withExtension("defaultTo",n)},index(n){return this.withExtension("index",n??!0)},unique(n){return this.withExtension("unique",n??!0)},columnType(n){return this.withExtension("columnType",n)},bigint(){return this.withExtension("columnType","bigint")},smallint(){return this.withExtension("columnType","smallint")},decimal(n,e){return this.withExtension("columnType",`decimal(${n},${e})`)},defaultToRaw(n){return this.withExtension("defaultTo",{raw:n})},rowVersion(n){return this.withExtension("rowVersion",{strategy:n?.strategy??"increment"})}},string:{columnType(n){return this.withExtension("columnType",n)},text(){return this.withExtension("columnType","text")},asUuid(){return this.withExtension("columnType","uuid")},citext(){return this.withExtension("columnType","citext")},jsonb(){return this.withExtension("columnType","jsonb")},tsvector(){return this.withExtension("columnType","tsvector")},references(n,e="id"){return this.withExtension("references",{table:n,column:e})},onDelete(n){return this.withExtension("onDelete",n)},onUpdate(n){return this.withExtension("onUpdate",n)},unique(n){return this.withExtension("unique",n??!0)},index(n){return this.withExtension("index",n??!0)},defaultTo(n){return this.withExtension("defaultTo",n)},check(n){return this.withExtension("check",n)},defaultToRaw(n){return this.withExtension("defaultTo",{raw:n})},rowVersion(){return this.withExtension("rowVersion",{strategy:"manual"})}},boolean:{defaultTo(n){return this.withExtension("defaultTo",n)},columnType(n){return this.withExtension("columnType",n)},index(n){return this.withExtension("index",n??!0)},unique(n){return this.withExtension("unique",n??!0)}},date:{defaultTo(n){return this.withExtension("defaultTo",n)},columnType(n){return this.withExtension("columnType",n)},index(n){return this.withExtension("index",n??!0)},defaultToRaw(n){return this.withExtension("defaultTo",{raw:n})},timestamptz(){return this.withExtension("columnType","timestamptz")},dateOnly(){return this.withExtension("columnType","date")},rowVersion(){return this.withExtension("rowVersion",{strategy:"timestamp"})}},object:{columnType(n){return this.withExtension("columnType",n)},jsonb(){return this.withExtension("columnType","jsonb")},json(){return this.withExtension("columnType","json")},hasIndex(n,e){let a=this.getExtension("indexes")??[];return this.withExtension("indexes",[...a,{columns:n,...e}])},hasUnique(n,e){let a=this.getExtension("uniques")??[];return this.withExtension("uniques",[...a,{columns:n,name:e}])},hasCheck(n){let e=this.getExtension("checks")??[];return this.withExtension("checks",[...e,n])},hasRawColumn(n,e){let a=this.getExtension("rawColumns")??[];return this.withExtension("rawColumns",[...a,{name:n,definition:e}])},hasRawIndex(n){let e=this.getExtension("rawIndexes")??[];return this.withExtension("rawIndexes",[...e,n])},hasMany(n,e){let a=this.getExtension("relations")??[];return this.withExtension("relations",[...a,{type:"hasMany",name:n,...e}])},hasOne(n,e){let a=this.getExtension("relations")??[];return this.withExtension("relations",[...a,{type:"hasOne",name:n,...e}])},belongsTo(n,e){let a=this.getExtension("relations")??[];return this.withExtension("relations",[...a,{type:"belongsTo",name:n,...e}])},belongsToMany(n,e){let a=this.getExtension("relations")??[];return this.withExtension("relations",[...a,{type:"belongsToMany",name:n,...e}])},hasTimestamps(n){return this.withExtension("timestamps",{createdAt:n?.createdAt??"created_at",updatedAt:n?.updatedAt??"updated_at"})},softDelete(n){return this.withExtension("softDelete",{column:n?.column??"deleted_at"})},scope(n,e){let a=this.getExtension("scopes")??{};return this.withExtension("scopes",{...a,[n]:e})},projection(n,...e){let a=g.getPropertiesFor(this),y=e.map(h=>{if(typeof h=="string")return h;let t=h(a)[E];if(!t)throw new Error(`projection('${n}'): each accessor must return a valid PropertyDescriptor. Use \`t => t.propName\`.`);if(typeof t.propertyName!="string")throw new Error(`projection('${n}'): could not resolve property name from descriptor. Ensure the accessor returns a top-level property descriptor.`);return t.propertyName}),o=this.getExtension("projections")??{};if(Object.hasOwn(o,n))throw new Error(`projection('${n}'): a projection with this name is already registered on this schema. Each projection name must be unique.`);return this.withExtension("projections",{...o,[n]:{keys:y}})},defaultScope(n){return this.withExtension("defaultScope",n)},beforeInsert(n){let e=this.getExtension("beforeInsert")??[];return this.withExtension("beforeInsert",[...e,n])},afterInsert(n){let e=this.getExtension("afterInsert")??[];return this.withExtension("afterInsert",[...e,n])},beforeUpdate(n){let e=this.getExtension("beforeUpdate")??[];return this.withExtension("beforeUpdate",[...e,n])},beforeDelete(n){let e=this.getExtension("beforeDelete")??[];return this.withExtension("beforeDelete",[...e,n])}}});function I(n,e,a){let y={},o=[];for(let[i,t]of Object.entries(a)){let c=t.schema,m=c.introspect().properties??{};if(e in m){let l=m[e].introspect().equalsTo;if(l===void 0)throw new Error(`withVariants: variant "${i}" declares discriminator property "${e}" but it has no literal value \u2014 use string('${i}') instead of string()`);if(l!==i)throw new Error(`withVariants: variant "${i}" declares discriminator "${e}" = string('${l}') but the map key is '${i}' \u2014 they must match`)}let u;if(t.storage==="cti"){if(u=c.getExtension("tableName"),!u)throw new Error(`withVariants: CTI variant "${i}" schema must have .hasTableName() configured`);if(!t.foreignKeyColumn)throw new Error(`withVariants: CTI variant "${i}" must specify foreignKey (the FK property accessor on the variant schema)`)}y[i]={storage:t.storage,schema:c,foreignKey:t.foreignKeyColumn,tableName:u,allowOrphan:t.allowOrphan??!1,enforceCheck:t.enforceCheck??!1,relations:t.relations??[]},o.push(c)}let h={discriminatorKey:e,variants:y};return n.withExtension("variants",h).withExtension("polymorphicVariants",o)}var s=f(B,S,p,w,R),_=s.string,K=s.number,V=s.boolean,F=s.date,q=s.object,k=s.array,M=s.union,Y=s.func,U=s.any;(()=>{let n=x.prototype;typeof n.primaryKey!="function"&&(n.primaryKey=function(y){return this.withExtension("primaryKey",{autoIncrement:y?.autoIncrement??!0})});let e=b.prototype;typeof e.primaryKey!="function"&&(e.primaryKey=function(){return this.withExtension("primaryKey",{autoIncrement:!1})});let a=g.prototype;typeof a.hasPrimaryKey!="function"&&(a.hasPrimaryKey=function(y){return this.withExtension("compositePrimaryKey",y)})})();function $(n,e){let a=n.getExtension("columnName");return typeof a=="string"?a:e}function L(n){let e=n.getExtension("tableName");if(typeof e!="string")throw new Error('Schema does not have a table name. Use .hasTableName("table_name") to set one.');return e}function H(n){return n.getExtension("projections")??{}}function X(n){let e=n.getExtension("variants");return e??null}function z(n){return n.getExtension("polymorphicVariants")??[]}export{D as a,P as b,v as c,w as d,R as e,I as f,_ as g,K as h,V as i,F as j,q as k,k as l,M as m,Y as n,U as o,$ as p,L as q,H as r,X as s,z as t,Q as u,W as v};
|
|
2
|
+
//# sourceMappingURL=chunk-V4TH6K42.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/extension.ts"],"sourcesContent":["// @cleverbrush/knex-schema — Schema extension: hasColumnName / hasTableName\n\nimport type {\n AnySchemaBuilder,\n ArraySchemaBuilder,\n BooleanSchemaBuilder,\n DateSchemaBuilder,\n FunctionSchemaBuilder,\n GenericSchemaBuilder,\n NumberSchemaBuilder,\n ObjectSchemaBuilder,\n PropertyDescriptor,\n PropertyDescriptorTree,\n SchemaBuilder,\n StringSchemaBuilder,\n UnionSchemaBuilder\n} from '@cleverbrush/schema';\nimport {\n arrayExtensions,\n defineExtension,\n EXTRA_TYPE_BRAND,\n METHOD_LITERAL_BRAND,\n NumberSchemaBuilder as NumberSchemaBuilderClass,\n numberExtensions,\n ObjectSchemaBuilder as ObjectSchemaBuilderClass,\n StringSchemaBuilder as StringSchemaBuilderClass,\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR,\n stringExtensions,\n withExtensions\n} from '@cleverbrush/schema';\nimport type {\n ResolvedVariantConfig,\n ResolvedVariantRelationSpec,\n ResolvedVariantSpec,\n VariantStorageType\n} from './types.js';\n\n// Re-export these symbols so consumer packages can name them when generating\n// TypeScript declarations for schemas built with @cleverbrush/knex-schema.\n// Without these exports, tsc emits TS4023 (\"cannot be named\") errors for any\n// file that exports a variable whose inferred type traverses FixedMethods.\nexport { EXTRA_TYPE_BRAND, METHOD_LITERAL_BRAND } from '@cleverbrush/schema';\n\n// ---------------------------------------------------------------------------\n// Primary-key type brands\n// ---------------------------------------------------------------------------\n\n/**\n * Phantom-type brand placed on a column schema by `.primaryKey()`.\n * Carried on the property schema's type so that {@link PrimaryKeyOf} can\n * locate primary-key columns at the type level.\n *\n * @public\n */\nexport const PRIMARY_KEY_BRAND: unique symbol = Symbol.for(\n '@cleverbrush/knex-schema:primaryKey'\n);\n\n/**\n * Phantom-type brand placed on an object schema by `.hasPrimaryKey([cols])`\n * to record the composite primary-key column tuple at the type level.\n *\n * @public\n */\nexport const COMPOSITE_PRIMARY_KEY_BRAND: unique symbol = Symbol.for(\n '@cleverbrush/knex-schema:compositePrimaryKey'\n);\n\n// ---------------------------------------------------------------------------\n// Polymorphic type brand\n// ---------------------------------------------------------------------------\n\n/**\n * Phantom-type brand placed on an `ObjectSchemaBuilder` by `.withVariants()`.\n * The brand carries the discriminated-union result type so that\n * `query(db, polymorphicSchema)` automatically infers the correct union type\n * without any extra type annotation.\n *\n * @public\n */\nexport const POLYMORPHIC_TYPE_BRAND: unique symbol = Symbol.for(\n '@cleverbrush/knex-schema:polymorphicType'\n);\n\n// ---------------------------------------------------------------------------\n// Shared implementations\n// ---------------------------------------------------------------------------\n\n/**\n * Stores the SQL column name for a schema property using the schema extension\n * system. Consumed by {@link getColumnName} and the query builder's column\n * resolution logic.\n */\nfunction hasColumnName(this: SchemaBuilder<any, any, any>, name: string) {\n return this.withExtension('columnName', name);\n}\n\n/**\n * Stores the SQL table name for an `ObjectSchemaBuilder` using the schema\n * extension system. Required for {@link query} to build queries — throws at\n * query creation time if not set.\n */\nfunction hasTableName(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n name: string\n) {\n return this.withExtension('tableName', name);\n}\n\n// ---------------------------------------------------------------------------\n// Extension definition\n// ---------------------------------------------------------------------------\n\n/**\n * Schema extension that adds database-mapping metadata to schema builders.\n *\n * Import the typed factory functions (`string`, `number`, `object`, etc.) from\n * this package instead of from `@cleverbrush/schema` to gain access to the\n * `.hasColumnName()` and `.hasTableName()` methods.\n *\n * @example\n * ```ts\n * import { object, string, number } from '@cleverbrush/knex-schema';\n *\n * const UserSchema = object({\n * id: number(),\n * firstName: string().hasColumnName('first_name'),\n * lastName: string().hasColumnName('last_name'),\n * createdAt: date().hasColumnName('created_at'),\n * }).hasTableName('users');\n * ```\n */\nexport const dbExtension = defineExtension({\n string: {\n /**\n * Override the SQL column name for this property.\n *\n * By default the property key is used as the column name. Call\n * `.hasColumnName('sql_col')` when the database column differs from the\n * schema property name (e.g. camelCase property → snake_case column).\n *\n * @param name - The SQL column name.\n */\n hasColumnName(\n this: StringSchemaBuilder<any, any, any, any, any>,\n name: string\n ) {\n return hasColumnName.call(this, name);\n }\n },\n number: {\n /**\n * Override the SQL column name for this property.\n * @param name - The SQL column name.\n */\n hasColumnName(\n this: NumberSchemaBuilder<any, any, any, any, any>,\n name: string\n ) {\n return hasColumnName.call(this, name);\n }\n },\n boolean: {\n /**\n * Override the SQL column name for this property.\n * @param name - The SQL column name.\n */\n hasColumnName(\n this: BooleanSchemaBuilder<any, any, any, any, any, any, any>,\n name: string\n ) {\n return hasColumnName.call(this, name);\n }\n },\n date: {\n /**\n * Override the SQL column name for this property.\n * @param name - The SQL column name.\n */\n hasColumnName(\n this: DateSchemaBuilder<any, any, any, any, any>,\n name: string\n ) {\n return hasColumnName.call(this, name);\n }\n },\n any: {\n /**\n * Override the SQL column name for this property.\n * @param name - The SQL column name.\n */\n hasColumnName(\n this: AnySchemaBuilder<any, any, any, any, any, any>,\n name: string\n ) {\n return hasColumnName.call(this, name);\n }\n },\n func: {\n /**\n * Override the SQL column name for this property.\n * @param name - The SQL column name.\n */\n hasColumnName(\n this: FunctionSchemaBuilder<any, any, any, any, any>,\n name: string\n ) {\n return hasColumnName.call(this, name);\n }\n },\n array: {\n /**\n * Override the SQL column name for this property.\n * @param name - The SQL column name.\n */\n hasColumnName(\n this: ArraySchemaBuilder<any, any, any, any, any, any, any>,\n name: string\n ) {\n return hasColumnName.call(this, name);\n }\n },\n union: {\n /**\n * Override the SQL column name for this property.\n * @param name - The SQL column name.\n */\n hasColumnName(\n this: UnionSchemaBuilder<any, any, any, any, any, any>,\n name: string\n ) {\n return hasColumnName.call(this, name);\n }\n },\n generic: {\n /**\n * Override the SQL column name for this property.\n * @param name - The SQL column name.\n */\n hasColumnName(\n this: GenericSchemaBuilder<any, any, any, any, any, any>,\n name: string\n ) {\n return hasColumnName.call(this, name);\n }\n },\n object: {\n /**\n * Set the SQL table name for this object schema.\n *\n * Required before creating a {@link query} builder — throws at\n * query creation time when not set.\n *\n * @param name - The SQL table name (e.g. `'users'`).\n */\n hasTableName(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n name: string\n ) {\n return hasTableName.call(this, name);\n }\n }\n});\n\n// ---------------------------------------------------------------------------\n// DDL + ORM extension\n// ---------------------------------------------------------------------------\n\ntype FKAction = 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION';\n\n/**\n * DDL and ORM extension that adds database DDL metadata, relationship\n * definitions, and ORM conveniences to schema builders.\n *\n * Column-level methods: `.primaryKey()`, `.references()`, `.unique()`,\n * `.index()`, `.defaultTo()`, `.columnType()`, `.check()`, `.defaultToRaw()`.\n *\n * Object-level methods: `.hasIndex()`, `.hasUnique()`, `.hasCheck()`,\n * `.hasPrimaryKey()`, `.hasRawColumn()`, `.hasRawIndex()`, `.hasMany()`,\n * `.hasOne()`, `.belongsTo()`, `.belongsToMany()`, `.hasTimestamps()`,\n * `.softDelete()`, `.scope()`, `.defaultScope()`, `.beforeInsert()`,\n * `.afterInsert()`, `.beforeUpdate()`, `.beforeDelete()`.\n */\nexport const ddlExtension = defineExtension({\n number: {\n /** Add a foreign key reference to another table.\n * @param table - The referenced table name.\n * @param column - The referenced column (defaults to `'id'`).\n */\n references(\n this: NumberSchemaBuilder<any, any, any, any, any>,\n table: string,\n column: string = 'id'\n ) {\n return this.withExtension('references', { table, column });\n },\n /** Set the ON DELETE action for a foreign key. */\n onDelete(\n this: NumberSchemaBuilder<any, any, any, any, any>,\n action: FKAction\n ) {\n return this.withExtension('onDelete', action);\n },\n /** Set the ON UPDATE action for a foreign key. */\n onUpdate(\n this: NumberSchemaBuilder<any, any, any, any, any>,\n action: FKAction\n ) {\n return this.withExtension('onUpdate', action);\n },\n /** Set a default value for this column. */\n defaultTo(\n this: NumberSchemaBuilder<any, any, any, any, any>,\n value: number | 'auto_increment'\n ) {\n return this.withExtension('defaultTo', value);\n },\n /** Add an index on this column.\n * @param name - Optional index name.\n */\n index(\n this: NumberSchemaBuilder<any, any, any, any, any>,\n name?: string\n ) {\n return this.withExtension('index', name ?? true);\n },\n /** Add a unique constraint on this column.\n * @param name - Optional constraint name.\n */\n unique(\n this: NumberSchemaBuilder<any, any, any, any, any>,\n name?: string\n ) {\n return this.withExtension('unique', name ?? true);\n },\n /** Override the SQL column type (e.g. `'bigint'`, `'smallint'`). */\n columnType(\n this: NumberSchemaBuilder<any, any, any, any, any>,\n type: string\n ) {\n return this.withExtension('columnType', type);\n },\n /** Shorthand for `.columnType('bigint')`. */\n bigint(this: NumberSchemaBuilder<any, any, any, any, any>) {\n return this.withExtension('columnType', 'bigint');\n },\n /** Shorthand for `.columnType('smallint')`. */\n smallint(this: NumberSchemaBuilder<any, any, any, any, any>) {\n return this.withExtension('columnType', 'smallint');\n },\n /** Shorthand for `.columnType('decimal(p,s)')` — exact numeric.\n * @param precision - Total digits.\n * @param scale - Digits after decimal point.\n */\n decimal(\n this: NumberSchemaBuilder<any, any, any, any, any>,\n precision: number,\n scale: number\n ) {\n return this.withExtension(\n 'columnType',\n `decimal(${precision},${scale})`\n );\n },\n /** Set a raw SQL default expression.\n * @param expression - Raw SQL expression (e.g. `\"nextval('my_seq')\"`).\n */\n defaultToRaw(\n this: NumberSchemaBuilder<any, any, any, any, any>,\n expression: string\n ) {\n return this.withExtension('defaultTo', { raw: expression });\n },\n /**\n * Mark this integer column as an optimistic-concurrency row-version\n * token checked by the ORM on every UPDATE / DELETE.\n *\n * A mismatch between the stored value and the snapshot taken at read\n * time throws `ConcurrencyError`.\n *\n * @param opts.strategy\n * - `'increment'` (default) — ORM adds 1 on each UPDATE.\n * - `'manual'` — caller supplies the new value.\n */\n rowVersion(\n this: NumberSchemaBuilder<any, any, any, any, any>,\n opts?: { strategy?: 'increment' | 'manual' }\n ) {\n return this.withExtension('rowVersion', {\n strategy: opts?.strategy ?? 'increment'\n });\n }\n },\n string: {\n /** Override the SQL column type (e.g. `'text'`, `'uuid'`, `'jsonb'`, `'citext'`). */\n columnType(\n this: StringSchemaBuilder<any, any, any, any, any>,\n type: string\n ) {\n return this.withExtension('columnType', type);\n },\n /** Shorthand for `.columnType('text')` — unlimited-length text. */\n text(this: StringSchemaBuilder<any, any, any, any, any>) {\n return this.withExtension('columnType', 'text');\n },\n /** Shorthand for `.columnType('uuid')` — UUID column type.\n * Note: this sets the *storage type* — for UUID format validation use\n * the schema-level `.uuid()` validator instead.\n */\n asUuid(this: StringSchemaBuilder<any, any, any, any, any>) {\n return this.withExtension('columnType', 'uuid');\n },\n /** Shorthand for `.columnType('citext')` — case-insensitive text (Postgres). */\n citext(this: StringSchemaBuilder<any, any, any, any, any>) {\n return this.withExtension('columnType', 'citext');\n },\n /** Shorthand for `.columnType('jsonb')` — binary JSON (Postgres). */\n jsonb(this: StringSchemaBuilder<any, any, any, any, any>) {\n return this.withExtension('columnType', 'jsonb');\n },\n /** Shorthand for `.columnType('tsvector')` — full-text search vector (Postgres). */\n tsvector(this: StringSchemaBuilder<any, any, any, any, any>) {\n return this.withExtension('columnType', 'tsvector');\n },\n /** Add a foreign key reference to another table. */\n references(\n this: StringSchemaBuilder<any, any, any, any, any>,\n table: string,\n column: string = 'id'\n ) {\n return this.withExtension('references', { table, column });\n },\n /** Set the ON DELETE action for a foreign key. */\n onDelete(\n this: StringSchemaBuilder<any, any, any, any, any>,\n action: FKAction\n ) {\n return this.withExtension('onDelete', action);\n },\n /** Set the ON UPDATE action for a foreign key. */\n onUpdate(\n this: StringSchemaBuilder<any, any, any, any, any>,\n action: FKAction\n ) {\n return this.withExtension('onUpdate', action);\n },\n /** Add a unique constraint on this column. */\n unique(\n this: StringSchemaBuilder<any, any, any, any, any>,\n name?: string\n ) {\n return this.withExtension('unique', name ?? true);\n },\n /** Add an index on this column. */\n index(\n this: StringSchemaBuilder<any, any, any, any, any>,\n name?: string\n ) {\n return this.withExtension('index', name ?? true);\n },\n /** Set a default value for this column. */\n defaultTo(\n this: StringSchemaBuilder<any, any, any, any, any>,\n value: string\n ) {\n return this.withExtension('defaultTo', value);\n },\n /** Add a CHECK constraint with raw SQL.\n * @param sql - SQL expression for the check (e.g. `\"role IN ('user','admin')\"`).\n */\n check(this: StringSchemaBuilder<any, any, any, any, any>, sql: string) {\n return this.withExtension('check', sql);\n },\n /** Set a raw SQL default expression. */\n defaultToRaw(\n this: StringSchemaBuilder<any, any, any, any, any>,\n expression: string\n ) {\n return this.withExtension('defaultTo', { raw: expression });\n },\n /**\n * Mark this string column as an optimistic-concurrency row-version\n * token (e.g. a UUID or opaque hash). Strategy is always `'manual'`\n * — the caller must supply a new value on each UPDATE.\n */\n rowVersion(this: StringSchemaBuilder<any, any, any, any, any>) {\n return this.withExtension('rowVersion', { strategy: 'manual' });\n }\n },\n boolean: {\n /** Set a default value for this column. */\n defaultTo(\n this: BooleanSchemaBuilder<any, any, any, any, any, any, any>,\n value: boolean\n ) {\n return this.withExtension('defaultTo', value);\n },\n /** Override the SQL column type (e.g. `'smallint'`, `'integer'`). */\n columnType(\n this: BooleanSchemaBuilder<any, any, any, any, any, any, any>,\n type: string\n ) {\n return this.withExtension('columnType', type);\n },\n /** Add an index on this column. */\n index(\n this: BooleanSchemaBuilder<any, any, any, any, any, any, any>,\n name?: string\n ) {\n return this.withExtension('index', name ?? true);\n },\n /** Add a unique constraint on this column. */\n unique(\n this: BooleanSchemaBuilder<any, any, any, any, any, any, any>,\n name?: string\n ) {\n return this.withExtension('unique', name ?? true);\n }\n },\n date: {\n /** Set a default value (`'now'` for current timestamp). */\n defaultTo(\n this: DateSchemaBuilder<any, any, any, any, any>,\n value: 'now'\n ) {\n return this.withExtension('defaultTo', value);\n },\n /** Override the SQL column type\n * (e.g. `'timestamptz'`, `'date'`, `'time'`).\n */\n columnType(\n this: DateSchemaBuilder<any, any, any, any, any>,\n type: string\n ) {\n return this.withExtension('columnType', type);\n },\n /** Add an index on this column. */\n index(this: DateSchemaBuilder<any, any, any, any, any>, name?: string) {\n return this.withExtension('index', name ?? true);\n },\n /** Set a raw SQL default expression. */\n defaultToRaw(\n this: DateSchemaBuilder<any, any, any, any, any>,\n expression: string\n ) {\n return this.withExtension('defaultTo', { raw: expression });\n },\n /** Shorthand for `.columnType('timestamptz')`. */\n timestamptz(this: DateSchemaBuilder<any, any, any, any, any>) {\n return this.withExtension('columnType', 'timestamptz');\n },\n /** Shorthand for `.columnType('date')` (date-only, no time). */\n dateOnly(this: DateSchemaBuilder<any, any, any, any, any>) {\n return this.withExtension('columnType', 'date');\n },\n /**\n * Mark this date column as an optimistic-concurrency row-version\n * token (timestamp-based). Strategy is always `'timestamp'` —\n * the ORM sets the value to `new Date()` on each UPDATE.\n */\n rowVersion(this: DateSchemaBuilder<any, any, any, any, any>) {\n return this.withExtension('rowVersion', { strategy: 'timestamp' });\n }\n },\n object: {\n /** Override the SQL column type for an object property stored inline.\n * Object-typed properties default to `jsonb` when used as columns in\n * a parent schema's table.\n */\n columnType(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n type: string\n ) {\n return this.withExtension('columnType', type);\n },\n /** Shorthand for `.columnType('jsonb')` — store this nested object as\n * a `jsonb` column (Postgres). Nested objects already default to\n * `jsonb` in DDL; calling `.jsonb()` makes the intent explicit.\n */\n jsonb(this: ObjectSchemaBuilder<any, any, any, any, any, any, any>) {\n return this.withExtension('columnType', 'jsonb');\n },\n /** Shorthand for `.columnType('json')` — store this nested object as\n * a plain `json` column (Postgres / MySQL).\n */\n json(this: ObjectSchemaBuilder<any, any, any, any, any, any, any>) {\n return this.withExtension('columnType', 'json');\n },\n /** Add a composite index on multiple columns.\n * @param columns - Column names to index.\n * @param opts - Optional index name and unique flag.\n */\n hasIndex(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n columns: string[],\n opts?: { name?: string; unique?: boolean }\n ) {\n const existing = (this.getExtension('indexes') as any[]) ?? [];\n return this.withExtension('indexes', [\n ...existing,\n { columns, ...opts }\n ]);\n },\n /** Add a composite unique constraint.\n * @param columns - Column names.\n * @param name - Optional constraint name.\n */\n hasUnique(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n columns: string[],\n name?: string\n ) {\n const existing = (this.getExtension('uniques') as any[]) ?? [];\n return this.withExtension('uniques', [\n ...existing,\n { columns, name }\n ]);\n },\n /** Add a table-level CHECK constraint.\n * @param sql - Raw SQL expression for the check.\n */\n hasCheck(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n sql: string\n ) {\n const existing = (this.getExtension('checks') as any[]) ?? [];\n return this.withExtension('checks', [...existing, sql]);\n },\n /** Add a raw SQL column definition not backed by a schema property.\n * @param name - Column name.\n * @param definition - SQL type and constraints (e.g. `\"tsvector GENERATED ALWAYS AS (...) STORED\"`).\n */\n hasRawColumn(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n name: string,\n definition: string\n ) {\n const existing = (this.getExtension('rawColumns') as any[]) ?? [];\n return this.withExtension('rawColumns', [\n ...existing,\n { name, definition }\n ]);\n },\n /** Add a raw SQL index statement executed after table creation.\n * @param sql - Full `CREATE INDEX` SQL statement.\n */\n hasRawIndex(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n sql: string\n ) {\n const existing = (this.getExtension('rawIndexes') as any[]) ?? [];\n return this.withExtension('rawIndexes', [...existing, sql]);\n },\n /** Define a one-to-many relationship.\n * @param name - Relation name used with `include()`.\n * @param opts - `{ schema, foreignKey }` — `foreignKey` is a ColumnRef on the foreign schema.\n */\n hasMany(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n name: string,\n opts: { schema: any; foreignKey: any }\n ) {\n const existing = (this.getExtension('relations') as any[]) ?? [];\n return this.withExtension('relations', [\n ...existing,\n { type: 'hasMany', name, ...opts }\n ]);\n },\n /** Define a one-to-one relationship (FK on foreign table).\n * @param name - Relation name used with `include()`.\n * @param opts - `{ schema, foreignKey }` — `foreignKey` is a ColumnRef on the foreign schema.\n */\n hasOne(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n name: string,\n opts: { schema: any; foreignKey: any }\n ) {\n const existing = (this.getExtension('relations') as any[]) ?? [];\n return this.withExtension('relations', [\n ...existing,\n { type: 'hasOne', name, ...opts }\n ]);\n },\n /** Define a belongs-to relationship (FK on local table).\n * @param name - Relation name used with `include()`.\n * @param opts - `{ schema, foreignKey }` — `foreignKey` is a ColumnRef on the local schema.\n */\n belongsTo(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n name: string,\n opts: { schema: any; foreignKey: any }\n ) {\n const existing = (this.getExtension('relations') as any[]) ?? [];\n return this.withExtension('relations', [\n ...existing,\n { type: 'belongsTo', name, ...opts }\n ]);\n },\n /** Define a many-to-many relationship through a pivot table.\n * @param name - Relation name used with `include()`.\n * @param opts - `{ schema, through: { table, localKey, foreignKey } }`.\n */\n belongsToMany(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n name: string,\n opts: {\n schema: any;\n through: {\n table: string;\n localKey: string;\n foreignKey: string;\n };\n }\n ) {\n const existing = (this.getExtension('relations') as any[]) ?? [];\n return this.withExtension('relations', [\n ...existing,\n { type: 'belongsToMany', name, ...opts }\n ]);\n },\n /** Auto-add `created_at` and `updated_at` timestamp columns.\n * @param opts - Optional custom column names.\n */\n hasTimestamps(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n opts?: { createdAt?: string; updatedAt?: string }\n ) {\n return this.withExtension('timestamps', {\n createdAt: opts?.createdAt ?? 'created_at',\n updatedAt: opts?.updatedAt ?? 'updated_at'\n });\n },\n /** Enable soft deletes (adds a `deleted_at` column, auto-filters queries).\n * @param opts - Optional custom column name.\n */\n softDelete(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n opts?: { column?: string }\n ) {\n return this.withExtension('softDelete', {\n column: opts?.column ?? 'deleted_at'\n });\n },\n /** Register a named query scope.\n * @param name - Scope name to use with `.scoped(name)`.\n * @param fn - Function that receives a `SchemaQueryBuilder` and applies filters.\n */\n scope<N extends string>(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n name: N,\n fn: Function\n ): typeof this & { readonly [METHOD_LITERAL_BRAND]?: N } {\n const existing =\n (this.getExtension('scopes') as Record<string, Function>) ?? {};\n return this.withExtension('scopes', {\n ...existing,\n [name]: fn\n }) as typeof this & { readonly [METHOD_LITERAL_BRAND]?: N };\n },\n /**\n * Register a **named projection** — a reusable column subset that can be\n * applied at query time via `.projected(name)`.\n *\n * When a projection is applied the query builder:\n * 1. Issues `SELECT <cols>` instead of `SELECT *`.\n * 2. Narrows the TypeScript result row type to `Pick<Row, Keys>`.\n *\n * Columns are passed as **rest parameters**. Each argument can be either:\n *\n * - A **string** literal of a property name (autocompleted against the\n * schema's properties):\n * ```ts\n * .projection('summary', 'id', 'title', 'completed')\n * ```\n * - An **accessor callback** (refactor-safe — renaming a property\n * updates the projection automatically):\n * ```ts\n * .projection('listView', t => t.id, t => t.title, t => t.userId)\n * ```\n *\n * The two forms can be mixed freely:\n * ```ts\n * .projection('mixed', 'id', t => t.title)\n * ```\n *\n * The literal property keys flow through the type system, so\n * `.projected('listView')` still narrows the result type to\n * `Pick<Row, 'id' | 'title' | 'userId'>`.\n *\n * @param name - Unique projection name (used with `.projected()`).\n * @param columns - One argument per column: either a property name\n * string or a `t => t.propName` accessor callback.\n *\n * @example\n * ```ts\n * const PostSchema = object({ id: number(), title: string(), body: string() })\n * .hasTableName('posts')\n * .projection('summary', 'id', 'title')\n * .projection('detail', t => t.id, t => t.title, t => t.body);\n *\n * // Later:\n * const rows = await query(db, PostSchema).projected('summary');\n * // rows: Array<Pick<Post, 'id' | 'title'>>\n * ```\n *\n * @see {@link SchemaQueryBuilder.projected}\n */\n projection<\n TProperties extends Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n >,\n const N extends string,\n const TKey extends keyof TProperties & string\n >(\n this: ObjectSchemaBuilder<\n TProperties,\n any,\n any,\n any,\n any,\n any,\n any\n >,\n name: N,\n ...columns: ReadonlyArray<\n | TKey\n | ((\n t: PropertyDescriptorTree<\n ObjectSchemaBuilder<\n TProperties,\n any,\n any,\n any,\n any,\n any,\n any\n >,\n ObjectSchemaBuilder<\n TProperties,\n any,\n any,\n any,\n any,\n any,\n any\n >\n >\n ) => PropertyDescriptor<any, any, any, TKey>)\n >\n ): typeof this & {\n readonly [EXTRA_TYPE_BRAND]?: { [P in N]: readonly TKey[] };\n } {\n const tree = ObjectSchemaBuilderClass.getPropertiesFor(this as any);\n const keys: string[] = (\n columns as ReadonlyArray<\n | string\n | ((t: any) => PropertyDescriptor<any, any, any, any>)\n >\n ).map(col => {\n if (typeof col === 'string') {\n return col;\n }\n const descriptor = col(tree);\n const inner = (descriptor as any)[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ];\n if (!inner) {\n throw new Error(\n `projection('${name}'): each accessor must return a valid PropertyDescriptor. ` +\n `Use \\`t => t.propName\\`.`\n );\n }\n if (typeof inner.propertyName !== 'string') {\n throw new Error(\n `projection('${name}'): could not resolve property name from descriptor. ` +\n `Ensure the accessor returns a top-level property descriptor.`\n );\n }\n return inner.propertyName as string;\n });\n const existing =\n (this.getExtension('projections') as Record<\n string,\n { keys: readonly string[] }\n >) ?? {};\n if (Object.hasOwn(existing, name)) {\n throw new Error(\n `projection('${name}'): a projection with this name is already registered on this schema. ` +\n `Each projection name must be unique.`\n );\n }\n return this.withExtension('projections', {\n ...existing,\n [name]: { keys }\n }) as typeof this & {\n readonly [EXTRA_TYPE_BRAND]?: { [P in N]: readonly TKey[] };\n };\n },\n /** Set a default scope applied to all queries unless `.unscoped()` is called.\n * @param fn - Function that receives a `SchemaQueryBuilder` and applies filters.\n */\n defaultScope(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n fn: Function\n ) {\n return this.withExtension('defaultScope', fn);\n },\n /** Register a before-insert lifecycle hook.\n * @param fn - Async function `(data) => data` called before inserting.\n */\n beforeInsert(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n fn: Function\n ) {\n const existing =\n (this.getExtension('beforeInsert') as Function[]) ?? [];\n return this.withExtension('beforeInsert', [...existing, fn]);\n },\n /** Register an after-insert lifecycle hook.\n * @param fn - Async function `(row)` called after inserting.\n */\n afterInsert(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n fn: Function\n ) {\n const existing =\n (this.getExtension('afterInsert') as Function[]) ?? [];\n return this.withExtension('afterInsert', [...existing, fn]);\n },\n /** Register a before-update lifecycle hook.\n * @param fn - Async function `(data) => data` called before updating.\n */\n beforeUpdate(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n fn: Function\n ) {\n const existing =\n (this.getExtension('beforeUpdate') as Function[]) ?? [];\n return this.withExtension('beforeUpdate', [...existing, fn]);\n },\n /** Register a before-delete lifecycle hook.\n * @param fn - Async function `(query)` called before deleting.\n */\n beforeDelete(\n this: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n fn: Function\n ) {\n const existing =\n (this.getExtension('beforeDelete') as Function[]) ?? [];\n return this.withExtension('beforeDelete', [...existing, fn]);\n }\n\n /**\n * Declare polymorphic variants for this schema.\n *\n * Turns a base schema into a **polymorphic schema** where a discriminator\n * column determines which variant each row belongs to. Variants can store\n * their extra fields either in a separate table (CTI — Class Table\n * Inheritance) or as nullable columns on the base table (STI — Single\n * Table Inheritance).\n *\n * The return type carries a phantom brand\n * (`[POLYMORPHIC_TYPE_BRAND]`) so that `query(db, schema)` automatically\n * infers the full discriminated-union result type.\n *\n * @param config.discriminator - Property key (or accessor) of the\n * discriminator column on the base table (e.g. `'type'` or `t => t.type`).\n * @param config.variants - Map from discriminator value to\n * `{ schema, storage, foreignKey?, allowOrphan?, enforceCheck? }`.\n * - `storage: 'cti'` — variant fields are in a separate table;\n * `foreignKey` (the FK column on the variant table) is required.\n * - `storage: 'sti'` — variant fields are nullable columns on the base table.\n *\n * @example\n * ```ts\n * const FileBase = object({ id: number().primaryKey(), name: string(), type: string() })\n * .hasTableName('files');\n *\n * const ImageExtras = object({ width: number(), height: number(), format: string() })\n * .hasTableName('image_file');\n *\n * const DocumentExtras = object({ size: number(), issueDate: date() })\n * .hasTableName('document_file');\n *\n * const ImageExtras = object({\n * fileId: number().hasColumnName('file_id'),\n * type: string('image'),\n * width: number(), height: number(), format: string()\n * }).hasTableName('image_file');\n *\n * const DocumentExtras = object({\n * fileId: number().hasColumnName('file_id'),\n * type: string('document'),\n * size: number(), issueDate: date()\n * }).hasTableName('document_file');\n *\n * const FileSchema = FileBase.withVariants({\n * discriminator: t => t.type,\n * variants: {\n * image: { schema: ImageExtras, storage: 'cti', foreignKey: t => t.fileId },\n * document: { schema: DocumentExtras, storage: 'cti', foreignKey: t => t.fileId },\n * },\n * });\n *\n * // query(db, FileSchema) returns:\n * // Array<\n * // | { id: number; name: string; type: 'image'; width: number; height: number; format: string }\n * // | { id: number; name: string; type: 'document'; size: number; issueDate: Date }\n * // >\n * ```\n */\n // NOTE: the public `.withVariants()` schema-level method has been\n // removed. Variants are now declared on the {@link Entity} chain via\n // `defineEntity(...).discriminator(...).ctiVariant(...).stiVariant(...)`.\n // The internal worker {@link applyVariantsToSchema} (below this\n // `defineExtension` block) is invoked by the Entity layer and stores\n // the same `'variants'` / `'polymorphicVariants'` extensions that\n // `SchemaQueryBuilder` reads at runtime.\n }\n});\n\n// ---------------------------------------------------------------------------\n// Variant resolution worker (used by the Entity layer)\n// ---------------------------------------------------------------------------\n\n/**\n * @internal Input to {@link applyVariantsToSchema}: a single resolved variant\n * entry, as produced by `Entity.ctiVariant()` / `Entity.stiVariant()`.\n */\nexport interface VariantInputForResolver {\n /** CTI-only: FK column name on the variant table that joins back to base PK. */\n foreignKeyColumn?: string;\n /** Variant body schema (CTI table or STI extras). */\n schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>;\n storage: VariantStorageType;\n allowOrphan?: boolean;\n enforceCheck?: boolean;\n /**\n * Variant-scoped relations (already resolved to FK column names). When\n * the variant entry is sourced from another `Entity`, these are read\n * from that entity's `'relations'` extension and passed in here.\n */\n relations?: ResolvedVariantRelationSpec[];\n}\n\n/**\n * @internal Validate + apply a fully-resolved variant config to a base\n * schema. Stores the `'variants'` and `'polymorphicVariants'` extensions\n * read by {@link SchemaQueryBuilder}.\n *\n * Called by the {@link Entity} chain (`.discriminator().ctiVariant().stiVariant()`).\n * Replaces the previous schema-level `.withVariants()` method.\n */\nexport function applyVariantsToSchema(\n baseSchema: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n discriminatorKey: string,\n variants: Record<string, VariantInputForResolver>\n): ObjectSchemaBuilder<any, any, any, any, any, any, any> {\n const resolvedVariants: Record<string, ResolvedVariantSpec> = {};\n const variantSchemas: ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >[] = [];\n\n for (const [key, spec] of Object.entries(variants)) {\n const varSchema = spec.schema;\n\n // Validate: if the variant schema declares the discriminator\n // property, its equalsTo literal must match the map key.\n const varIntrospected = varSchema.introspect() as any;\n const varProps: Record<string, any> = varIntrospected.properties ?? {};\n if (discriminatorKey in varProps) {\n const discPropIntrospected = varProps[\n discriminatorKey\n ].introspect() as any;\n const equalsTo: string | undefined = discPropIntrospected.equalsTo;\n if (equalsTo === undefined) {\n throw new Error(\n `withVariants: variant \"${key}\" declares discriminator property \"${discriminatorKey}\" ` +\n `but it has no literal value — use string('${key}') instead of string()`\n );\n }\n if (equalsTo !== key) {\n throw new Error(\n `withVariants: variant \"${key}\" declares discriminator \"${discriminatorKey}\" = ` +\n `string('${equalsTo}') but the map key is '${key}' — they must match`\n );\n }\n }\n\n let tableName: string | undefined;\n if (spec.storage === 'cti') {\n tableName = varSchema.getExtension('tableName') as\n | string\n | undefined;\n if (!tableName) {\n throw new Error(\n `withVariants: CTI variant \"${key}\" schema must have .hasTableName() configured`\n );\n }\n if (!spec.foreignKeyColumn) {\n throw new Error(\n `withVariants: CTI variant \"${key}\" must specify foreignKey (the FK property accessor on the variant schema)`\n );\n }\n }\n\n resolvedVariants[key] = {\n storage: spec.storage,\n schema: varSchema,\n foreignKey: spec.foreignKeyColumn,\n tableName,\n allowOrphan: spec.allowOrphan ?? false,\n enforceCheck: spec.enforceCheck ?? false,\n relations: spec.relations ?? []\n };\n\n variantSchemas.push(varSchema);\n }\n\n const variantConfig: Omit<ResolvedVariantConfig, 'discriminatorColumn'> = {\n discriminatorKey,\n variants: resolvedVariants\n };\n\n return (baseSchema as any)\n .withExtension('variants', variantConfig)\n .withExtension('polymorphicVariants', variantSchemas);\n}\n\n// ---------------------------------------------------------------------------\n// Extended factory functions\n// ---------------------------------------------------------------------------\n\nconst extended = withExtensions(\n stringExtensions,\n numberExtensions,\n arrayExtensions,\n dbExtension,\n ddlExtension\n);\n\nexport const string = extended.string;\nexport const number = extended.number;\nexport const boolean = extended.boolean;\nexport const date = extended.date;\nexport const object = extended.object;\nexport const array = extended.array;\nexport const union = extended.union;\nexport const func = extended.func;\nexport const any = extended.any;\n\n// ---------------------------------------------------------------------------\n// Primary-key methods (declared & patched out-of-band)\n// ---------------------------------------------------------------------------\n//\n// `.primaryKey()` and `.hasPrimaryKey()` are declared here via TypeScript\n// declaration merging on the base builder classes (NumberSchemaBuilder,\n// StringSchemaBuilder, ObjectSchemaBuilder) instead of via `defineExtension`.\n//\n// Why: `defineExtension` methods are rewritten by the schema library's\n// `FixedMethods<>` mapped type, which replaces the declared return type of\n// every extension method with `TBase & FixedMethods<...>`. That rewriting\n// silently discards any phantom-brand intersections (such as the\n// `[PRIMARY_KEY_BRAND]` carried on the return type), so downstream type-level\n// helpers like `PrimaryKeyOf` / `PrimaryKeyValueOf` resolve to `never`.\n//\n// By declaring these methods directly on the underlying classes via module\n// augmentation, the brand intersection survives — `this & { [PRIMARY_KEY_BRAND]?: true }`\n// stays attached to the builder type stored in `TProperties[K]`, so\n// `find(id)` infers the correct primary-key value type.\n// ---------------------------------------------------------------------------\n\ndeclare module '@cleverbrush/schema' {\n interface NumberSchemaBuilder<\n TResult,\n TRequired extends boolean,\n TNullable extends boolean,\n THasDefault extends boolean,\n TExtensions\n > {\n /** Mark this column as a primary key.\n * @param opts - Options. `autoIncrement` defaults to `true`.\n */\n primaryKey(opts?: { autoIncrement?: boolean }): this & {\n readonly [PRIMARY_KEY_BRAND]?: true;\n };\n }\n\n interface StringSchemaBuilder<\n TResult,\n TRequired extends boolean,\n TNullable extends boolean,\n THasDefault extends boolean,\n TExtensions\n > {\n /** Mark this column as a primary key (non-auto-increment). */\n primaryKey(): this & {\n readonly [PRIMARY_KEY_BRAND]?: true;\n };\n }\n\n interface ObjectSchemaBuilder<\n TProperties extends Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n >,\n TRequired extends boolean,\n TNullable extends boolean,\n TExplicitType,\n THasDefault extends boolean,\n TExtensions,\n TConstructorSchemas\n > {\n /** Set a composite primary key.\n * @param columns - Column names (or property keys) forming the primary key,\n * in declaration order. Use `as const` to preserve ordering at the type level.\n */\n hasPrimaryKey<const TCols extends readonly string[]>(\n columns: TCols\n ): this & {\n readonly [COMPOSITE_PRIMARY_KEY_BRAND]?: TCols;\n };\n }\n}\n\n// Runtime patches: install the methods on the prototypes.\n// Idempotent — safe even if the module is loaded multiple times.\n(() => {\n const numProto = NumberSchemaBuilderClass.prototype as any;\n if (typeof numProto.primaryKey !== 'function') {\n numProto.primaryKey = function (opts?: { autoIncrement?: boolean }) {\n return this.withExtension('primaryKey', {\n autoIncrement: opts?.autoIncrement ?? true\n });\n };\n }\n\n const strProto = StringSchemaBuilderClass.prototype as any;\n if (typeof strProto.primaryKey !== 'function') {\n strProto.primaryKey = function () {\n return this.withExtension('primaryKey', { autoIncrement: false });\n };\n }\n\n const objProto = ObjectSchemaBuilderClass.prototype as any;\n if (typeof objProto.hasPrimaryKey !== 'function') {\n objProto.hasPrimaryKey = function (columns: readonly string[]) {\n return this.withExtension(\n 'compositePrimaryKey',\n columns as readonly string[]\n );\n };\n }\n})();\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Get the SQL column name for a schema property.\n * Returns the `hasColumnName()` value if set, otherwise falls back to `propertyKey`.\n */\nexport function getColumnName(\n schema: SchemaBuilder<any, any, any>,\n propertyKey: string\n): string {\n const col = schema.getExtension('columnName');\n return typeof col === 'string' ? col : propertyKey;\n}\n\n/**\n * Get the SQL table name from an ObjectSchemaBuilder.\n * Throws if `hasTableName()` was never called.\n */\nexport function getTableName(\n schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>\n): string {\n const table = schema.getExtension('tableName');\n if (typeof table !== 'string') {\n throw new Error(\n 'Schema does not have a table name. Use .hasTableName(\"table_name\") to set one.'\n );\n }\n return table;\n}\n\n/**\n * Retrieve the named projections registered on a schema via\n * `.projection(name, columns)`.\n *\n * Returns a map of `{ [name]: { keys: readonly string[] } }` where each\n * entry's `keys` array contains the **property keys** (not SQL column names)\n * for that projection. Returns an empty object when no projections are\n * defined.\n *\n * @example\n * ```ts\n * const projs = getProjections(PostSchema);\n * // { summary: { keys: ['id', 'title'] } }\n * ```\n */\nexport function getProjections(\n schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>\n): Record<string, { keys: readonly string[] }> {\n return (\n (schema.getExtension('projections') as Record<\n string,\n { keys: readonly string[] }\n >) ?? {}\n );\n}\n\n/**\n * Retrieve the resolved variant configuration stored by `.withVariants()`.\n * Returns `null` when the schema is not polymorphic.\n *\n * @internal — used by {@link SchemaQueryBuilder}.\n */\nexport function getVariants(\n schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>\n): Omit<ResolvedVariantConfig, 'discriminatorColumn'> | null {\n const cfg = schema.getExtension('variants');\n return cfg != null\n ? (cfg as Omit<ResolvedVariantConfig, 'discriminatorColumn'>)\n : null;\n}\n\n/**\n * Retrieve the array of variant `ObjectSchemaBuilder` instances stored by\n * `.withVariants()`. Used by migration / DDL tools to discover variant\n * tables without walking the full schema tree.\n *\n * @internal\n */\nexport function getPolymorphicVariantSchemas(\n schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>\n): ObjectSchemaBuilder<any, any, any, any, any, any, any>[] {\n return (\n (schema.getExtension('polymorphicVariants') as ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >[]) ?? []\n );\n}\n"],"mappings":"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","names":["arrayExtensions","defineExtension","NumberSchemaBuilderClass","numberExtensions","ObjectSchemaBuilderClass","StringSchemaBuilderClass","SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR","stringExtensions","withExtensions","EXTRA_TYPE_BRAND","METHOD_LITERAL_BRAND","PRIMARY_KEY_BRAND","COMPOSITE_PRIMARY_KEY_BRAND","POLYMORPHIC_TYPE_BRAND","hasColumnName","name","hasTableName","dbExtension","ddlExtension","table","column","action","value","type","precision","scale","expression","opts","sql","columns","existing","definition","fn","tree","keys","col","inner","applyVariantsToSchema","baseSchema","discriminatorKey","variants","resolvedVariants","variantSchemas","key","spec","varSchema","varProps","equalsTo","tableName","variantConfig","extended","string","number","boolean","date","object","array","union","func","any","numProto","strProto","objProto","getColumnName","schema","propertyKey","getTableName","getProjections","getVariants","cfg","getPolymorphicVariantSchemas"]}
|
package/dist/columns.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ObjectSchemaBuilder } from '@cleverbrush/schema';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
2
3
|
import type { ColumnRef } from './types.js';
|
|
3
4
|
interface ColumnMapResult {
|
|
4
5
|
propToCol: Map<string, string>;
|
|
@@ -11,17 +12,85 @@ interface ColumnMapResult {
|
|
|
11
12
|
*/
|
|
12
13
|
export declare function buildColumnMap(schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>): ColumnMapResult;
|
|
13
14
|
/**
|
|
14
|
-
* Resolve a ColumnRef to a plain SQL column name.
|
|
15
|
+
* Resolve a ColumnRef to a plain SQL column name (or a Knex.Raw expression for
|
|
16
|
+
* nested JSON paths when `knex` is supplied).
|
|
15
17
|
*
|
|
16
|
-
* - String refs are treated as **property keys**
|
|
17
|
-
* names via the column map.
|
|
18
|
+
* - String refs are treated as **property keys** (or dot-separated nested paths
|
|
19
|
+
* such as `'address.city'`) and translated to column names via the column map.
|
|
18
20
|
* - Function refs (property accessor) are resolved via PropertyDescriptorTree,
|
|
19
|
-
* then translated to column names.
|
|
21
|
+
* then translated to column names. Nested accessors (e.g. `t => t.address.city`)
|
|
22
|
+
* produce a `Knex.Raw` JSON-path expression when `knex` is provided.
|
|
23
|
+
*
|
|
24
|
+
* @param ref - Column reference (property key, dotted path, or accessor fn).
|
|
25
|
+
* @param schema - Root ObjectSchemaBuilder.
|
|
26
|
+
* @param label - Human-readable label for error messages.
|
|
27
|
+
* @param knex - Knex instance. Required for nested JSON-path refs; when omitted
|
|
28
|
+
* and a nested path is detected, an error is thrown.
|
|
20
29
|
*/
|
|
30
|
+
export declare function resolveColumnRef(ref: ColumnRef<any>, schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>, label: string, knex: Knex): string | Knex.Raw;
|
|
21
31
|
export declare function resolveColumnRef(ref: ColumnRef<any>, schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>, label: string): string;
|
|
22
32
|
/**
|
|
23
33
|
* Resolve a ColumnRef to the **property key** (not the column name).
|
|
24
34
|
* Used for result mapping.
|
|
25
35
|
*/
|
|
26
36
|
export declare function resolvePropertyKey(ref: ColumnRef<any>, schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>, label: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Result of {@link getPrimaryKeyColumns}.
|
|
39
|
+
*
|
|
40
|
+
* `propertyKeys` are the schema property names (the keys you'd use in
|
|
41
|
+
* `.where(t => t.id, ...)` style accessors); `columnNames` are the SQL
|
|
42
|
+
* column names after applying any `hasColumnName()` overrides. Order is
|
|
43
|
+
* preserved: composite-PK ordering matches the user's `hasPrimaryKey()`
|
|
44
|
+
* declaration; single-PK arrays have length 1.
|
|
45
|
+
*
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
export interface PrimaryKeyColumns {
|
|
49
|
+
readonly propertyKeys: readonly string[];
|
|
50
|
+
readonly columnNames: readonly string[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the primary-key columns of a schema at runtime.
|
|
54
|
+
*
|
|
55
|
+
* Composite primary keys (declared via `.hasPrimaryKey([...])` on the
|
|
56
|
+
* object schema) take precedence over single-column primary keys. The
|
|
57
|
+
* `columns` argument to `.hasPrimaryKey()` may contain either property keys
|
|
58
|
+
* or SQL column names — both forms are accepted and normalised.
|
|
59
|
+
*
|
|
60
|
+
* Returns `{ propertyKeys: [], columnNames: [] }` when no primary key is
|
|
61
|
+
* declared.
|
|
62
|
+
*
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
export declare function getPrimaryKeyColumns(schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>): PrimaryKeyColumns;
|
|
66
|
+
/**
|
|
67
|
+
* Concurrency-token strategy stored by `.rowVersion()`.
|
|
68
|
+
* - `'increment'` — ORM increments an integer counter on each UPDATE.
|
|
69
|
+
* - `'timestamp'` — ORM sets the value to `new Date()` on each UPDATE.
|
|
70
|
+
* - `'manual'` — caller supplies the new value; ORM only enforces the check.
|
|
71
|
+
*
|
|
72
|
+
* @public
|
|
73
|
+
*/
|
|
74
|
+
export type RowVersionStrategy = 'increment' | 'timestamp' | 'manual';
|
|
75
|
+
/**
|
|
76
|
+
* Resolved row-version column for a schema, returned by
|
|
77
|
+
* {@link getRowVersionColumn}.
|
|
78
|
+
*
|
|
79
|
+
* @public
|
|
80
|
+
*/
|
|
81
|
+
export interface RowVersionColumn {
|
|
82
|
+
/** Schema property name. */
|
|
83
|
+
propertyKey: string;
|
|
84
|
+
/** SQL column name (after any `hasColumnName()` override). */
|
|
85
|
+
columnName: string;
|
|
86
|
+
/** Concurrency-token update strategy. */
|
|
87
|
+
strategy: RowVersionStrategy;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Find the first column marked `.rowVersion()` on the schema and return its
|
|
91
|
+
* metadata. Returns `null` when no row-version column is declared.
|
|
92
|
+
*
|
|
93
|
+
* @public
|
|
94
|
+
*/
|
|
95
|
+
export declare function getRowVersionColumn(schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>): RowVersionColumn | null;
|
|
27
96
|
export {};
|
package/dist/ddl.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ObjectSchemaBuilder } from '@cleverbrush/schema';
|
|
2
|
+
import type { Knex } from 'knex';
|
|
3
|
+
/**
|
|
4
|
+
* Generate a Knex `schema.createTable()` call from an ObjectSchemaBuilder's
|
|
5
|
+
* introspected metadata and DDL extensions.
|
|
6
|
+
*
|
|
7
|
+
* Returns a function that accepts a Knex instance and returns a
|
|
8
|
+
* `SchemaBuilder` (thenable). Call it inside a migration or setup script:
|
|
9
|
+
*
|
|
10
|
+
* @param schema - An `ObjectSchemaBuilder` with `.hasTableName()` and
|
|
11
|
+
* column-level DDL extensions (`.primaryKey()`, `.references()`, etc.).
|
|
12
|
+
* @returns `(knex: Knex) => Knex.SchemaBuilder` — a function that creates the
|
|
13
|
+
* table when executed.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const createUsersTable = generateCreateTable(UserSchema);
|
|
18
|
+
* await createUsersTable(knex);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function generateCreateTable(schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>): (knex: Knex) => Knex.SchemaBuilder;
|
|
22
|
+
/**
|
|
23
|
+
* Generate `up` and `down` TypeScript code *strings* (4-space-indented\n * fragments) for creating a table from a schema.
|
|
24
|
+
*
|
|
25
|
+
* Unlike {@link generateCreateTable} (which returns a Knex callback), the
|
|
26
|
+
* output is plain source text suitable for writing into a migration file. The
|
|
27
|
+
* fragments are designed to be embedded directly inside `up()` / `down()`
|
|
28
|
+
* functions.
|
|
29
|
+
*
|
|
30
|
+
* @param schema - An `ObjectSchemaBuilder` with `.hasTableName()` and column
|
|
31
|
+
* DDL extensions.
|
|
32
|
+
* @returns `{ up, down }` source fragments.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const { up, down } = generateCreateTableSource(UserSchema);
|
|
37
|
+
* // up: " await knex.schema.createTable('users', (table) => { … });"
|
|
38
|
+
* // down: " await knex.schema.dropTableIfExists('users');"
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function generateCreateTableSource(schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>): {
|
|
42
|
+
up: string;
|
|
43
|
+
down: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Generate a series of Knex `schema.createTable()` calls for a polymorphic
|
|
47
|
+
* schema and all its CTI variant tables.
|
|
48
|
+
*
|
|
49
|
+
* Returns an array of `(knex: Knex) => Knex.SchemaBuilder` functions — one
|
|
50
|
+
* for the base table and one for each CTI variant. STI variants add their
|
|
51
|
+
* columns to the base table itself so no extra table is needed.
|
|
52
|
+
*
|
|
53
|
+
* Execute them sequentially in a migration:
|
|
54
|
+
*
|
|
55
|
+
* @param schema - The base `ObjectSchemaBuilder` with `.withVariants()` applied.
|
|
56
|
+
* @returns An array of table-creation functions to execute in order.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const creators = generateCreatePolymorphicTables(FileSchema);
|
|
61
|
+
* for (const create of creators) {
|
|
62
|
+
* await create(knex);
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function generateCreatePolymorphicTables(schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>): Array<(knex: Knex) => Knex.SchemaBuilder>;
|
package/dist/entity.d.ts
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { type ArraySchemaBuilder, type InferType, ObjectSchemaBuilder, type PropertyDescriptorTree, type SchemaBuilder, SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR } from '@cleverbrush/schema';
|
|
2
|
+
/**
|
|
3
|
+
* Phantom info for a single declared relation, stored on `Entity`'s `TRels`
|
|
4
|
+
* generic. `TForeign` is captured so `.include()` can type the customize
|
|
5
|
+
* callback against the correct foreign schema.
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export interface RelationInfo<TKind extends 'belongsTo' | 'hasOne' | 'hasMany' | 'belongsToMany' = any, TForeign extends ObjectSchemaBuilder<any, any, any, any, any, any, any> = ObjectSchemaBuilder<any, any, any, any, any, any, any>> {
|
|
10
|
+
readonly kind: TKind;
|
|
11
|
+
readonly foreign: TForeign;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Extract the property record `{ [name]: SchemaBuilder }` from any
|
|
15
|
+
* `ObjectSchemaBuilder` regardless of variance positions.
|
|
16
|
+
*
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
export type SchemaProps<T> = T extends ObjectSchemaBuilder<infer P, any, any, any, any, any, any> ? P : never;
|
|
20
|
+
/**
|
|
21
|
+
* Strips the `withExtensions()` overlay from a schema type, reducing it to a
|
|
22
|
+
* plain `ObjectSchemaBuilder<TProps, TReq>` so `PropertyDescriptorTree<...>`
|
|
23
|
+
* resolves without hitting TypeScript's recursion depth limit.
|
|
24
|
+
*
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
type EntitySchemaBase<T extends ObjectSchemaBuilder<any, any, any, any, any, any, any>> = T extends ObjectSchemaBuilder<infer P, infer Req, any, any, any, any, any> ? ObjectSchemaBuilder<P, Req> : never;
|
|
28
|
+
/**
|
|
29
|
+
* `PropertyDescriptorTree` overlay that pins each top-level property's
|
|
30
|
+
* literal `propertyName` into its descriptor. Required because
|
|
31
|
+
* `PropertyDescriptorTree` widens `propertyName` to `string` for sub-trees
|
|
32
|
+
* whose property is itself an `ObjectSchemaBuilder` (since
|
|
33
|
+
* `PropertyDescriptorTree` lacks a `TPropertyKey` generic).
|
|
34
|
+
*
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
type EntityTree<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>> = PropertyDescriptorTree<EntitySchemaBase<TSchema>, EntitySchemaBase<TSchema>> & {
|
|
38
|
+
readonly [K in keyof SchemaProps<TSchema> & string]: {
|
|
39
|
+
readonly [SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]: {
|
|
40
|
+
readonly propertyName: K;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Selector callback that receives a real {@link PropertyDescriptorTree} so
|
|
46
|
+
* `t => t.someProp` navigates to the property definition and preserves its
|
|
47
|
+
* JSDoc in IDE tooltips. The return shape is matched structurally on the
|
|
48
|
+
* `[SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR].propertyName` field, which captures
|
|
49
|
+
* the literal property key as `TKey`.
|
|
50
|
+
*
|
|
51
|
+
* @public
|
|
52
|
+
*/
|
|
53
|
+
export type EntityPropSelector<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TKey extends string = string> = (t: EntityTree<TSchema>) => {
|
|
54
|
+
readonly [SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR]: {
|
|
55
|
+
readonly propertyName: TKey;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Peel `.optional()` / `array(...)` wrappers off a navigation property's
|
|
60
|
+
* schema to recover the underlying foreign `ObjectSchemaBuilder`.
|
|
61
|
+
*
|
|
62
|
+
* @public
|
|
63
|
+
*/
|
|
64
|
+
export type UnwrapNavSchema<TProp> = TProp extends ArraySchemaBuilder<infer TEl, any, any, any> ? TEl extends ObjectSchemaBuilder<any, any, any, any, any, any, any> ? TEl : never : TProp extends ObjectSchemaBuilder<any, any, any, any, any, any, any> ? TProp : TProp extends SchemaBuilder<infer T, any, any, any, any> ? T extends ObjectSchemaBuilder<any, any, any, any, any, any, any> ? T : never : never;
|
|
65
|
+
/**
|
|
66
|
+
* Merge type for a single polymorphic variant branch: variant schema fields
|
|
67
|
+
* overlay base schema fields (narrowing the discriminator from `string` to its
|
|
68
|
+
* specific literal value).
|
|
69
|
+
*
|
|
70
|
+
* @public
|
|
71
|
+
*/
|
|
72
|
+
export type VariantBranch<TBaseSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TVarSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>> = Omit<InferType<TBaseSchema>, keyof InferType<TVarSchema>> & InferType<TVarSchema>;
|
|
73
|
+
/**
|
|
74
|
+
* Return type for an Entity method that adds a relation: keeps `TSchema`,
|
|
75
|
+
* extends `TRels` with one more entry, and preserves `TVariantUnion`.
|
|
76
|
+
*
|
|
77
|
+
* @public
|
|
78
|
+
*/
|
|
79
|
+
export type WithRelation<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TRels extends Record<string, RelationInfo>, TKey extends string, TKind extends 'belongsTo' | 'hasOne' | 'hasMany' | 'belongsToMany', TForeign extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TVariantUnion = never> = Entity<TSchema, TRels & Record<TKey, RelationInfo<TKind, TForeign>>, TVariantUnion>;
|
|
80
|
+
/**
|
|
81
|
+
* A typed wrapper around an `ObjectSchemaBuilder` that tracks declared
|
|
82
|
+
* relations in its `TRels` generic. Use {@link defineEntity} to create one.
|
|
83
|
+
*
|
|
84
|
+
* Relations are declared via `.hasOne()` / `.hasMany()` / `.belongsTo()` /
|
|
85
|
+
* `.belongsToMany()` on the entity (NOT on the underlying schema). Each
|
|
86
|
+
* call returns a new `Entity` whose `TRels` includes the new relation, so
|
|
87
|
+
* `query(db, entity).include(t => t.assignee)` is fully typed.
|
|
88
|
+
*
|
|
89
|
+
* @public
|
|
90
|
+
*/
|
|
91
|
+
export declare class Entity<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>, TRels extends Record<string, RelationInfo> = {}, TVariantUnion = never> {
|
|
92
|
+
/** The underlying schema (relations registered via `withExtension('relations', ...)`). */
|
|
93
|
+
readonly schema: TSchema;
|
|
94
|
+
/** @internal Phantom slot to retain `TRels` in inferred types. */
|
|
95
|
+
private readonly __relations__;
|
|
96
|
+
/** @internal Phantom slot to retain `TVariantUnion` in inferred types. */
|
|
97
|
+
private readonly __variantUnion__;
|
|
98
|
+
constructor(schema: TSchema);
|
|
99
|
+
private _addRelation;
|
|
100
|
+
/**
|
|
101
|
+
* Declare a one-to-one relation where the FK lives on the FOREIGN table.
|
|
102
|
+
* `navSel` selects the navigation property on THIS schema (must hold the
|
|
103
|
+
* foreign schema, typically `.optional()`). The foreign schema is
|
|
104
|
+
* auto-resolved at runtime from that property; provided explicitly here
|
|
105
|
+
* via `opts.foreign` only when peeling fails.
|
|
106
|
+
*
|
|
107
|
+
* @param navSel Selector of nav property: `t => t.author`
|
|
108
|
+
* @param localSel Selector of local-side join key: `l => l.id`
|
|
109
|
+
* @param remoteSel Selector of remote-side FK on foreign schema: `r => r.userId`
|
|
110
|
+
* @param opts Optional `{ optional?: boolean }` (default false).
|
|
111
|
+
*/
|
|
112
|
+
hasOne<TKey extends keyof SchemaProps<TSchema> & string, TForeign extends ObjectSchemaBuilder<any, any, any, any, any, any, any> = UnwrapNavSchema<SchemaProps<TSchema>[TKey]>>(navSel: EntityPropSelector<TSchema, TKey>, _localSel: EntityPropSelector<TSchema>, remoteSel: EntityPropSelector<TForeign>, opts?: {
|
|
113
|
+
optional?: boolean;
|
|
114
|
+
}): WithRelation<TSchema, TRels, TKey, 'hasOne', TForeign, TVariantUnion>;
|
|
115
|
+
/**
|
|
116
|
+
* Declare a one-to-many relation where the FK lives on the FOREIGN table.
|
|
117
|
+
*
|
|
118
|
+
* @param navSel Selector of nav array property: `t => t.posts`
|
|
119
|
+
* @param localSel Selector of local-side join key: `l => l.id`
|
|
120
|
+
* @param remoteSel Selector of remote-side FK on foreign schema: `r => r.userId`
|
|
121
|
+
*/
|
|
122
|
+
hasMany<TKey extends keyof SchemaProps<TSchema> & string, TForeign extends ObjectSchemaBuilder<any, any, any, any, any, any, any> = UnwrapNavSchema<SchemaProps<TSchema>[TKey]>>(navSel: EntityPropSelector<TSchema, TKey>, _localSel: EntityPropSelector<TSchema>, remoteSel: EntityPropSelector<TForeign>): WithRelation<TSchema, TRels, TKey, 'hasMany', TForeign, TVariantUnion>;
|
|
123
|
+
/**
|
|
124
|
+
* Declare a many-to-one relation where the FK lives on THIS table.
|
|
125
|
+
*
|
|
126
|
+
* @param navSel Selector of nav property (foreign schema, usually `.optional()`).
|
|
127
|
+
* @param localSel Selector of local-side FK property: `l => l.userId`
|
|
128
|
+
* @param remoteSel Selector of foreign-side PK property: `r => r.id`
|
|
129
|
+
*/
|
|
130
|
+
belongsTo<TKey extends keyof SchemaProps<TSchema> & string, TForeign extends ObjectSchemaBuilder<any, any, any, any, any, any, any> = UnwrapNavSchema<SchemaProps<TSchema>[TKey]>>(navSel: EntityPropSelector<TSchema, TKey>, localSel: EntityPropSelector<TSchema>, _remoteSel: EntityPropSelector<TForeign>, opts?: {
|
|
131
|
+
optional?: boolean;
|
|
132
|
+
}): WithRelation<TSchema, TRels, TKey, 'belongsTo', TForeign, TVariantUnion>;
|
|
133
|
+
/**
|
|
134
|
+
* Declare a many-to-many relation through a pivot table.
|
|
135
|
+
*
|
|
136
|
+
* @param navSel Selector of nav array property: `t => t.tags`
|
|
137
|
+
* @param through Pivot table config `{ table, localKey, foreignKey }`.
|
|
138
|
+
*/
|
|
139
|
+
belongsToMany<TKey extends keyof SchemaProps<TSchema> & string, TForeign extends ObjectSchemaBuilder<any, any, any, any, any, any, any> = UnwrapNavSchema<SchemaProps<TSchema>[TKey]>>(navSel: EntityPropSelector<TSchema, TKey>, through: {
|
|
140
|
+
table: string;
|
|
141
|
+
localKey: string;
|
|
142
|
+
foreignKey: string;
|
|
143
|
+
}): WithRelation<TSchema, TRels, TKey, 'belongsToMany', TForeign, TVariantUnion>;
|
|
144
|
+
/**
|
|
145
|
+
* Mark this entity as polymorphic by selecting the discriminator
|
|
146
|
+
* property. Returns a new `Entity` whose `.ctiVariant()` /
|
|
147
|
+
* `.stiVariant()` methods declare each variant.
|
|
148
|
+
*
|
|
149
|
+
* Declare ordinary relations (`.belongsTo()` etc.) on this entity BEFORE
|
|
150
|
+
* `.discriminator()`. After `.discriminator()` you are in the
|
|
151
|
+
* polymorphic-builder phase: `.hasOne/.hasMany/.belongsTo/.belongsToMany`
|
|
152
|
+
* still work, but the per-variant relations live on the `.ctiVariant()`
|
|
153
|
+
* / `.stiVariant()` calls.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* const FileEntity = defineEntity(FileSchema)
|
|
158
|
+
* .discriminator(t => t.type)
|
|
159
|
+
* .ctiVariant('image', ImageEntity, t => t.fileId)
|
|
160
|
+
* .ctiVariant('document', DocumentEntity, t => t.fileId);
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
discriminator<TKey extends keyof SchemaProps<TSchema> & string>(sel: TKey | EntityPropSelector<TSchema, TKey>): Entity<TSchema, TRels, never>;
|
|
164
|
+
/**
|
|
165
|
+
* Declare a CTI (Class Table Inheritance) variant. Only valid after
|
|
166
|
+
* `.discriminator()`. Returns a new `Entity` whose schema carries the
|
|
167
|
+
* updated `'variants'` extension.
|
|
168
|
+
*
|
|
169
|
+
* @param key Discriminator literal (e.g. `'image'`).
|
|
170
|
+
* @param variant Entity wrapping the variant table schema.
|
|
171
|
+
* @param fkSel Accessor returning the FK property on the variant
|
|
172
|
+
* schema that joins back to the base PK.
|
|
173
|
+
* @param opts `{ allowOrphan?: boolean; relations?: ... }` — see CTI docs.
|
|
174
|
+
*/
|
|
175
|
+
ctiVariant<TVarSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(key: string, variant: Entity<TVarSchema, any>, fkSel: (t: EntityTree<TVarSchema>) => any, opts?: {
|
|
176
|
+
allowOrphan?: boolean;
|
|
177
|
+
relations?: Record<string, VariantRelationInput<TVarSchema>>;
|
|
178
|
+
}): Entity<TSchema, TRels, TVariantUnion | VariantBranch<TSchema, TVarSchema>>;
|
|
179
|
+
/**
|
|
180
|
+
* Declare an STI (Single Table Inheritance) variant. Only valid after
|
|
181
|
+
* `.discriminator()`. Accepts either an {@link Entity} or a bare
|
|
182
|
+
* {@link ObjectSchemaBuilder}.
|
|
183
|
+
*/
|
|
184
|
+
stiVariant<TVarSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(key: string, body: Entity<TVarSchema, any> | TVarSchema, opts?: {
|
|
185
|
+
enforceCheck?: boolean;
|
|
186
|
+
relations?: Record<string, VariantRelationInput<TVarSchema>>;
|
|
187
|
+
}): Entity<TSchema, TRels, TVariantUnion | VariantBranch<TSchema, TVarSchema>>;
|
|
188
|
+
/** @internal Apply the variant extension to the schema and return a new Entity. */
|
|
189
|
+
private _withVariantBuilder;
|
|
190
|
+
/**
|
|
191
|
+
* @internal
|
|
192
|
+
* Resolve a selector callback against the real `PropertyDescriptorTree`
|
|
193
|
+
* of the given schema, returning the top-level property name. Throws if
|
|
194
|
+
* the accessor does not yield a valid descriptor.
|
|
195
|
+
*/
|
|
196
|
+
private _resolvePropName;
|
|
197
|
+
/** @internal Resolve the foreign schema from a nav property. */
|
|
198
|
+
private _resolveForeignSchema;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Inline relation spec accepted by {@link Entity.ctiVariant} /
|
|
202
|
+
* {@link Entity.stiVariant} via `opts.relations`. The `foreignKey`
|
|
203
|
+
* accessor (when given) is typed against the variant's own schema and
|
|
204
|
+
* resolved to a SQL column name.
|
|
205
|
+
*
|
|
206
|
+
* @public
|
|
207
|
+
*/
|
|
208
|
+
export interface VariantRelationInput<TVarSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any> = ObjectSchemaBuilder<any, any, any, any, any, any, any>> {
|
|
209
|
+
type: 'belongsTo' | 'hasOne' | 'hasMany' | 'belongsToMany';
|
|
210
|
+
/**
|
|
211
|
+
* Foreign side — accept either a bare schema, an Entity wrapper, or a
|
|
212
|
+
* lazy thunk for forward references.
|
|
213
|
+
*/
|
|
214
|
+
schema?: ObjectSchemaBuilder<any, any, any, any, any, any, any> | (() => ObjectSchemaBuilder<any, any, any, any, any, any, any>);
|
|
215
|
+
entity?: Entity<any, any>;
|
|
216
|
+
foreignKey?: (t: EntityTree<TVarSchema>) => any;
|
|
217
|
+
through?: {
|
|
218
|
+
table: string;
|
|
219
|
+
localKey: string;
|
|
220
|
+
foreignKey: string;
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Wrap an `ObjectSchemaBuilder` in an {@link Entity}, enabling typed relation
|
|
225
|
+
* declaration via `.hasOne()` / `.hasMany()` / `.belongsTo()` / `.belongsToMany()`.
|
|
226
|
+
*
|
|
227
|
+
* @public
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```ts
|
|
231
|
+
* const UserEntity = defineEntity(
|
|
232
|
+
* object({
|
|
233
|
+
* id: number().primaryKey(),
|
|
234
|
+
* name: string(),
|
|
235
|
+
* posts: array(PostEntity.schema)
|
|
236
|
+
* }).hasTableName('users')
|
|
237
|
+
* ).hasMany(t => t.posts, l => l.id, r => r.userId);
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
export declare function defineEntity<TSchema extends ObjectSchemaBuilder<any, any, any, any, any, any, any>>(schema: TSchema): Entity<TSchema, {}>;
|
|
241
|
+
/**
|
|
242
|
+
* Type-level helper: extract `TRels` from any `Entity` type.
|
|
243
|
+
* @public
|
|
244
|
+
*/
|
|
245
|
+
export type EntityRelations<E> = E extends Entity<any, infer R, any> ? R : never;
|
|
246
|
+
/**
|
|
247
|
+
* Type-level helper: extract the underlying schema type from any `Entity`.
|
|
248
|
+
* @public
|
|
249
|
+
*/
|
|
250
|
+
export type EntitySchema<E> = E extends Entity<infer S, any, any> ? S : never;
|
|
251
|
+
/**
|
|
252
|
+
* Type-level helper: extract the accumulated variant union from any `Entity`.
|
|
253
|
+
* Resolves to `never` for non-polymorphic entities.
|
|
254
|
+
* @public
|
|
255
|
+
*/
|
|
256
|
+
export type EntityVariantUnion<E> = E extends Entity<any, any, infer U> ? U : never;
|
|
257
|
+
/**
|
|
258
|
+
* Type-level helper: union of relation key names declared on an entity.
|
|
259
|
+
* Used by `SchemaQueryBuilder.insert()/update()/upsert()` to omit relation
|
|
260
|
+
* navigation properties from accepted input.
|
|
261
|
+
* @public
|
|
262
|
+
*/
|
|
263
|
+
export type EntityRelationKeys<E> = E extends Entity<any, infer R, any> ? keyof R & string : never;
|
|
264
|
+
export {};
|