uql-orm 0.65.1 → 0.67.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.
Files changed (193) hide show
  1. package/dist/browser/querier/httpQuerier.js +1 -8
  2. package/dist/browser/uql-browser.min.js.map +5 -5
  3. package/dist/bunSql/bunSql.util.d.ts +2 -6
  4. package/dist/bunSql/bunSql.util.js +2 -6
  5. package/dist/bunSql/bunSqlQuerier.d.ts +2 -5
  6. package/dist/bunSql/bunSqlQuerier.js +2 -5
  7. package/dist/cockroachdb/cockroachDialect.d.ts +4 -13
  8. package/dist/cockroachdb/cockroachDialect.js +4 -13
  9. package/dist/context/context.browser.js +2 -10
  10. package/dist/context/context.d.ts +4 -17
  11. package/dist/context/context.js +4 -17
  12. package/dist/dialect/abstractDialect.d.ts +4 -19
  13. package/dist/dialect/abstractDialect.js +2 -20
  14. package/dist/dialect/abstractSqlDialect.d.ts +47 -212
  15. package/dist/dialect/abstractSqlDialect.js +68 -222
  16. package/dist/dialect/aliases.d.ts +2 -12
  17. package/dist/dialect/aliases.js +4 -12
  18. package/dist/dialect/hydrateColumn.d.ts +2 -6
  19. package/dist/dialect/hydrateColumn.js +3 -13
  20. package/dist/dialect/jsonArrayElemMatchUtils.d.ts +1 -7
  21. package/dist/dialect/jsonArrayElemMatchUtils.js +1 -7
  22. package/dist/dialect/jsonSql.d.ts +6 -27
  23. package/dist/dialect/jsonSql.js +6 -27
  24. package/dist/dialect/mergeSqlDialect.d.ts +4 -22
  25. package/dist/dialect/mergeSqlDialect.js +4 -22
  26. package/dist/dialect/mysqlLikeSqlDialect.d.ts +11 -37
  27. package/dist/dialect/mysqlLikeSqlDialect.js +35 -51
  28. package/dist/dialect/pgLikeSqlDialect.d.ts +8 -22
  29. package/dist/dialect/pgLikeSqlDialect.js +36 -39
  30. package/dist/dialect/queryContext.d.ts +4 -22
  31. package/dist/dialect/queryContext.js +4 -22
  32. package/dist/dialect/queryJoins.d.ts +3 -12
  33. package/dist/dialect/queryJoins.js +3 -12
  34. package/dist/dialect/vectorCast.d.ts +2 -12
  35. package/dist/dialect/vectorCast.js +3 -19
  36. package/dist/dialect/vectorSqlDialect.d.ts +8 -38
  37. package/dist/dialect/vectorSqlDialect.js +7 -38
  38. package/dist/entity/decorator/bag.d.ts +6 -19
  39. package/dist/entity/decorator/bag.js +6 -22
  40. package/dist/entity/decorator/entity.d.ts +5 -10
  41. package/dist/entity/decorator/entity.js +2 -7
  42. package/dist/entity/decorator/members.d.ts +10 -31
  43. package/dist/entity/decorator/members.js +3 -12
  44. package/dist/entity/metadata/definition.d.ts +5 -21
  45. package/dist/entity/metadata/definition.js +69 -91
  46. package/dist/http/handler.d.ts +2 -14
  47. package/dist/index.d.ts +3 -1
  48. package/dist/index.js +3 -1
  49. package/dist/libsql/libsqlDialect.d.ts +1 -8
  50. package/dist/libsql/libsqlDialect.js +1 -8
  51. package/dist/maria/mariaDialect.d.ts +3 -5
  52. package/dist/maria/mariaDialect.js +5 -5
  53. package/dist/maria/mariadbQuerier.js +2 -2
  54. package/dist/maria/mariadbQuerierPool.js +1 -6
  55. package/dist/migrate/builder/migrationBuilder.js +3 -19
  56. package/dist/migrate/builder/splitSqlStatements.d.ts +1 -14
  57. package/dist/migrate/builder/splitSqlStatements.js +2 -22
  58. package/dist/migrate/builder/types.d.ts +2 -15
  59. package/dist/migrate/cli-config.js +2 -11
  60. package/dist/migrate/cli.js +2 -7
  61. package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -15
  62. package/dist/migrate/codegen/entityCodeGenerator.js +15 -44
  63. package/dist/migrate/codegen/fieldOptionsSource.d.ts +1 -8
  64. package/dist/migrate/codegen/fieldOptionsSource.js +3 -22
  65. package/dist/migrate/ddl/indexDdl.d.ts +2 -5
  66. package/dist/migrate/ddl/indexDdl.js +2 -5
  67. package/dist/migrate/ddl/pgIndexDdl.d.ts +3 -13
  68. package/dist/migrate/ddl/pgIndexDdl.js +3 -13
  69. package/dist/migrate/generator/definitionToNode.d.ts +2 -9
  70. package/dist/migrate/generator/definitionToNode.js +3 -17
  71. package/dist/migrate/generator/indexNodeToSchema.d.ts +2 -3
  72. package/dist/migrate/generator/indexNodeToSchema.js +2 -3
  73. package/dist/migrate/generator/mongoCommand.d.ts +1 -8
  74. package/dist/migrate/generator/mongoSchemaGenerator.d.ts +1 -8
  75. package/dist/migrate/generator/mongoSchemaGenerator.js +1 -8
  76. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +6 -26
  77. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +9 -41
  78. package/dist/migrate/introspection/baseSqlIntrospector.js +0 -1
  79. package/dist/migrate/introspection/mongoIntrospector.d.ts +3 -1
  80. package/dist/migrate/introspection/mongoIntrospector.js +48 -46
  81. package/dist/migrate/introspection/mssqlIntrospector.d.ts +4 -4
  82. package/dist/migrate/introspection/mssqlIntrospector.js +18 -27
  83. package/dist/migrate/introspection/mysqlIntrospector.d.ts +7 -2
  84. package/dist/migrate/introspection/mysqlIntrospector.js +16 -14
  85. package/dist/migrate/introspection/postgresIntrospector.d.ts +24 -9
  86. package/dist/migrate/introspection/postgresIntrospector.js +68 -59
  87. package/dist/migrate/introspection/sqliteIntrospector.d.ts +1 -1
  88. package/dist/migrate/introspection/sqliteIntrospector.js +8 -10
  89. package/dist/migrate/migrator.d.ts +9 -53
  90. package/dist/migrate/migrator.js +32 -65
  91. package/dist/migrate/schemaGenerator.d.ts +20 -66
  92. package/dist/migrate/schemaGenerator.js +32 -93
  93. package/dist/mongo/mongoDialect.d.ts +21 -53
  94. package/dist/mongo/mongoDialect.js +25 -70
  95. package/dist/mongo/mongodbQuerier.d.ts +5 -8
  96. package/dist/mongo/mongodbQuerier.js +31 -65
  97. package/dist/mssql/mssqlDialect.d.ts +8 -34
  98. package/dist/mssql/mssqlDialect.js +37 -51
  99. package/dist/mssql/mssqlQuerier.d.ts +37 -4
  100. package/dist/mssql/mssqlQuerier.js +2 -2
  101. package/dist/mssql/mssqlWireTypes.d.ts +2 -14
  102. package/dist/mssql/mssqlWireTypes.js +2 -14
  103. package/dist/nestjs/uqlModule.js +2 -7
  104. package/dist/pglite/pgliteQuerier.d.ts +1 -9
  105. package/dist/pglite/pgliteQuerierPool.d.ts +4 -26
  106. package/dist/pglite/pgliteQuerierPool.js +3 -18
  107. package/dist/postgres/abstractPgQuerierPool.d.ts +1 -8
  108. package/dist/postgres/abstractPgQuerierPool.js +1 -8
  109. package/dist/postgres/pgNumericTypes.d.ts +3 -26
  110. package/dist/postgres/pgNumericTypes.js +3 -26
  111. package/dist/postgres/postgresDialect.d.ts +4 -10
  112. package/dist/postgres/postgresDialect.js +4 -10
  113. package/dist/querier/abstractQuerier.d.ts +35 -103
  114. package/dist/querier/abstractQuerier.js +105 -201
  115. package/dist/querier/abstractSharedHandleQuerierPool.d.ts +3 -17
  116. package/dist/querier/abstractSharedHandleQuerierPool.js +3 -17
  117. package/dist/querier/abstractSqlQuerier.d.ts +15 -36
  118. package/dist/querier/abstractSqlQuerier.js +49 -131
  119. package/dist/schema/canonicalType.d.ts +3 -21
  120. package/dist/schema/canonicalType.js +22 -67
  121. package/dist/schema/dependencyGraph.d.ts +2 -8
  122. package/dist/schema/dependencyGraph.js +2 -32
  123. package/dist/schema/index.d.ts +1 -25
  124. package/dist/schema/index.js +0 -26
  125. package/dist/schema/indexColumns.d.ts +1 -8
  126. package/dist/schema/indexColumns.js +1 -8
  127. package/dist/schema/indexDifferences.d.ts +7 -40
  128. package/dist/schema/indexDifferences.js +6 -31
  129. package/dist/schema/schemaAST.d.ts +8 -175
  130. package/dist/schema/schemaAST.js +13 -365
  131. package/dist/schema/schemaASTBuilder.d.ts +2 -24
  132. package/dist/schema/schemaASTBuilder.js +6 -41
  133. package/dist/schema/schemaASTDiffer.d.ts +6 -46
  134. package/dist/schema/schemaASTDiffer.js +8 -56
  135. package/dist/schema/types.d.ts +5 -61
  136. package/dist/schema/types.js +3 -6
  137. package/dist/sqlite/abstractSqliteQuerier.d.ts +1 -8
  138. package/dist/sqlite/localSqliteQuerierPool.d.ts +1 -7
  139. package/dist/sqlite/localSqliteQuerierPool.js +1 -7
  140. package/dist/sqlite/nodeSqliteQuerierPool.d.ts +2 -7
  141. package/dist/sqlite/nodeSqliteQuerierPool.js +2 -7
  142. package/dist/sqlite/sqliteDialect.d.ts +5 -20
  143. package/dist/sqlite/sqliteDialect.js +29 -35
  144. package/dist/turso/tursoDialect.d.ts +4 -6
  145. package/dist/turso/tursoDialect.js +4 -6
  146. package/dist/turso/tursoLocalQuerierPool.d.ts +1 -7
  147. package/dist/turso/tursoLocalQuerierPool.js +1 -7
  148. package/dist/turso/tursoQuerierPool.d.ts +2 -6
  149. package/dist/turso/tursoQuerierPool.js +2 -6
  150. package/dist/turso/tursoSessionQuerier.d.ts +1 -7
  151. package/dist/turso/tursoSessionQuerier.js +1 -7
  152. package/dist/type/dialect.d.ts +42 -94
  153. package/dist/type/dialect.js +3 -13
  154. package/dist/type/entity.d.ts +189 -551
  155. package/dist/type/entity.js +26 -9
  156. package/dist/type/logger.d.ts +2 -14
  157. package/dist/type/migration.d.ts +9 -38
  158. package/dist/type/querier.d.ts +9 -28
  159. package/dist/type/querierPool.d.ts +4 -26
  160. package/dist/type/query.d.ts +28 -78
  161. package/dist/type/query.js +2 -7
  162. package/dist/type/queryAggregate.d.ts +18 -98
  163. package/dist/type/queryRaw.d.ts +1 -8
  164. package/dist/type/queryRaw.js +1 -8
  165. package/dist/type/queryWhere.d.ts +13 -61
  166. package/dist/type/universalQuerier.d.ts +18 -105
  167. package/dist/type/utility.d.ts +12 -24
  168. package/dist/type/vector.d.ts +8 -38
  169. package/dist/type/vector.js +1 -1
  170. package/dist/type/wire.d.ts +2 -5
  171. package/dist/util/dialect.util.d.ts +9 -27
  172. package/dist/util/dialect.util.js +10 -27
  173. package/dist/util/field.util.d.ts +5 -37
  174. package/dist/util/field.util.js +7 -50
  175. package/dist/util/fieldOption.util.d.ts +7 -15
  176. package/dist/util/fieldOption.util.js +1 -1
  177. package/dist/util/filters.util.d.ts +2 -5
  178. package/dist/util/filters.util.js +2 -5
  179. package/dist/util/logger.d.ts +2 -6
  180. package/dist/util/logger.js +2 -6
  181. package/dist/util/object.util.d.ts +2 -6
  182. package/dist/util/object.util.js +1 -5
  183. package/dist/util/raw.d.ts +3 -23
  184. package/dist/util/relationQuery.util.d.ts +3 -14
  185. package/dist/util/relationQuery.util.js +3 -14
  186. package/dist/util/rowKey.util.d.ts +2 -10
  187. package/dist/util/rowKey.util.js +2 -10
  188. package/dist/util/sql.util.d.ts +6 -37
  189. package/dist/util/sql.util.js +13 -73
  190. package/dist/util/sqlLiteral.d.ts +2 -13
  191. package/dist/util/sqlLiteral.js +8 -13
  192. package/dist/util/string.util.js +0 -2
  193. package/package.json +4 -4
@@ -4,89 +4,54 @@ import type { ColumnRef, QueryRaw } from './queryRaw.js';
4
4
  import type { QueryWhere } from './queryWhere.js';
5
5
  import type { Except, IsMany, Json, Scalar, Type, Unpacked } from './utility.js';
6
6
  import type { VectorDistance, VectorIndexOptions, VectorIndexType } from './vector.js';
7
- /**
8
- * Allow to customize the name of the property that identifies an entity
9
- */
7
+ /** Brands the property an entity is identified by, where it is not `id`, `_id` or `uuid`. */
10
8
  export declare const idKey: unique symbol;
11
- /**
12
- * The one filter name uql registers itself, from `@Field({ softDelete })`. Four ends have to agree on
13
- * it and none would fail if they drifted: the field that registers it, the decorator that reserves
14
- * the name against a user's own filter, the hard delete that switches it off, and the bypass check
15
- * that lets it through on an entity which never declared one.
16
- */
9
+ /** The filter `@Field({ softDelete })` registers, a name reserved against an entity's own filters. */
17
10
  export declare const SOFT_DELETE_FILTER = "softDelete";
18
- /**
19
- * Infers the key names of an entity
20
- */
11
+ /** A filter name an entity may declare: any but {@link SOFT_DELETE_FILTER}, which a refusal names. */
12
+ export type FilterName<N extends string> = N extends typeof SOFT_DELETE_FILTER ? `'${N}' is reserved for the filter @Field({ softDelete }) registers` : N;
13
+ /** The key names of an entity. */
21
14
  export type Key<E> = keyof E & string;
22
15
  /**
23
- * Infers the field names of an entity.
24
- * Includes scalar fields, JSON fields, scalar arrays (e.g. vector `number[]`) and arrays of JSON.
25
- * The `-?` modifier strips optionality so the indexed access yields clean key unions
26
- * (without it, optional properties leak `undefined` into the union).
27
- *
28
- * `readonly Json[]` is its own arm because the brand sits on the element, so the `Json` arm cannot
29
- * see it. What keeps a to-many relation out of that arm is the weak-type check: `Json<unknown>` is
30
- * all-optional, which a class with named properties is not assignable to.
31
- *
32
- * The check is bracketed so `any` resolves once rather than matching both this and
33
- * {@link RelationKey}: an unbracketed `any extends X` satisfies either branch. It reads
34
- * `readonly Scalar[]`, which every mutable one satisfies too, so declaring a vector or a scalar
35
- * array `readonly` does not push the field over into {@link RelationKey}.
16
+ * The field names of an entity: scalars, scalar arrays (a vector) and JSON, including a list of JSON
17
+ * documents, whose brand sits on the element. The check is bracketed so `any` lands on one side, and a
18
+ * class is kept off the `Json` arm by the weak-type check.
36
19
  */
37
20
  export type FieldKey<E> = {
38
21
  readonly [K in keyof E]-?: [NonNullable<E[K]>] extends [Scalar | readonly Scalar[] | Json | readonly Json[]] ? K : never;
39
22
  }[Key<E>];
40
- /**
41
- * Infers the relation names of an entity: whatever is left once its fields and its methods are
42
- * taken out. Stated as the complement rather than as {@link FieldKey}'s test negated, so the two
43
- * cannot drift; methods are subtracted because one is not a `Scalar` and would otherwise read as a
44
- * relation.
45
- */
23
+ /** The relation names of an entity: every key but its fields and its methods, so the two sets cannot drift. */
46
24
  export type RelationKey<E> = Exclude<Key<E>, FieldKey<E> | MethodKey<E>>;
47
- /**
48
- * Whether `T` carries the `Json` brand. Checks for the `__json` marker key explicitly:
49
- * a bare `extends Json<infer T>` is not discriminating in check position (primitives match it,
50
- * inferring junk like `T = string`), while the marker key only exists on branded types.
51
- */
25
+ /** Whether `T` carries the `Json` brand, read off its marker key: a primitive matches `Json<infer P>` too. */
52
26
  type IsJson<T> = '__json' extends keyof T ? true : false;
53
- /** Whether `T` is what a JSON column holds: the branded payload, or an array of them. */
54
- type IsJsonColumn<T> = IsJson<T> extends true ? true : IsJson<NonNullable<Unpacked<T>>>;
55
- /** The payload `P` of a branded `Json<P>`, or `never` for any non-JSON type. */
27
+ /** The payload `P` of a branded `Json<P>`, or `never` for any other type. */
56
28
  type UnwrapJson<T> = IsJson<T> extends true ? (T extends Json<infer P> ? P : never) : never;
29
+ /** What a JSON column declared as `V` holds, `Json<P>` or `Json<P>[]` alike; `never` on any other column. */
30
+ type JsonPayload<V, T = NonNullable<V>> = IsJson<T> extends true ? UnwrapJson<T> : UnwrapJson<NonNullable<Unpacked<T>>>;
31
+ /** Whether `V` is what a JSON column holds. */
32
+ type IsJsonColumn<V> = [JsonPayload<V>] extends [never] ? false : true;
33
+ /** The JSON columns of `E`, which an index can address. */
34
+ type JsonColumnKey<E> = {
35
+ readonly [K in keyof E]-?: IsJsonColumn<E[K]> extends true ? K : never;
36
+ }[Key<E>];
57
37
  /**
58
- * The one branded value a field value `V` holds: `Json<T>` for both `Json<T>` and `Json<T>[]`, via
59
- * `Unpacked`, a no-op for the non-array case.
60
- */
61
- type JsonElement<V> = NonNullable<Unpacked<NonNullable<V>>>;
62
- /** The `Json` payload of a field value `V`; `never` when `V` is not a JSON field. */
63
- type JsonPayload<V> = UnwrapJson<JsonElement<V>>;
64
- /**
65
- * The fields carrying the `Json` brand, `never` on an entity with none - which is most of them, and
66
- * what makes {@link JsonFieldPaths} collapse to `never` without deriving a path for anything. Tests
67
- * the brand rather than the payload, whose extra `Json<infer P>` inference is only worth doing once
68
- * a field is known to be JSON.
38
+ * The JSON columns a dot-path reads into: all but one holding an array (`Json<string[]>`), which has
39
+ * no path. `never` on an entity with none, which is most of them.
69
40
  */
70
41
  type JsonFieldKey<E> = {
71
- readonly [K in keyof E]-?: IsJson<JsonElement<E[K]>> extends true ? K : never;
72
- }[Key<E>];
42
+ readonly [K in JsonColumnKey<E>]: IsMany<JsonPayload<E[K]>> extends true ? never : K;
43
+ }[JsonColumnKey<E>];
73
44
  /**
74
- * Recursively derives dot-notation key paths from a JSON payload type. Handles every shape at
75
- * entry: an untyped (`unknown`) payload accepts any suffix via a `string` pattern, scalars are
76
- * leaves (they contribute no deeper path and self-prune through `` `${K}.${never}` ``), arrays
77
- * contribute their element type's paths, and objects recurse per key up to 5 levels deep
78
- * (deeper suffixes stay accepted via the `string` pattern at the cutoff).
45
+ * The dot-paths into a JSON payload: any suffix on an untyped one, none past a scalar, an array's
46
+ * element's, and an object's keys five levels deep, below which any suffix is accepted.
79
47
  */
80
48
  type DeepJsonKeys<T, D extends unknown[] = []> = unknown extends T ? string : NonNullable<T> extends Scalar ? never : NonNullable<T> extends readonly (infer U)[] ? DeepJsonKeys<U, D> : D['length'] extends 5 ? string : {
81
49
  [K in keyof NonNullable<T> & string]: K | `${K}.${DeepJsonKeys<NonNullable<T>[K], [...D, unknown]>}`;
82
50
  }[keyof NonNullable<T> & string];
83
51
  /**
84
- * Extracts dot-notation paths from `Json<T>` values, handling both scalar JSON
85
- * and arrays of JSON (`Json<{foo: string}>[]`, a column holding a list of documents).
86
- * For `kind?: Json<{ public: number; theme: { color: string } }>`,
87
- * produces `'kind.public' | 'kind.theme' | 'kind.theme.color'`.
88
- * For `items?: Json<{id: string}>[]`, produces `'items.id'`.
89
- * An untyped `Json<unknown>` field yields the scoped pattern `` `${K}.${string}` ``.
52
+ * The dot-paths into an entity's JSON columns: `kind?: Json<{ theme: { color: string } }>` gives
53
+ * `'kind.theme' | 'kind.theme.color'`, `items?: Json<{ id: string }>[]` gives `'items.id'`, and an
54
+ * untyped `Json` gives `` `kind.${string}` ``.
90
55
  */
91
56
  export type JsonFieldPaths<E> = {
92
57
  readonly [K in JsonFieldKey<E>]: `${K & string}.${DeepJsonKeys<JsonPayload<E[K]>>}`;
@@ -96,41 +61,16 @@ export type JsonFieldPaths<E> = {
96
61
  * `Record<string, unknown>` leaf). Arrays are stepped into via their element type.
97
62
  */
98
63
  type PathValue<T, P extends string> = unknown extends T ? unknown : NonNullable<T> extends readonly (infer U)[] ? PathValue<NonNullable<U>, P> : P extends `${infer K}.${infer Rest}` ? K extends keyof NonNullable<T> ? PathValue<NonNullable<T>[K], Rest> : unknown : P extends keyof NonNullable<T> ? NonNullable<T>[P] : unknown;
99
- /**
100
- * The value type at a JSON dot-path `P` of entity `E`; `unknown` when unresolvable, which keeps
101
- * untyped paths fully permissive in `$where`. Gated on {@link JsonFieldKey}, the same predicate
102
- * {@link JsonFieldPaths} derives its keys from, so a path that is offered always resolves a value.
103
- */
64
+ /** The value at a JSON dot-path of `E`, `unknown` where it cannot be resolved, which keeps an untyped path permissive. */
104
65
  export type JsonFieldPathValue<E, P extends string> = P extends `${infer F}.${infer Rest}` ? F extends JsonFieldKey<E> ? PathValue<JsonPayload<E[F]>, Rest> : unknown : unknown;
105
- /**
106
- * Extracts only the array-typed keys from `T`, mapping each to its element type via `Unpacked`.
107
- * Used by `$push` and `$pull` to provide type-safe element targets.
108
- */
66
+ /** The array keys of `T`, each mapped to its element type: what `$push` and `$pull` address. */
109
67
  export type JsonArrayFields<T> = {
110
68
  [K in keyof T as IsMany<T[K]> extends true ? K & string : never]?: Unpacked<NonNullable<T[K]>>;
111
69
  };
112
70
  /**
113
- * Operator shape accepted by JSON/JSONB fields in update payloads: `$set`/`$unset` target object
114
- * keys, `$push`/`$pull` target array elements. All four are type-safe with IDE autocomplete.
115
- *
116
- * `$set` is shallow: it assigns the given top-level keys and leaves the rest untouched (it is not
117
- * an RFC 7396 recursive merge). `$pull` removes *every* element equal to the given value.
118
- *
119
- * Operators are applied `$pull` -> `$set` -> `$push` -> `$unset`, so any combination - including
120
- * `$pull` and `$push` on the same key - yields the same result on every dialect.
121
- *
122
- * @example
123
- * ```ts
124
- * // set only - autocompletes keys from the JSON field's inner type
125
- * querier.updateOneById(Company, id, { kind: { $set: { public: 1 } } });
126
- * // unset only - autocompletes keys from the JSON field's inner type
127
- * querier.updateOneById(Company, id, { kind: { $unset: ['private'] } });
128
- * // append to / remove from an array - autocompletes array keys, value matches element type
129
- * querier.updateOneById(Company, id, { kind: { $push: { tags: 'new-tag' } } });
130
- * querier.updateOneById(Company, id, { kind: { $pull: { tags: 'stale-tag' } } });
131
- * // combine
132
- * querier.updateOneById(Company, id, { kind: { $set: { public: 1 }, $push: { tags: 'x' }, $unset: ['private'] } });
133
- * ```
71
+ * A JSON field's update operators, applied `$pull`, `$set`, `$push`, `$unset` on every engine: `$set`
72
+ * assigns top-level keys (no deep merge), `$pull` removes every equal element. See the JSON guide.
73
+ * @example `{ kind: { $set: { public: 1 }, $push: { tags: 'x' }, $unset: ['private'] } }`
134
74
  */
135
75
  export type JsonUpdateOp<T = unknown> = {
136
76
  readonly $set?: Partial<T>;
@@ -139,53 +79,30 @@ export type JsonUpdateOp<T = unknown> = {
139
79
  readonly $pull?: JsonArrayFields<T>;
140
80
  };
141
81
  /**
142
- * The {@link JsonUpdateOp} a field accepts, or `never` where the operators do not apply:
143
- * - Non-JSON fields. {@link UnwrapJson}'s {@link IsJson} guard avoids the non-discriminating bare
144
- * `Json<infer T>` match that would otherwise offer `$set`/`$unset` on plain scalar fields.
145
- * - `Json<T[]>` payloads. All four operators address object keys of the JSON document, so on an
146
- * array column none is meaningful: PostgreSQL's `||` would concatenate arrays while
147
- * `JSON_SET(arr, '$.k', v)` is a no-op on MySQL and SQLite. Replace the whole value instead.
148
- * `Json<unknown>` stays permissive, since `unknown` is not an array.
82
+ * The {@link JsonUpdateOp} a field takes: `never` on a non-JSON field, and on a JSON array, whose
83
+ * operators would address keys it does not have (engines disagree on what that does).
149
84
  */
150
85
  type JsonUpdateOpFor<V, T = UnwrapJson<NonNullable<V>>> = [T] extends [never] ? never : IsMany<T> extends true ? never : JsonUpdateOp<T>;
86
+ /** What an update takes beyond the value: `null` to clear an optional member, `raw` SQL, and JSON operators. */
87
+ type UpdateExtra<V> = (undefined extends V ? null : never) | QueryRaw | JsonUpdateOpFor<V>;
151
88
  /**
152
- * Accepted value for a single field in an update payload: the value itself, `null` where the column
153
- * is nullable, `QueryRaw` for a raw SQL expression (e.g. ``raw`NOW()` ``), and - for JSON object
154
- * fields - the JSON operators.
155
- *
156
- * An optional property is a nullable column, and clearing one is what an update is for, so `null`
157
- * belongs in the declared type rather than behind a cast.
158
- */
159
- type UpdateFieldValue<V> = V | (undefined extends V ? null : never) | QueryRaw | JsonUpdateOpFor<V>;
160
- /**
161
- * An entity's fields and relations, each keeping its declared optionality: what the whole-record
162
- * writes (`insertOne`, `saveOne`, `upsertOne`, and their `*Many`) persist.
163
- *
164
- * Not `E`: that *demands* back every method the class declares, so on an entity carrying a
165
- * lifecycle hook - `@BeforeInsert() generateSlug()` - a plain `{ title: 'Hello' }` was rejected as
166
- * "missing the following properties". Method-free entities were unaffected, which is why the other
167
- * examples worked. No runtime filter can help; the call never gets that far. `Pick` because it
168
- * stays indexable by `IdKey<E>`, which the write path needs.
169
- */
170
- export type EntityData<E> = Pick<E, FieldKey<E> | RelationKey<E>>;
171
- /**
172
- * Payload type for update operations: {@link EntityData} made partial, and widened per field to
173
- * accept `QueryRaw` or `JsonUpdateOp` (for JSON fields), which gives IDE autocomplete for
174
- * `$set`/`$push`/`$pull` keys via `Json<infer T>`.
89
+ * What a whole-record write persists: the fields and relations with their declared optionality, a
90
+ * related row's alike, and no methods. Two mapped types, since asking each key costs a conditional.
175
91
  */
92
+ export type EntityData<E, F extends keyof E = FieldKey<E>, R extends keyof E = RelationKey<E>> = {
93
+ [P in F]: E[P];
94
+ } & {
95
+ [P in R]: E[P] | RelationData<E[P]>;
96
+ };
97
+ /** A relation's value as its rows' {@link EntityData}. */
98
+ type RelationData<V> = V extends readonly (infer T)[] ? EntityData<T>[] : V extends object ? EntityData<V> : never;
99
+ /** {@link EntityData} made partial, each member also taking its {@link UpdateExtra}. */
176
100
  export type UpdatePayload<E, F extends keyof E = FieldKey<E>, R extends keyof E = RelationKey<E>> = {
177
- [K in F]?: UpdateFieldValue<E[K]>;
101
+ [P in F]?: E[P] | UpdateExtra<E[P]>;
178
102
  } & {
179
- [K in R]?: E[K];
103
+ [P in R]?: E[P] | RelationData<E[P]> | UpdateExtra<E[P]>;
180
104
  };
181
- /**
182
- * Infers the field values of an entity
183
- */
184
- export type FieldValue<E> = E[FieldKey<E>];
185
- /**
186
- * The key's name where the entity states it: the `idKey` brand first, then the conventional names.
187
- * `never` when nothing does, which is the case {@link IdKey} falls back on and `@Id` refuses.
188
- */
105
+ /** The key's name where the entity states it, by the `idKey` brand or a conventional name; `never` otherwise. */
189
106
  export type NamedIdKey<E> = E extends {
190
107
  [idKey]?: infer K;
191
108
  } ? K & FieldKey<E> : E extends {
@@ -195,279 +112,143 @@ export type NamedIdKey<E> = E extends {
195
112
  } ? 'id' & FieldKey<E> : E extends {
196
113
  uuid?: unknown;
197
114
  } ? 'uuid' & FieldKey<E> : never;
198
- /**
199
- * Infers the name of the key identifier on an entity
200
- */
115
+ /** The primary key's name, every field where the entity names none. `& string` for a generic `E`. */
201
116
  export type IdKey<E> = ([NamedIdKey<E>] extends [never] ? FieldKey<E> : NamedIdKey<E>) & string;
202
- /**
203
- * Infers the value of the key identifier on an entity.
204
- *
205
- * A composite key is addressed by an object carrying every key, which is also the `$where` map it
206
- * reduces to - so both spellings are one type. Completeness is checked at run time by
207
- * `assertIdValue`: TypeScript cannot accumulate `@Id` across properties into the class type, so it
208
- * cannot know how many keys there are.
209
- *
210
- * Nullable, because an entity declares its id optional - nothing has assigned one before the
211
- * insert. That puts `undefined` inside every by-id method's parameter, where it would mean "no
212
- * filter"; `assertIdValue` is what rejects it.
213
- */
117
+ /** The primary key's value, optional as the entity declares it: a by-id method refuses a nullish one at run time. */
214
118
  export type IdValue<E> = E[IdKey<E>];
119
+ /** Whether `E`'s primary key spans several columns, which no single column can reference. */
120
+ export type HasCompositeKey<E> = true extends IsUnion<IdKey<E>> ? true : false;
215
121
  /** Every column of a key, which is how a composite row is named and what a `$where` reduces to. */
216
122
  type IdMap<E> = Partial<Pick<E, IdKey<E>>>;
217
123
  /**
218
- * How a row is addressed by its primary key: the value for a single key, an object carrying every
219
- * key for a composite - which is also the `$where` map it reduces to, so both spellings are one type.
220
- *
221
- * A union rather than a choice between the two, because a caller holding one column's value has to
222
- * reach the same parameter as one holding a map. {@link WrittenId} is where a shape is committed to.
223
- *
224
- * The keys stay optional, and completeness is checked at run time by `assertIdValue`: requiring them
225
- * would refuse a `$where` map that names one row while it is still being built up.
124
+ * How a row is addressed by its primary key: the value, or a map carrying every key of a composite,
125
+ * which is also the `$where` it reduces to. Completeness is checked at run time.
226
126
  */
227
127
  export type EntityId<E> = IdValue<E> | IdMap<E>;
228
128
  /** Whether `T` is a union of more than one member, which for a key means the entity's is composite. */
229
129
  type IsUnion<T, U = T> = T extends unknown ? ([U] extends [T] ? false : true) : never;
230
130
  /**
231
- * The id a write reports: the column's value for a single key, the key map for a composite.
232
- *
233
- * Exact where {@link EntityId} is a union, and that is the difference between them - a by-id method
234
- * *accepts* either spelling, a write *commits* to one.
235
- *
236
- * Falls back to `EntityId` where {@link NamedIdKey} names nothing, because there `IdKey` is every
237
- * field and a composite cannot be told from a single key: reporting a map for a scalar would be a
238
- * lie. `@Id` refuses an unnamed key, so a decorated entity is always exact, and `defineEntity` is
239
- * the path where this fallback is still reachable.
131
+ * The id a write reports: the value for a single key, the map for a composite. {@link EntityId} where
132
+ * the entity names no key, since a composite cannot then be told from a single one.
240
133
  */
241
134
  export type WrittenId<E> = [NamedIdKey<E>] extends [never] ? EntityId<E> : IsUnion<IdKey<E>> extends true ? IdMap<E> : IdValue<E>;
242
- /**
243
- * Infers the values of the relations on an entity
244
- */
245
- export type RelationValue<E> = E[RelationKey<E>];
246
- /**
247
- * SQL numeric column types
248
- */
249
- export type NumericColumnType = 'int' | 'integer' | 'tinyint' | 'smallint' | 'bigint' | 'float' | 'float4' | 'float8' | 'double' | 'double precision' | 'decimal' | 'numeric' | 'real';
250
- /**
251
- * SQL string column types
252
- */
253
- export type StringColumnType = 'char' | 'varchar' | 'text' | 'uuid';
254
- /**
255
- * SQL date/time column types
256
- */
257
- export type DateColumnType = 'date' | 'time' | 'datetime' | 'timestamp' | 'timestamptz';
258
- /**
259
- * SQL JSON column types
260
- */
261
- export type JsonColumnType = 'json' | 'jsonb';
262
- /**
263
- * SQL binary/blob column types
264
- */
265
- export type BlobColumnType = 'blob' | 'bytea';
266
- /**
267
- * SQL boolean column types
268
- */
269
- export type BooleanColumnType = 'bool' | 'boolean';
270
- /**
271
- * SQL vector column types
272
- */
273
- export type VectorColumnType = 'vector' | 'halfvec' | 'sparsevec';
274
- /**
275
- * SQL column types supported by uql migrations
276
- */
277
- export type ColumnType = NumericColumnType | StringColumnType | DateColumnType | JsonColumnType | BlobColumnType | BooleanColumnType | VectorColumnType;
278
- /**
279
- * Logical types for a field
280
- */
135
+ /** Every SQL column type a field may declare, by family: the unions below and `columnFamily` both read it. */
136
+ export declare const COLUMN_TYPES: {
137
+ readonly numeric: readonly ['int', 'integer', 'tinyint', 'smallint', 'bigint', 'float', 'float4', 'float8', 'double', 'double precision', 'decimal', 'numeric', 'real'];
138
+ readonly string: readonly ['char', 'varchar', 'text', 'uuid'];
139
+ readonly date: readonly ['date', 'time', 'datetime', 'timestamp', 'timestamptz'];
140
+ readonly json: readonly ['json', 'jsonb'];
141
+ readonly blob: readonly ['blob', 'bytea'];
142
+ readonly boolean: readonly ['bool', 'boolean'];
143
+ readonly vector: readonly ['vector', 'halfvec', 'sparsevec'];
144
+ };
145
+ /** The kind of column a field lands on, which decides whether an option means anything on it. */
146
+ export type ColumnFamily = keyof typeof COLUMN_TYPES;
147
+ type ColumnTypeOf<F extends ColumnFamily> = (typeof COLUMN_TYPES)[F][number];
148
+ export type NumericColumnType = ColumnTypeOf<'numeric'>;
149
+ export type StringColumnType = ColumnTypeOf<'string'>;
150
+ export type DateColumnType = ColumnTypeOf<'date'>;
151
+ export type JsonColumnType = ColumnTypeOf<'json'>;
152
+ export type BlobColumnType = ColumnTypeOf<'blob'>;
153
+ export type BooleanColumnType = ColumnTypeOf<'boolean'>;
154
+ export type VectorColumnType = ColumnTypeOf<'vector'>;
155
+ /** SQL column types supported by uql migrations. */
156
+ export type ColumnType = ColumnTypeOf<ColumnFamily>;
157
+ type ColumnTypeFamily<T> = {
158
+ [F in ColumnFamily]: T extends ColumnTypeOf<F> ? F : never;
159
+ }[ColumnFamily];
160
+ /** The family a declared `type` puts a column in, or every family where it names none. */
161
+ export type FamilyOf<T> = T extends ColumnType ? ColumnTypeFamily<T> : T extends NumberConstructor | BigIntConstructor ? 'numeric' : T extends StringConstructor ? 'string' : T extends DateConstructor ? 'date' : T extends BooleanConstructor ? 'boolean' : ColumnFamily;
162
+ /** What a field declares its `type` as: a constructor, or a column type. */
281
163
  export type FieldType = StringConstructor | NumberConstructor | BooleanConstructor | DateConstructor | BigIntConstructor | ColumnType;
282
164
  /**
283
- * The {@link FieldType} values legal for a field declared as `V`.
284
- *
285
- * This is what makes an explicit `type` an improvement over the reflected one it replaces: the
286
- * annotation is checked against the property's real TypeScript type, so `@Field({ type: String })` on
287
- * a `number` no longer compiles into a silent TEXT column. `unknown` shapes fall through to the full
288
- * {@link FieldType}, keeping genuinely untyped fields usable.
289
- *
290
- * JSON is matched on the `__json` brand rather than structurally, because {@link Json} intersects its
291
- * payload (`Json<string>` really does extend `string`) and would otherwise land on the string arm.
292
- * Both `Json<T>` and `Json<T>[]` have to be recognised, and the array check has to precede the scalar
293
- * arms so a `number[]` vector is not read as a `number`.
165
+ * The {@link FieldType}s legal for a field declared as `V`, so `type: String` on a `number` does not
166
+ * compile. JSON is matched on its brand, which a `Json<string>` shares with `string`, and arrays before
167
+ * scalars, so a vector is not read as a `number`.
294
168
  */
295
169
  export type TypeFor<V, T = NonNullable<V>> = IsJsonColumn<T> extends true ? JsonColumnType : T extends readonly number[] ? VectorColumnType : T extends string ? StringConstructor | StringColumnType : T extends number ? NumberConstructor | NumericColumnType : T extends bigint ? BigIntConstructor | NumericColumnType : T extends boolean ? BooleanConstructor | BooleanColumnType : T extends Date ? DateConstructor | DateColumnType : T extends Uint8Array ? BlobColumnType : FieldType;
296
- /**
297
- * A field as the registry holds it: what the user authored, plus what registration worked out.
298
- *
299
- * Separate from {@link FieldOptions} so neither of these can be written in a decorator. They used to
300
- * live there behind an `@internal` tag and a "do not set this" note, which is a comment standing in
301
- * for a type boundary.
302
- */
170
+ /** A field as the registry holds it: what was authored, plus what registration worked out, which no decorator can write. */
303
171
  export type FieldMeta<V = TsTypeOf<FieldType>> = Except<FieldOptions<V>, 'computed'> & {
304
172
  /** {@link FieldOptions.computed}, a callback resolved to the SQL it returns. */
305
173
  readonly computed?: QueryRaw;
306
- /**
307
- * Set by `defineField` when the field gave `references` but no `type`, so schema generation resolves
308
- * the column from the referenced primary key rather than from whatever ended up in `type`. That is
309
- * what keeps a `uuid` primary key from becoming TEXT on every foreign key pointing at it.
310
- */
174
+ /** Whether the column type comes from the referenced key, where the field gave `references` but no `type`. */
311
175
  readonly typeFromReference?: boolean;
312
- /**
313
- * Which key of the referenced entity this column points at, where that entity has more than one.
314
- * Set by `fillOwningSide`; without it a composite target's columns would all take the first key's type.
315
- */
316
- readonly referencedKey?: string;
317
176
  };
318
- /**
319
- * Configurable options for a field, carrying `V`, the value the column holds: what a generator returns
320
- * and what a default is has to be that value, checked the same way the declared `type` is. `Scalar` by
321
- * default, for the places that handle a field without knowing which one it is.
322
- */
177
+ /** A field's options, checked against `V`, the value the column holds: every scalar where the field is unknown. */
323
178
  export type FieldOptions<V = TsTypeOf<FieldType>, E = unknown> = {
324
179
  readonly name?: string;
325
180
  readonly isId?: true;
326
181
  readonly type?: FieldType;
327
- /**
328
- * Dimensions for vector fields. Used in schema generation.
329
- * @example `@Field({ type: 'vector', dimensions: 1536 })`
330
- */
182
+ /** A vector column's dimensions: `@Field({ type: 'vector', dimensions: 1536 })`. */
331
183
  readonly dimensions?: number;
332
- /**
333
- * Default distance metric for vector similarity queries on this field.
334
- * Queries can override via `$distance`. Defaults to `'cosine'` if omitted.
335
- * @example `@Field({ type: 'vector', dimensions: 1536, distance: 'cosine' })`
336
- */
184
+ /** The metric a vector search on this field uses unless it names its own `$distance`; `'cosine'` by default. */
337
185
  readonly distance?: VectorDistance;
338
- /**
339
- * Entity that this field references (for foreign keys).
340
- */
186
+ /** The entity this column is a foreign key to. */
341
187
  readonly references?: EntityGetter;
342
188
  /**
343
- * Referential action for the generated foreign key. Delete side only: `onUpdate` below already means
344
- * a value callback. Reach for `@ManyToOne({ onDelete, onUpdate })` when the update side matters too, or
345
- * when this disagrees with a relation also declared on the same column (the relation wins).
346
- * @example `@Field({ references: () => Company, onDelete: 'CASCADE' }) companyId?: string;`
189
+ * The foreign key's delete action, `@Field({ references: () => Company, onDelete: 'CASCADE' })`. A
190
+ * relation over the column wins, and is where the update action goes: `onUpdate` here is a value callback.
347
191
  */
348
192
  readonly onDelete?: ForeignKeyAction;
349
193
  /**
350
- * The values the column accepts, enforced by the database as well as by TypeScript.
351
- *
352
- * Emitted as a column `CHECK (col IN (...))` on every SQL dialect rather than a native enum type:
353
- * one code path, no separate schema object to order, and adding a value stays an ordinary column
354
- * change instead of Postgres's irreversible `ALTER TYPE ... ADD VALUE`.
355
- *
356
- * Not constrained against the field's own type here: the decorator narrows the property to these
357
- * values instead, which reports a mismatch where the mistake is rather than as an unrelated
358
- * `never`. `as const` is what makes them literal, and so what makes any of it check.
359
- *
360
- * @example `@Field({ type: String, enum: ['draft', 'paid'] as const })`
194
+ * The values the column accepts, `enum: ['draft', 'paid'] as const`: a `CHECK (col IN (...))` on every
195
+ * SQL engine, and the property's type through the decorator, which is why they have to be `as const`.
361
196
  */
362
197
  readonly enum?: EnumValues;
363
198
  /**
364
- * An expression the database computes, rather than a value the caller writes. Never part of an
365
- * insert or update either way.
366
- *
367
- * Unstored, it is spliced into each statement that reads the field, so nothing is persisted and any
368
- * expression will do. With `stored`, it becomes a real column - `GENERATED ALWAYS AS (...) STORED` -
369
- * which the engine keeps up to date, so it can be indexed and read like any other.
370
- *
371
- * @example `@Field({ type: String, computed: (user) => raw`${user.first} || ' ' || ${user.last}`, stored: true })`
199
+ * An expression the database computes, never written: spliced into each read, or with `stored` a
200
+ * generated column, `computed: (user) => raw`${user.first} || ' ' || ${user.last}``.
372
201
  */
373
202
  readonly computed?: EntitySql<E>;
374
- /**
375
- * Whether {@link FieldOptions.computed} is a column the database keeps, rather than an expression
376
- * spliced into each statement. The dial to flip after profiling: `$select`, `$where` and `$sort`
377
- * read the field the same way either side of it, so no call site changes.
378
- */
203
+ /** Whether {@link FieldOptions.computed} is a generated column rather than spliced into each read; no query changes either way. */
379
204
  readonly stored?: boolean;
380
205
  readonly updatable?: boolean;
381
206
  readonly eager?: boolean;
382
207
  readonly onInsert?: OnFieldCallback<V>;
383
208
  readonly onUpdate?: OnFieldCallback<V>;
384
209
  /**
385
- * Marks this field as the soft-delete field. Its presence makes the entity "soft deletable":
386
- * a `delete` becomes an `UPDATE` that stamps this field instead of removing the row, and reads
387
- * filter it out (`<field> IS NULL`). An entity may have at most one soft-delete field.
388
- *
389
- * The value controls what is stamped on delete: `true` stamps the current timestamp
390
- * (`new Date()`); any other `Scalar`/`QueryRaw` or `() => Scalar | QueryRaw` callback stamps
391
- * that value (e.g. `() => Date.now()` for an epoch-millis column).
392
- * @example `@Field({ softDelete: true }) deletedAt?: Date;`
393
- * @example `@Field({ softDelete: () => Date.now() }) deletedAt?: number;`
210
+ * Makes a delete stamp this field instead of removing the row, and reads skip stamped rows. `true`
211
+ * stamps `new Date()`, anything else is the value or callback stamped, `softDelete: () => Date.now()`.
394
212
  */
395
213
  readonly softDelete?: true | OnFieldCallback<V>;
396
- /**
397
- * SQL column type for migrations. If not specified, inferred from TypeScript type.
398
- */
214
+ /** The SQL type, where it differs from the one `type` implies: `type: String, columnType: 'decimal'`. */
399
215
  readonly columnType?: ColumnType;
400
- /**
401
- * Field length (e.g. for varchar)
402
- */
216
+ /** A string column's length. */
403
217
  readonly length?: number;
404
- /**
405
- * Field precision (e.g. for decimal)
406
- */
218
+ /** A decimal column's precision. */
407
219
  readonly precision?: number;
408
- /**
409
- * Field scale (e.g. for decimal)
410
- */
220
+ /** A decimal column's scale. */
411
221
  readonly scale?: number;
412
- /**
413
- * Whether the field is nullable
414
- */
415
222
  readonly nullable?: boolean;
416
- /**
417
- * Whether the field is unique
418
- */
419
223
  readonly unique?: boolean;
420
- /**
421
- * The column's DDL default, rendered into `CREATE TABLE` by `formatDefaultValue`.
422
- */
224
+ /** The column's DDL default. */
423
225
  readonly defaultValue?: DdlDefault<V>;
424
- /**
425
- * Whether the column is auto-incrementing (for integer IDs).
426
- */
226
+ /** Whether the database generates the value; a numeric sole key does unless something else fills it. */
427
227
  readonly autoIncrement?: boolean;
428
228
  /**
429
229
  * `true` for an index over the column, a string to name it. A foreign key column is indexed unless
430
230
  * this is `false`.
431
231
  */
432
232
  readonly index?: boolean | string;
433
- /**
434
- * Column comment/description for database documentation.
435
- */
233
+ /** The column's comment in the database. */
436
234
  readonly comment?: string;
437
235
  };
438
236
  export type OnFieldCallback<V = TsTypeOf<FieldType>> = V | QueryRaw | (() => V | QueryRaw);
439
237
  /**
440
- * What a column may default to: the value it holds, except on a JSON column, which defaults with the
441
- * SQL literal it stores (`defaultValue: '{}'`) whatever the property's TypeScript type is. Opening
442
- * that exception to every field is what let `@Field({ type: Number, defaultValue: 'hello' })` compile.
443
- *
444
- * The erased shape - `FieldOptions` with no field in mind - admits every column's default at once, or
445
- * no `FieldOptions<V>` would be assignable to the one the registry and the dialects read.
238
+ * What a column may default to: its value, or on a JSON column the SQL literal it stores, `'{}'`. The
239
+ * erased `FieldOptions` takes both, so every field's options stay assignable to it.
446
240
  */
447
241
  type DdlDefault<V, T = NonNullable<V>> = IsJsonColumn<T> extends true ? JsonDdlDefault : [TsTypeOf<FieldType>] extends [T] ? JsonDdlDefault | T : T;
448
242
  /** What a JSON column, and the field-less `FieldOptions`, may default to. */
449
243
  type JsonDdlDefault = Scalar | Record<string, unknown>;
450
244
  /**
451
- * The TypeScript types a field may be declared as, given the `type` it registers: the inverse of
452
- * {@link TypeFor}.
453
- *
454
- * Both directions are needed because they are consumed at opposite ends. `defineEntity` keys its bulk
455
- * `fields` by property name, so the property's type is already known and {@link TypeFor} narrows the
456
- * `type` allowed. A decorator has it the other way round: `@Field({ type: String })` is checked before
457
- * the class exists, so the only way to reach the property is to state what `type: String` implies and
458
- * let the decorator's context position compare it against the real field. Neither can be derived from
459
- * the other by inference, so `entityOptions.test-d.ts` asserts they agree instead.
245
+ * The TypeScript type a declared `type` implies, the inverse of {@link TypeFor}: a decorator checks the
246
+ * property against it, `defineEntity` the other way round. `entityOptions.test-d.ts` keeps them agreeing.
460
247
  */
461
248
  export type TsTypeOf<T> = T extends StringConstructor ? string : T extends NumberConstructor ? number : T extends BigIntConstructor ? bigint : T extends BooleanConstructor ? boolean : T extends DateConstructor ? Date : T extends StringColumnType ? string : T extends NumericColumnType ? number | bigint : T extends BooleanColumnType ? boolean : T extends DateColumnType ? Date : T extends JsonColumnType ? Json<unknown> | readonly Json<unknown>[] : T extends BlobColumnType ? Uint8Array : T extends VectorColumnType ? readonly number[] : unknown;
462
249
  /**
463
- * {@link FieldOptions} for a field declared as `V`, with `type` required and checked by
464
- * {@link TypeFor}.
465
- *
466
- * The second arm is load-bearing rather than a convenience: a foreign-key column may omit `type` so
467
- * that schema generation resolves it from the referenced primary key instead, picking up that key's
468
- * `columnType`, length and chained references. Forcing `type: Number` onto
469
- * `@Field({ references: () => Company })` would silently downgrade a `uuid` key to TEXT on every
470
- * column pointing at it.
250
+ * {@link FieldOptions} for a field declared as `V`, `type` checked by {@link TypeFor}. A foreign key may
251
+ * omit it, taking the referenced key's column type instead.
471
252
  */
472
253
  export type FieldOptionsFor<V, E = unknown> = (FieldOptions<NonNullable<V>, E> & {
473
254
  readonly type: TypeFor<V>;
@@ -475,15 +256,11 @@ export type FieldOptionsFor<V, E = unknown> = (FieldOptions<NonNullable<V>, E> &
475
256
  readonly references: EntityGetter;
476
257
  readonly type?: TypeFor<V>;
477
258
  });
478
- /**
479
- * The entity a relation field points at: `Company` for both `company?: Company` and
480
- * `companies?: Company[]`.
481
- */
259
+ /** The entity a relation points at: `Company` for `company?: Company` and `companies?: Company[]` alike. */
482
260
  export type RelationTarget<V> = Extract<Unpacked<V>, object>;
483
261
  /**
484
- * {@link RelationOptions} for a relation field declared as `V`: what each cardinality's decorator takes,
485
- * keyed on the `cardinality` written and restricted to the ones the field's shape holds. A key, not a
486
- * conditional on `IsMany<V>`, which left a `mappedBy` callback untyped inside `defineEntity`.
262
+ * {@link RelationOptions} for a relation declared as `V`, keyed on the `cardinality` its shape allows: a
263
+ * key rather than a conditional, which is what types a `mappedBy` callback inside `defineEntity`.
487
264
  */
488
265
  export type RelationOptionsFor<V, O = unknown> = {
489
266
  readonly cardinality: IsMany<V> extends true ? '1m' | 'mm' : '11' | 'm1';
@@ -496,20 +273,13 @@ export type RelationOptionsFor<V, O = unknown> = {
496
273
  } & RelationOneToManyOptions<RelationTarget<V>, O>) | ({
497
274
  readonly cardinality: 'mm';
498
275
  } & RelationManyToManyOptions<RelationTarget<V>, O>));
499
- /**
500
- * The method names of an entity, so hook registrations name a method that exists.
501
- */
276
+ /** The method names of an entity, which a hook registration names. */
502
277
  export type MethodKey<E> = {
503
278
  readonly [K in keyof E]-?: NonNullable<E[K]> extends (...args: never[]) => unknown ? K : never;
504
279
  }[Key<E>];
505
280
  /**
506
- * A deferred reference to an entity class, e.g. `() => Company`.
507
- *
508
- * A getter rather than the class itself because decorator expressions are evaluated while the class is
509
- * being defined, before its binding is initialized, so naming the class directly is a `ReferenceError`
510
- * for a self-reference and for whichever side of a circular import is evaluated first - the two shapes an
511
- * entity graph almost always has. Nothing about the standard decorator spec changes that; it only removed
512
- * the reflected `design:type` that used to make `entity` optional.
281
+ * An entity class read later, `() => Company`: a decorator runs before its class is bound, so a
282
+ * self-reference or a circular import would otherwise throw.
513
283
  */
514
284
  export type EntityGetter<E = object> = () => Type<E>;
515
285
  export type CascadeType = 'persist' | 'delete';
@@ -522,53 +292,43 @@ export type RelationOptions<E, O = unknown> = {
522
292
  cardinality: RelationCardinality;
523
293
  readonly cascade?: boolean | CascadeType;
524
294
  /**
525
- * Referential actions for the generated foreign key, letting the database cascade instead of the ORM's
526
- * `cascade` (pick one; declaring both leaves the FK nothing to do). Read from the owning side
527
- * (`@ManyToOne`, or a `@OneToOne` without `mappedBy`). `onDelete` falls back to the FK field's own
528
- * `@Field({ onDelete })` when unset here; `onUpdate` has no such fallback since that key already means
529
- * a value callback on `FieldOptions`.
295
+ * The foreign key's delete action, the database's alternative to `cascade`, read on the owning side;
296
+ * unset, the column's own `@Field({ onDelete })` applies.
530
297
  */
531
298
  readonly onDelete?: ForeignKeyAction;
532
299
  readonly onUpdate?: ForeignKeyAction;
533
300
  /** The inverse side: the member of the target holding the foreign key or the owning relation, `(post) => post.author`. */
534
- mappedBy?: (keys: KeyMap<E>) => Key<E>;
535
- /**
536
- * The pivot entity of a many-to-many. Unconstrained by `E`: a pivot holds foreign keys to both
537
- * sides and is not a relation value of the target, so nothing about it is derivable from `E`.
538
- */
301
+ mappedBy?: (keys: KeyMap<E>) => RelationKey<E> | ForeignKey<E, O>;
302
+ /** The junction entity of a many-to-many, holding a foreign key to each side. */
539
303
  through?: EntityGetter;
540
304
  /**
541
- * The join columns: the foreign key a to-one declares, `(post) => post.authorId`, which points at the
542
- * target's primary key, or pairs where no key fits, `(order, customer) => [{ local: order.customerCode,
543
- * foreign: customer.code }]`. A `through` relation takes none: it joins by the junction's column
544
- * referencing each side.
305
+ * The join columns: a to-one's foreign key, `(post) => post.authorId`, or pairs where no key fits,
306
+ * `(order, customer) => [{ local: order.customerCode, foreign: customer.code }]`. Not with `through`.
545
307
  */
546
- references?: (local: KeyMap<O>, foreign: KeyMap<E>) => FieldKey<O> | readonly RelationReference<O, E>[];
308
+ references?: (local: KeyMap<O>, foreign: KeyMap<E>) => ForeignKey<O, E> | readonly RelationReference<O, E>[];
547
309
  };
548
- /** One pair of join columns, each a field read off its entity's key map. */
310
+ /** The one column of `O` that can be a foreign key to `E`: a field holding `E`'s key, which has to be a single one. */
311
+ type ForeignKey<O, E> = HasCompositeKey<E> extends true ? never : FieldKeyHolding<O, IdValue<E>>;
312
+ /** One pair of join columns, each a field read off its entity's key map, the local one holding the foreign's value. */
549
313
  export type RelationReference<O, E> = {
550
- readonly local: FieldKey<O>;
551
- readonly foreign: FieldKey<E>;
552
- };
314
+ readonly [F in keyof E]-?: {
315
+ readonly local: FieldKeyHolding<O, E[F]>;
316
+ readonly foreign: F;
317
+ };
318
+ }[FieldKey<E>];
319
+ /** The fields of `O` that can hold any value `V` takes. */
320
+ type FieldKeyHolding<O, V> = {
321
+ readonly [K in keyof O]-?: [NonNullable<V>] extends [NonNullable<O[K]>] ? K : never;
322
+ }[FieldKey<O>];
553
323
  /** {@link RelationOptions.references} as pairs alone, for a to-many, which holds no foreign key of its own to name. */
554
324
  type RelationReferencePairs<E, O> = (local: KeyMap<O>, foreign: KeyMap<E>) => readonly RelationReference<O, E>[];
555
- /**
556
- * A relation once `getMeta` has resolved it: `references` is filled in and `mappedBy` is the key its
557
- * callback named. Consumers read this shape rather than {@link RelationOptions}, so they need no
558
- * assertions - `fillRelations` establishes the invariant once, and throws where it cannot.
559
- *
560
- * `entity` and `through` stay {@link EntityGetter}s. Resolution could call them once and store the class,
561
- * but only by keeping the authored relations in a second map: it settles them in place, reading them across
562
- * entities not resolved yet, so the authored and the settled shape have to be one object. A phase-split
563
- * metadata map costs more than the call parentheses it saves.
564
- */
325
+ /** A relation once `getMeta` resolved it: `references` settled into pairs and `mappedBy` a name. */
565
326
  export type RelationMeta = Omit<RelationRegistration, 'references'> & {
566
327
  references: RelationReferences;
567
328
  };
568
329
  /**
569
- * A relation as the registry takes it, whichever entity it targets: `mappedBy` and `references` read
570
- * off their key maps down to the names they give. `references` stays unset, or the one column a to-one
571
- * names, until `getMeta` pairs it with the target's key, which registration may run before the target has.
330
+ * A relation as the registry takes it: `mappedBy` and `references` read down to names, `references` a
331
+ * single column until `getMeta` pairs it with the target's key.
572
332
  */
573
333
  export type RelationRegistration = Omit<RelationOptions<object>, 'mappedBy' | 'references'> & {
574
334
  mappedBy?: string;
@@ -581,38 +341,23 @@ type RelationOwnerJoin<E, O> = (Required<Pick<RelationOptions<E, O>, 'through'>>
581
341
  readonly references: RelationReferencePairs<E, O>;
582
342
  readonly through?: never;
583
343
  };
584
- type RelationOptionsOwner<E, O> = Pick<RelationOptions<E, O>, 'entity' | 'references' | 'cascade' | 'onDelete' | 'onUpdate'>;
585
- type RelationOptionsInverseSide<E> = Pick<RelationOptions<E>, 'entity' | 'cascade'> & Required<Pick<RelationOptions<E>, 'mappedBy'>>;
344
+ /** The side holding the foreign key, which `references` names and the actions attach to. */
345
+ type RelationOptionsOwner<E, O> = Pick<RelationOptions<E, O>, 'entity' | 'cascade' | 'onDelete' | 'onUpdate'> & Required<Pick<RelationOptions<E, O>, 'references'>>;
346
+ type RelationOptionsInverseSide<E, O> = Pick<RelationOptions<E, O>, 'entity' | 'cascade'> & Required<Pick<RelationOptions<E, O>, 'mappedBy'>>;
586
347
  type RelationOptionsThroughOwner<E, O> = Pick<RelationOptions<E, O>, 'entity' | 'cascade'> & RelationOwnerJoin<E, O>;
587
- /**
588
- * The key names of `E` as values, so a definition reads a member off it - `(post) => post.author` -
589
- * and follows a rename. Homomorphic in `E`, which is what keeps that link, and `-?` so an optional
590
- * member still names itself. At runtime one `Proxy` answering its own key serves every entity.
591
- */
348
+ /** The key names of `E` as values, so a definition reads a member off it, `(post) => post.author`, and follows a rename. */
592
349
  export type KeyMap<E> = {
593
350
  readonly [K in keyof E]-?: K;
594
351
  };
595
- /**
596
- * The fields of `E` as {@link ColumnRef}s, for SQL that names them: `refs(User)` in a statement, the
597
- * callback's parameter in a definition. Keyed over a type parameter constrained to `keyof E`, as
598
- * {@link KeyMap} is over `keyof E`, which keeps each ref linked to its field for rename.
599
- */
352
+ /** The fields of `E` as {@link ColumnRef}s, for SQL that names them: `refs(User)`, or a definition's callback. */
600
353
  export type RefMap<E, F extends keyof E = FieldKey<E>> = {
601
354
  readonly [K in F]-?: ColumnRef<K & string>;
602
355
  };
603
- /**
604
- * SQL a definition writes: `raw`, or a callback reading the entity's fields off its refs. The callback is
605
- * declared as a method, bivariant in its refs, so one typed for its entity still fits where the entity is
606
- * erased: the registry, which resolves it.
607
- */
356
+ /** SQL a definition writes: `raw`, or a callback reading the fields off its refs, bivariant so the registry can hold it. */
608
357
  export type EntitySql<E> = QueryRaw | {
609
358
  sql(refs: RefMap<E>): QueryRaw;
610
359
  }['sql'];
611
- /**
612
- * A predicate DDL carries, over the entity's own fields: a relation, full-text search and a sub-query
613
- * have nothing a `CHECK` or a partial index can hold. An intersection rather than `Except`, which would
614
- * remap the keys and lose each one's link to its field.
615
- */
360
+ /** A predicate DDL can hold: the entity's own fields, without a relation, `$text` or a sub-query. */
616
361
  export type EntityPredicate<E> = QueryWhere<E> & {
617
362
  readonly [K in RelationKey<E>]?: never;
618
363
  } & {
@@ -629,32 +374,19 @@ export type RelationReferences = {
629
374
  readonly foreign: string;
630
375
  }[];
631
376
  export type RelationCardinality = '11' | 'm1' | '1m' | 'mm';
632
- export type RelationOneToOneOptions<E, O = unknown> = RelationOptionsOwner<E, O> | RelationOptionsInverseSide<E>;
633
- export type RelationOneToManyOptions<E, O = unknown> = RelationOptionsInverseSide<E> | RelationOptionsThroughOwner<E, O>;
377
+ export type RelationOneToOneOptions<E, O = unknown> = RelationOptionsOwner<E, O> | RelationOptionsInverseSide<E, O>;
378
+ export type RelationOneToManyOptions<E, O = unknown> = RelationOptionsInverseSide<E, O> | RelationOptionsThroughOwner<E, O>;
634
379
  export type RelationManyToOneOptions<E, O = unknown> = RelationOptionsOwner<E, O>;
635
- export type RelationManyToManyOptions<E, O = unknown> = RelationOptionsThroughOwner<E, O> | RelationOptionsInverseSide<E>;
636
- /**
637
- * Lifecycle hook event names.
638
- */
380
+ export type RelationManyToManyOptions<E, O = unknown> = RelationOptionsThroughOwner<E, O> | RelationOptionsInverseSide<E, O>;
381
+ /** The lifecycle events. An upsert has its own pair: which branch a row takes is the database's to decide. */
639
382
  export type HookEvent = 'beforeInsert' | 'afterInsert' | 'beforeUpdate' | 'afterUpdate' | 'beforeUpsert' | 'afterUpsert' | 'beforeDelete' | 'afterDelete' | 'afterLoad';
640
- /**
641
- * A registered hook: the method name on the entity class to call.
642
- */
383
+ /** A registered hook: the entity's method to call. */
643
384
  export type HookRegistration = {
644
385
  readonly methodName: string;
645
386
  };
646
387
  /**
647
- * Index type paired with the metric it needs. `distance` is required for {@link VectorIndexType}
648
- * because omitting it changes the DDL semantics silently: MariaDB's `DISTANCE=` defaults to
649
- * euclidean (so a cosine query full-scans instead of using the index) and pgvector has no default
650
- * operator class. MongoDB's `vectorSearch` is excluded - its generator emits no metric at all, so
651
- * requiring one would demand a value that is dropped.
652
- *
653
- * The non-vector arm forbids `distance` (rather than just omitting it) because `VectorIndexOptions`
654
- * - intersected in below by {@link EntityIndexMeta} - already declares `distance` as optional, for
655
- * the sake of the migration/introspection schema types that reuse it without this discriminated
656
- * `type`/`distance` pairing. Without the explicit `never` here, that optional `distance` would
657
- * survive the intersection and silently typecheck `{ type: 'btree', distance: 'cosine' }`.
388
+ * An index type with the metric it needs: a vector index has to name one, since engines default to
389
+ * a different one than the queries use, and any other index names none.
658
390
  */
659
391
  export type IndexTypeOptions = {
660
392
  type: VectorIndexType;
@@ -663,22 +395,14 @@ export type IndexTypeOptions = {
663
395
  type?: Exclude<IndexType, VectorIndexType>;
664
396
  distance?: never;
665
397
  };
666
- /**
667
- * One index entry as the migration builder takes it: a column name, `raw` for an expression, or an object
668
- * when the entry needs more. An entity's entries are this too, which is what `normalizeIndexColumn` reads.
669
- */
398
+ /** One index entry as the migration builder takes it: a column name, `raw`, or an object when it needs more. */
670
399
  export type IndexColumnInput = string | QueryRaw | EntityIndexColumn;
671
400
  /**
672
- * One entry of an entity's index, read off its refs: a column, `raw` for an expression, or an object when
673
- * the entry needs more, a JSON entry's path checked against its column.
401
+ * One entry of an entity's index, read off its refs: a column, `raw`, or an object when it needs more.
674
402
  * @example `@Index((post) => [post.tenantId, { column: post.createdAt, order: 'desc' }, raw`lower(${post.email})`])`
675
403
  */
676
404
  export type EntityIndexColumnInput<E> = QueryRaw | IndexColumnOptions | IndexJsonColumnOptions<E>;
677
- /**
678
- * The JSON entries, one arm per JSON field, each `path` checked against the payload of the column its own
679
- * entry names: a misspelled path still builds a valid index that no query matches. On a column that is
680
- * the array (`Json<string[]>`), `jsonArray`'s path resolves to `never`, so it can only be omitted.
681
- */
405
+ /** A JSON entry, its `path` checked against its own column's payload: a misspelled one builds an index nothing uses. */
682
406
  type IndexJsonColumnOptions<E> = {
683
407
  [K in JsonColumnKey<E>]: IndexColumnPlainModifiers & {
684
408
  readonly column: ColumnRef<K & string>;
@@ -690,37 +414,15 @@ type IndexJsonColumnOptions<E> = {
690
414
  readonly jsonPath?: never;
691
415
  });
692
416
  }[JsonColumnKey<E>];
693
- /**
694
- * The JSON columns an index can address, which is a wider set than {@link JsonFieldKey}: that one
695
- * unwraps arrays to find the brand, so a column that *is* an array (`Json<string[]>`) reads as a
696
- * plain one - right for `$where`, which has no path into it, and wrong for `jsonArray`, whose whole
697
- * subject is that column.
698
- */
699
- type JsonColumnKey<E> = {
700
- readonly [K in keyof E]-?: IsJsonColumn<NonNullable<E[K]>> extends true ? K : never;
701
- }[Key<E>];
702
- /** The payload a path is checked against: the column's own brand, or that of the documents it holds. */
703
- type JsonColumnPayload<V> = IsJson<NonNullable<V>> extends true ? UnwrapJson<NonNullable<V>> : JsonPayload<V>;
704
- /**
705
- * A JSON modifier with its `path` narrowed to the ones that column's payload actually has. Everything
706
- * else - and `path`'s own optionality, which `jsonArray` needs and `jsonPath` does not - is taken
707
- * from the declared type rather than restated, so a property added to either cannot miss the checked
708
- * form.
709
- */
417
+ /** A JSON modifier with its `path` narrowed to the column's payload, everything else taken from `T` as declared. */
710
418
  type WithCheckedPath<T extends {
711
419
  path?: string;
712
420
  }, E, K extends Key<E>> = Except<T, 'path' & keyof T> & {
713
- [P in keyof Pick<T, Extract<keyof T, 'path'>>]: DeepJsonKeys<JsonColumnPayload<E[K]>>;
421
+ [P in keyof Pick<T, Extract<keyof T, 'path'>>]: DeepJsonKeys<JsonPayload<E[K]>>;
714
422
  };
715
- /**
716
- * What an index entry can carry besides the thing being indexed. Shared with the normalized
717
- * `IndexColumnSchema`, so the authored and internal shapes cannot drift apart.
718
- */
423
+ /** What an index entry carries besides its column, shared by the authored and the normalized entry. */
719
424
  export type IndexColumnModifiers = {
720
- /**
721
- * Index only the first `n` characters. MySQL and MariaDB *require* this to index a `TEXT`/`BLOB`
722
- * column at all ("used in key specification without a key length"); no other engine accepts it.
723
- */
425
+ /** Index only the first `n` characters, which the MySQL family requires on a `TEXT` or `BLOB` column. */
724
426
  readonly length?: number;
725
427
  /** Stored sort order, which lets `ORDER BY ... DESC` pagination use the index. */
726
428
  readonly order?: 'asc' | 'desc';
@@ -734,18 +436,9 @@ export type IndexColumnModifiers = {
734
436
  readonly jsonArray?: IndexJsonArray;
735
437
  };
736
438
  /**
737
- * An index over one path inside a JSON column, compiled by the same code a `$where` on that path is:
738
- * an expression index is matched by its own text, so an index spelled even slightly differently is
739
- * one the planner never reaches for.
740
- *
741
- * `type` picks the reading the way an operand's own type does (`jsonCompareMode`): compared as a
742
- * number, indexed as a number. Which engines have it is `IndexFeature`'s `jsonPath`.
743
- *
744
- * @example
745
- * ```ts
746
- * @Index((user) => [{ column: user.kind, jsonPath: { path: 'theme.color', type: String } }]) // 'kind.theme.color': 'red'
747
- * @Index((user) => [{ column: user.kind, jsonPath: { path: 'rating', type: Number } }]) // 'kind.rating': { $gte: 4 }
748
- * ```
439
+ * An index over a path inside a JSON column, spelled as the `$where` on it compiles, which is how the
440
+ * planner matches the two. `type` is how the queries compare it.
441
+ * @example `@Index((user) => [{ column: user.kind, jsonPath: { path: 'rating', type: Number } }])`
749
442
  */
750
443
  export type IndexJsonPath = {
751
444
  /** The path inside the column, spelled as a `$where` key spells it: `'theme.color'`. */
@@ -754,18 +447,9 @@ export type IndexJsonPath = {
754
447
  readonly type: FieldType;
755
448
  };
756
449
  /**
757
- * MySQL's multi-valued index: one key per *element* of the JSON array at `path` (the column itself
758
- * when there is none), which is the only index `$all`/`$elemMatch` containment can use. `type` is
759
- * the element's, and a string or binary one needs a `length`, since the cast is what sizes the key.
760
- *
761
- * MySQL is alone in having it - `IndexFeature`'s `jsonArray` - and an index asking for it elsewhere
762
- * is refused rather than silently built.
763
- *
764
- * @example
765
- * ```ts
766
- * @Index((user) => [{ column: user.tags, jsonArray: { type: String, length: 64 } }]) // tags: { $all: [...] }
767
- * @Index((user) => [{ column: user.kind, jsonArray: { path: 'ids', type: Number } }]) // 'kind.ids': { $all: [...] }
768
- * ```
450
+ * MySQL's multi-valued index, one key per element of the JSON array at `path`, what `$all` and
451
+ * `$elemMatch` containment use; refused on any other engine.
452
+ * @example `@Index((user) => [{ column: user.tags, jsonArray: { type: String, length: 64 } }])`
769
453
  */
770
454
  export type IndexJsonArray = {
771
455
  /** The array's path inside the column, spelled as a `$where` key spells it; omit for the column. */
@@ -777,37 +461,25 @@ export type IndexJsonArray = {
777
461
  };
778
462
  /** The modifiers that do not name a JSON path, and so need no entity to be checked against. */
779
463
  type IndexColumnPlainModifiers = Except<IndexColumnModifiers, 'jsonPath' | 'jsonArray'>;
780
- /**
781
- * An entity's entry with plain modifiers. `jsonPath` and `jsonArray` are `never` here, since a JSON entry
782
- * would otherwise match this shape too, its path unchecked.
783
- */
464
+ /** An entity's entry with plain modifiers, never a JSON one, whose path would then go unchecked. */
784
465
  type IndexColumnOptions = IndexColumnPlainModifiers & {
785
466
  /** A column read off the refs, or `raw` for an expression. */
786
467
  readonly column: QueryRaw;
787
468
  readonly jsonPath?: never;
788
469
  readonly jsonArray?: never;
789
470
  };
790
- /**
791
- * One index entry, normalized: every authored shape reduces to this before any dialect or generator sees
792
- * it, so rendering never re-parses the sugar.
793
- */
471
+ /** One index entry, normalized, as every dialect and generator reads it. */
794
472
  export type IndexColumnSchema = IndexColumnModifiers & {
795
473
  /** A column name, or raw SQL when {@link expression} is set. */
796
474
  readonly column: string;
797
475
  /** Whether {@link column} is an expression to emit as-is rather than an identifier to quote. */
798
476
  readonly expression?: boolean;
799
477
  };
800
- /**
801
- * One index entry as entity metadata keeps it: a member, or an expression left unrendered until the
802
- * schema is built, where the dialect and the naming strategy resolve what it references. Rendered, it
803
- * is an {@link IndexColumnSchema}.
804
- */
478
+ /** One index entry as metadata keeps it: a member, or an expression rendered when the schema is built. */
805
479
  export type EntityIndexColumn = IndexColumnModifiers & {
806
480
  readonly column: string | QueryRaw;
807
481
  };
808
- /**
809
- * An index as stored in entity metadata: authored options with the columns normalized.
810
- */
482
+ /** An index as metadata keeps it. */
811
483
  export type EntityIndexMeta<E = object> = {
812
484
  /** The indexed columns, in order. */
813
485
  columns: readonly EntityIndexColumn[];
@@ -817,10 +489,7 @@ export type EntityIndexMeta<E = object> = {
817
489
  unique?: boolean;
818
490
  /** Partial index predicate, compiled when the schema is built. */
819
491
  where?: EntityWhereMeta<E>;
820
- /**
821
- * Extra columns stored in the index but not part of its key, so a query reading only these is
822
- * answered from the index alone. Postgres-wire only (`INCLUDE`).
823
- */
492
+ /** Columns stored in the index beyond its key, so a query reading only these needs no row. Postgres-wire only. */
824
493
  include?: readonly string[];
825
494
  } & VectorIndexOptions & IndexTypeOptions;
826
495
  export type EntityMeta<E> = {
@@ -831,13 +500,7 @@ export type EntityMeta<E> = {
831
500
  derivedName?: boolean;
832
501
  /** Set only when the entity named one; unset defers to the pool where it is used. See `AbstractDialect.resolveSchema`. */
833
502
  schema?: string;
834
- /**
835
- * Every key of the primary key, in declaration order. One unless the entity declares a composite.
836
- *
837
- * The only stored form: a single `id` beside it could only ever be right for a single-key entity,
838
- * so every reader had to know whether it was safe. Asking whether *this* field is part of the key
839
- * is `fields[key].isId`, which is O(1) and the source this list is derived from.
840
- */
503
+ /** Every column of the primary key, in declaration order. */
841
504
  ids: readonly IdKey<E>[];
842
505
  softDelete?: FieldKey<E>;
843
506
  /** Named, default-on `$where` filters applied to every query unless bypassed. */
@@ -858,20 +521,14 @@ export type EntityMeta<E> = {
858
521
  checks?: EntityCheckMeta<E>[];
859
522
  /** Lifecycle hooks registered via @BeforeInsert, @AfterUpdate, etc. */
860
523
  hooks?: Partial<Record<HookEvent, HookRegistration[]>>;
861
- /**
862
- * Bumped by every `define*` call, so anything derived from this metadata can tell that it changed.
863
- * A content type registered at runtime keeps adding to an entity that has already been read.
864
- */
524
+ /** Bumped by every `define*` call, so what is derived from the metadata can tell it changed. */
865
525
  revision: number;
866
526
  /** The revision `getMeta` last finalized, which is what makes finalizing idempotent and re-entrant. */
867
527
  processedAt?: number;
868
528
  };
869
529
  /**
870
- * A table-level `CHECK`: a predicate over the entity's fields, or SQL reading them off refs. A value in
871
- * either is written as its literal, since DDL has no placeholder to bind one into.
872
- *
873
- * @example `{ where: { balance: { $gte: 0 } } }`
874
- * @example `{ where: (wallet) => raw`${wallet.spent} <= ${wallet.balance}` }`
530
+ * A table's `CHECK`, `{ where: { balance: { $gte: 0 } } }`, or SQL off the refs,
531
+ * `{ where: (wallet) => raw`${wallet.spent} <= ${wallet.balance}` }`.
875
532
  */
876
533
  export type CheckOptions<E = unknown> = {
877
534
  /** Derived from the table and the constraint's position when absent. */
@@ -883,11 +540,7 @@ export type EntityCheckMeta<E = object> = {
883
540
  readonly name?: string;
884
541
  readonly where: EntityWhereMeta<E>;
885
542
  };
886
- /**
887
- * An entity's members as the registry takes them, keyed by plain strings - what a decorator bag, an
888
- * {@link EntityOptions} and a decorator bag both reduce to before anything is registered: a member
889
- * decorator has no class to key against, so by then the keys are plain strings either way.
890
- */
543
+ /** An entity's members as the registry takes them, keyed by name: what decorators and `defineEntity` both reduce to. */
891
544
  export type EntityMembers = {
892
545
  readonly fields?: Readonly<Record<string, FieldOptions | undefined>>;
893
546
  readonly relations?: Readonly<Record<string, RelationRegistration | undefined>>;
@@ -897,36 +550,21 @@ export type EntityMembers = {
897
550
  type EntityFieldOptions<E, F extends keyof E = FieldKey<E>> = {
898
551
  readonly [K in F]?: FieldOptionsFor<E[K], E>;
899
552
  };
900
- /**
901
- * An entity's relations as `defineEntity` takes them. Keyed over every member rather than `RelationKey<E>`:
902
- * inside the generic call, only a map over `keyof E` gives a `mappedBy` callback its contextual type. A
903
- * field named here still fails, on its options, since its value is no entity.
904
- */
553
+ /** An entity's relations as `defineEntity` takes them, keyed over every member: only that types a `mappedBy` callback. */
905
554
  type EntityRelationOptions<E> = {
906
555
  readonly [K in keyof E]?: RelationOptionsFor<E[K], E>;
907
556
  };
908
- /**
909
- * Configurable options for an entity (`@Entity()` / `defineEntity`).
910
- *
911
- * Optional `fields`, `relations`, `indexes`, and `hooks` register metadata in one call for
912
- * decorator-free setups. Omit them when using `@Field` / `@ManyToOne` / etc.
913
- */
557
+ /** An entity's options, `@Entity()` or `defineEntity`; the members too where no decorator declares them. */
914
558
  export type EntityOptions<E = unknown> = {
915
559
  readonly name?: string;
916
- /**
917
- * The base to inherit fields, relations, hooks and filters from, for a class that cannot extend one
918
- * (minted at runtime, or its base chosen from data): the merge `class Child extends Base` does, with
919
- * the class's real base nearer, so it wins. Checked against whatever properties the entity declares,
920
- * which a class behind an index signature has none of. See the Inheritance guide.
921
- */
560
+ /** A base to inherit members from, for a class that cannot extend it; its real base, if any, wins. See the Inheritance guide. */
922
561
  readonly extends?: string extends keyof E ? Type<object> : Type<Partial<E>>;
923
- /**
924
- * The schema (in MySQL terms, database) this table lives in, pinning it whichever pool reads it;
925
- * unset follows the pool's own. Not in `name`: a dotted `name` is rejected.
926
- */
562
+ /** The schema (a MySQL database) the table lives in; unset follows the pool's. */
927
563
  readonly schema?: string;
928
564
  /** Named, default-on `$where` filters (soft-delete is auto-registered from `@Field({ softDelete })`). */
929
- readonly filters?: Record<string, FilterOptions<E>>;
565
+ readonly filters?: Record<string, FilterOptions<E>> & {
566
+ readonly [SOFT_DELETE_FILTER]?: never;
567
+ };
930
568
  /** Scalar fields; use `isId: true` on exactly one field for the primary key. */
931
569
  readonly fields?: EntityFieldOptions<E>;
932
570
  readonly relations?: EntityRelationOptions<E>;