uql-orm 0.66.0 → 0.67.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +2 -7
  41. package/dist/entity/decorator/entity.js +2 -7
  42. package/dist/entity/decorator/members.d.ts +7 -30
  43. package/dist/entity/decorator/members.js +3 -12
  44. package/dist/entity/metadata/definition.d.ts +2 -18
  45. package/dist/entity/metadata/definition.js +6 -28
  46. package/dist/http/handler.d.ts +2 -14
  47. package/dist/index.d.ts +4 -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 +16 -66
  92. package/dist/migrate/schemaGenerator.js +21 -74
  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 +2 -30
  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 +163 -534
  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 +19 -73
  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 +2 -31
  174. package/dist/util/field.util.js +3 -43
  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
@@ -11,14 +11,7 @@ export class QueryRaw {
11
11
  as(alias) {
12
12
  return new QueryRaw(this[RAW_VALUE], alias);
13
13
  }
14
- /**
15
- * Emit this expression into `opts.ctx`. How a raw value becomes SQL is the raw value's own
16
- * business, which is what lets a `raw` tagged template resolve an interpolated fragment without
17
- * the dialect having to expose a method for it.
18
- *
19
- * The alias is not emitted here: it names a `$select` projection, which writes it after the term,
20
- * and anywhere else it would land mid-expression.
21
- */
14
+ /** Writes the expression into `opts.ctx`. The alias is the projection's to write, after the term. */
22
15
  render(opts) {
23
16
  const emitted = this[RAW_VALUE](opts);
24
17
  if (typeof emitted === 'string' || (typeof emitted === 'number' && !Number.isNaN(emitted))) {
@@ -22,19 +22,9 @@ export type QueryTextSearchOptions<E> = {
22
22
  $config?: string;
23
23
  };
24
24
  /**
25
- * Field comparison, JSON dot-path access, and relation filtering - all fully typed.
26
- * JSON dot-paths are restricted to real JSON fields, and typed payloads type each path's value
27
- * (untyped `Json` payloads accept any `field.suffix` path with a permissive value). Relations are
28
- * filtered via nested typed objects; dotted relation paths are not supported (the dialects throw
29
- * for non-JSON dotted keys).
30
- *
31
- * Fields and relations share one mapped type over `K extends keyof E`, which keeps each key linked to
32
- * its property (see {@link QuerySelect}). JSON paths are not keys of `E`, so they are a second member,
33
- * and only where the entity has one: an empty member would switch off the weak-type check that
34
- * rejects `$where: 1`.
35
- *
36
- * An object and nothing else: in a union with ids or lists, TypeScript reports a wrong value against
37
- * the whole `$where` instead of the key holding it. Ids are `{ id: 1 }`, or the by-id methods.
25
+ * A filter by fields, JSON paths (typed by their payload) and relations, one mapped type over the
26
+ * entity's keys so each stays linked for rename. An object and nothing else, so a wrong value is
27
+ * reported on its key: ids go through `{ id: 1 }` or the by-id methods.
38
28
  */
39
29
  export type QueryWhere<E, K extends keyof E = FieldKey<E> | RelationKey<E>> = QueryWhereRootOperator<E> & {
40
30
  [P in K]?: P extends FieldKey<E> ? QueryWhereFieldValue<E[P]> : QueryWhere<RelationTarget<E[P]>> | QueryRelationSizeFilter;
@@ -103,29 +93,9 @@ export type QuerySizeComparisonOps = {
103
93
  [K in QueryHavingOp | '$between']?: NonNullable<QueryWhereFieldOperatorMap<number>[K]>;
104
94
  };
105
95
  /**
106
- * Filter by distance to a query vector: `$where`'s counterpart to `$sort`'s ranking, so "the closest
107
- * ten" and "everything closer than 0.35" stay separate asks.
108
- *
109
- * Bounded by {@link QueryOrderedOp} - what {@link QuerySizeComparisonOps} ranges over, minus
110
- * `$eq`/`$ne`. A distance is a float, so exact equality against one is a bug every time, where
111
- * `$size` compares an integer `COUNT`. No `$project` either: naming the distance is `$sort`'s job,
112
- * since the `SELECT` list is built from `$sort` alone and a `$near` nested inside an `$or` has no
113
- * business projecting a column.
114
- *
115
- * `$distance` is here for the same reason `$sort` has it: each clause states its own search
116
- * completely, so neither depends on the other. Omitted, it falls back to the field's declared metric,
117
- * which is where the metric belongs - beside the index it has to match. Naming a different one per
118
- * query mostly buys a full scan, since an ANN index is built for exactly one operator class.
119
- *
120
- * `$vector` is required, and repeating it beside a `$sort` that ranks by the same field is the point:
121
- * every other `$where` operator means the same thing wherever it appears, and inheriting one from a
122
- * sibling clause would make this the first whose validity depends on what else the query contains -
123
- * unfixable in the type, and carried into merged entity filters and `/http` payloads alike. Naming
124
- * the vector in a `const` is what removes the repetition, at the call site where it belongs.
125
- *
126
- * A `$near` carrying no bound is a `WHERE` that is always true. The dialect rejects that rather than
127
- * the type: `/http` casts client JSON straight to `Query`, so the check has to exist there anyway,
128
- * and an "at least one of these five" union would cost every caller worse errors for a second copy.
96
+ * Filter by distance to a vector, `{ $near: { $vector: v, $lt: 0.35 } }`: ordered bounds only, since a
97
+ * distance is a float. Each clause names its own `$vector`, and `$distance` falls back to the field's.
98
+ * One with no bound is refused at run time, where `/http` input is checked anyway.
129
99
  */
130
100
  export type QueryVectorNear = QueryVectorQuery & {
131
101
  [K in QueryOrderedOp]?: NonNullable<QueryWhereFieldOperatorMap<number>[K]>;
@@ -224,13 +194,7 @@ export type QueryWhereFieldOperatorMap<T> = {
224
194
  * @example { tags: { $all: ['typescript', 'orm'] } }
225
195
  */
226
196
  $all?: unknown extends T ? unknown[] : NonNullable<T> extends readonly (infer U)[] ? ExpandScalar<U>[] : never;
227
- /**
228
- * whether an array has the specified length.
229
- * Accepts a number for exact match, or a comparison operator object for range queries.
230
- * @example { roles: { $size: 3 } }
231
- * @example { roles: { $size: { $gte: 2 } } }
232
- * @example { roles: { $size: { $gt: 0, $lte: 5 } } }
233
- */
197
+ /** whether an array has the given length, or one in range: `{ roles: { $size: { $gte: 2 } } }`. */
234
198
  $size?: number | QuerySizeComparisonOps;
235
199
  /**
236
200
  * whether an array contains at least one element matching all specified conditions.
@@ -292,11 +256,8 @@ type QueryOrderedOp = QueryCompareOp | keyof Pick<QueryWhereFieldOperatorMap<unk
292
256
  */
293
257
  type QueryVectorOp = keyof Pick<QueryWhereFieldOperatorMap<unknown>, '$near'>;
294
258
  /**
295
- * Operators applicable to every field type: equality, membership, negation, and null checks.
296
- *
297
- * @remarks This is a subtraction, not a list, so an operator added to
298
- * {@link QueryWhereFieldOperatorMap} without also being classified above lands here and is offered
299
- * on every field - `$near` on a `boolean`, say. Classify first, then add.
259
+ * The operators every field takes. A subtraction, so an operator added to the map without being
260
+ * classified above is offered on every field: classify it first.
300
261
  */
301
262
  type QueryCommonOp = Exclude<keyof QueryWhereFieldOperatorMap<unknown>, QueryStringOp | QueryArrayOp | QueryOrderedOp | QueryVectorOp>;
302
263
  /**
@@ -305,13 +266,8 @@ type QueryCommonOp = Exclude<keyof QueryWhereFieldOperatorMap<unknown>, QueryStr
305
266
  */
306
267
  type QueryAllowedOp<T> = QueryCommonOp | ([NonNullable<T>] extends [QueryComparableScalar] ? QueryOrderedOp : never) | ([NonNullable<T>] extends [string] ? QueryStringOp : never) | ([NonNullable<T>] extends [readonly number[] | Uint8Array] ? QueryVectorOp : never) | (IsMany<T> extends true ? QueryArrayOp : never);
307
268
  /**
308
- * Operators applicable to a field of type `T`: string operators require string fields, ordering
309
- * operators comparable fields, array operators array fields.
310
- *
311
- * Two shapes stay fully permissive, because neither says anything to check against: `unknown`
312
- * (untyped JSON dot-paths, erased dialect shapes), and a field typed as every scalar at once - the
313
- * column of a content type defined at runtime. Narrowing to what they share would leave a dynamic
314
- * row with equality alone, since no operator applies to a boolean and a blob both.
269
+ * The operators a field of type `T` takes. `unknown`, and a column typed as every scalar at once (a
270
+ * runtime-defined entity), take all of them, since nothing narrows what they hold.
315
271
  */
316
272
  export type QueryWhereFieldOperators<T> = unknown extends T ? QueryWhereFieldOperatorMap<T> : IsUntypedColumn<T> extends true ? QueryWhereFieldOperatorMap<T> : Pick<QueryWhereFieldOperatorMap<T>, QueryAllowedOp<T>>;
317
273
  /**
@@ -321,12 +277,8 @@ export type QueryWhereFieldOperators<T> = unknown extends T ? QueryWhereFieldOpe
321
277
  */
322
278
  type IsUntypedColumn<T> = [Scalar] extends [NonNullable<T>] ? true : false;
323
279
  /**
324
- * Value for a field comparison. A bare array is an implicit `$in` for scalar fields only:
325
- * on array-typed fields (e.g. a vector `number[]`) an array of arrays is ambiguous, so
326
- * membership there requires an explicit operator.
327
- *
328
- * `null` is accepted on a nullable field (an optional property is a nullable column), matching what
329
- * `$eq: null` already took.
280
+ * A field's filter value: the value, `null` where it is optional, a list as an implicit `$in` (not on
281
+ * an array field, where it would be ambiguous), or an operator map.
330
282
  */
331
283
  export type QueryWhereFieldValue<T> = T | (undefined extends T ? null : never) | (IsMany<T> extends true ? never : T[]) | QueryWhereFieldOperators<T> | QueryRaw;
332
284
  /**
@@ -4,24 +4,11 @@ import type { QueryAggMap, QueryAggregate, QueryAggregateResult, QueryGroupMap }
4
4
  import type { Type } from './utility.js';
5
5
  import type { QuerierCountedResult, QuerierResult, QuerierTransport } from './wire.js';
6
6
  /**
7
- * The operations {@link UniversalQuerier} and `ClientQuerier` declare identically, written once and
8
- * instantiated per transport: `SharedQuerier<'server', QueryOptions>` against
9
- * `SharedQuerier<'client', RequestOptions, QueryOptions & RequestOptions>`. See {@link QuerierResult}
10
- * for how the return type follows `W`.
11
- *
12
- * @typeParam W - which side of the wire, picking each method's return type.
13
- * @typeParam O - the per-call options.
14
- * @typeParam DO - the delete methods' options, which the client also lets carry {@link QueryOptions}
15
- * (`hardDelete`, `filters`) since it has no other way to reach them.
7
+ * The operations the server and the browser client declare alike, per transport `W`, options `O`,
8
+ * and delete options `DO`, which on the client also carry the {@link QueryOptions} it cannot pass otherwise.
16
9
  */
17
10
  export interface SharedQuerier<W extends QuerierTransport, O, DO = O> {
18
- /**
19
- * obtains the record with the given primary key.
20
- * @param entity the target entity
21
- * @param id the primary key value
22
- * @param q the additional criteria options
23
- * @return the record
24
- */
11
+ /** Find the record with the given primary key. */
25
12
  findOneById<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(entity: Type<E>, id: EntityId<E>, q?: QueryOneProjected<E, S, V, X, P, C>, opts?: O): QuerierResult<W, QueryFindResult<E, S, V, X, P, C> | undefined>;
26
13
  /**
27
14
  * obtains the first record matching the given search parameters.
@@ -37,45 +24,15 @@ export interface SharedQuerier<W extends QuerierTransport, O, DO = O> {
37
24
  * @return the records
38
25
  */
39
26
  findMany<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(entity: Type<E>, q: QueryProjected<E, S, V, X, P, C>, opts?: O): QuerierResult<W, QueryFindResult<E, S, V, X, P, C>[]>;
40
- /**
41
- * obtains the records matching the given search parameters,
42
- * also counts the number of matches ignoring pagination.
43
- * @param entity the target entity
44
- * @param q the criteria options
45
- * @return the records and the count
46
- */
27
+ /** Find the records matching the query, and count every match past its page. */
47
28
  findManyAndCount<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(entity: Type<E>, q: QueryProjected<E, S, V, X, P, C>, opts?: O): QuerierCountedResult<W, QueryFindResult<E, S, V, X, P, C>>;
48
- /**
49
- * counts the number of records matching the given filter, optionally paged - a `$skip`/`$limit`
50
- * settles the matching rows first and counts them, rather than scanning every match.
51
- * @param entity the target entity
52
- * @param q the filter
53
- * @return the count
54
- */
29
+ /** Count the records matching the filter, or those a page of them takes. */
55
30
  count<E extends object>(entity: Type<E>, q?: QueryPage<E>, opts?: O): QuerierResult<W, number>;
56
- /**
57
- * whether any record matches the given filter - a count capped at one row, so the engine stops at
58
- * the first match instead of scanning every other one.
59
- * @param entity the target entity
60
- * @param q the filter
61
- * @return whether anything matched
62
- */
31
+ /** Whether any record matches: a count capped at one row, so the engine stops at the first match. */
63
32
  exists<E extends object>(entity: Type<E>, q?: QueryFilter<E>, opts?: O): QuerierResult<W, boolean>;
64
- /**
65
- * updates a record partially.
66
- * @param entity the entity to persist on
67
- * @param id the primary key of the record to be updated
68
- * @param payload the data to be persisted
69
- * @return the number of affected records
70
- */
33
+ /** Update the record with the given primary key; resolves to the number of affected rows. */
71
34
  updateOneById<E extends object>(entity: Type<E>, id: EntityId<E>, payload: UpdatePayload<E>, opts?: O): QuerierResult<W, number>;
72
- /**
73
- * updates many records partially.
74
- * @param entity the entity to persist on
75
- * @param q the criteria to look for the records
76
- * @param payload the data to be persisted
77
- * @return the number of affected records
78
- */
35
+ /** Update the records matching the query; resolves to the number of affected rows. */
79
36
  updateMany<E extends object>(entity: Type<E>, q: QuerySearch<E>, payload: UpdatePayload<E>, opts?: O): QuerierResult<W, number>;
80
37
  /**
81
38
  * delete or SoftDelete a record.
@@ -97,56 +54,21 @@ export interface SharedQuerier<W extends QuerierTransport, O, DO = O> {
97
54
  */
98
55
  export interface UniversalQuerier extends SharedQuerier<'server', QueryOptions> {
99
56
  /**
100
- * streams the records matching the given search parameters as an async iterable, each with the
101
- * relations and counts `findMany` reads. Fires no lifecycle hooks, and holds one row at a time, for
102
- * bulk reads (ETL, exports, migrations).
103
- * @param entity the target entity
104
- * @param q the criteria options
105
- * @return an async iterable of records
57
+ * Stream the records matching the query one at a time, each with the relations and counts `findMany`
58
+ * reads, for bulk reads. Fires no lifecycle hooks.
106
59
  */
107
60
  findManyStream<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(entity: Type<E>, q: QueryProjected<E, S, V, X, P, C>, opts?: QueryOptions): AsyncIterable<QueryFindResult<E, S, V, X, P, C>>;
108
- /**
109
- * Insert a single record and return its ID (provided, `onInsert`-generated, or
110
- * database-generated - see {@link UniversalQuerier.insertMany} for the exact semantics).
111
- * Returns `undefined` only where the database cannot report one: MySQL, whose `LAST_INSERT_ID()`
112
- * speaks for `AUTO_INCREMENT` columns alone and is left *stale* rather than cleared otherwise, so
113
- * a non-auto-increment key the caller did not supply has no id to give and a header read would
114
- * hand back an earlier row's. Every other backend uses `RETURNING` and is exact, SQLite included.
115
- * @param entity the entity to persist on
116
- * @param payload the data to be persisted
117
- * @return the ID
118
- */
61
+ /** Insert a record and resolve to its id. See {@link UniversalQuerier.insertMany}. */
119
62
  insertOne<E extends object>(entity: Type<E>, payload: EntityData<E>): Promise<WrittenId<E> | undefined>;
120
63
  /**
121
- * Insert multiple records in a single statement (auto-chunked when the batch exceeds the
122
- * dialect's bind-parameter limit) and return their IDs in payload order.
123
- *
124
- * Provided IDs and client-generated ones (`@Id({ onInsert })`) are always returned as-is.
125
- * Database-generated IDs are exact wherever the statement returns them, which is everywhere but
126
- * MySQL: there they are inferred from the driver header, which is only reliable for
127
- * auto-increment keys in batches without explicit IDs - otherwise those entries are
128
- * `undefined` rather than potentially wrong values. A composite key is never one the statement
129
- * reports, so those rows are named as written, `onInsert` columns included.
130
- * @param entity the entity to persist on
131
- * @param payload the data to be persisted
132
- * @return the IDs
64
+ * Insert records in as few statements as the bind limit allows, resolving to their ids in payload order.
65
+ * Ids are exact everywhere but MySQL, which infers them from its header and reports `undefined` rather
66
+ * than a guess where it cannot: a batch naming some keys, or a key that is not `AUTO_INCREMENT`.
133
67
  */
134
68
  insertMany<E extends object>(entity: Type<E>, payload: EntityData<E>[]): Promise<(WrittenId<E> | undefined)[]>;
135
- /**
136
- * Insert or update a record based on the conflict paths.
137
- * @param entity the entity to persist on
138
- * @param conflictPaths the keys to use for the unique search
139
- * @param payload the data to be persisted
140
- * @return the id and whether it was created; see {@link QueryUpsertOneResult}
141
- */
69
+ /** Insert or update a record by its conflict paths; resolves to its id and whether it was created. */
142
70
  upsertOne<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityData<E>): Promise<QueryUpsertOneResult<E>>;
143
- /**
144
- * Insert or update many records based on the conflict paths.
145
- * @param entity the entity to persist on
146
- * @param conflictPaths the keys to use for the unique search
147
- * @param payload the data to be persisted
148
- * @return the ids, in payload order; see {@link QueryUpsertManyResult}
149
- */
71
+ /** Insert or update records by their conflict paths; resolves to their ids in payload order. */
150
72
  upsertMany<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityData<E>[]): Promise<QueryUpsertManyResult<E>>;
151
73
  /**
152
74
  * insert or update a record.
@@ -176,17 +98,8 @@ export interface UniversalQuerier extends SharedQuerier<'server', QueryOptions>
176
98
  */
177
99
  aggregate<E extends object, const G extends QueryGroupMap<E>, const A extends QueryAggMap<E>>(entity: Type<E>, q: QueryAggregate<E, G, A>, opts?: QueryOptions): Promise<QueryAggregateResult<E, G, A>[]>;
178
100
  /**
179
- * How many rows the engine's own statistics say the table holds, without reading one: Postgres'
180
- * `pg_class.reltuples`, CockroachDB's table statistics, MySQL/MariaDB's `information_schema`,
181
- * MongoDB's `estimatedDocumentCount`. For a table too big to {@link count} cheaply.
182
- *
183
- * The whole table, and only ever approximately. It takes no filter because none of those sources
184
- * can answer one, which also puts soft-deleted rows and every entity `filters` inside the number.
185
- * It is as stale as the last `ANALYZE`/autovacuum (Postgres reports nothing at all until the
186
- * first one, which reads here as `0`), and a table SQLite can answer for does not exist - it
187
- * throws there rather than quietly running the scan this exists to avoid.
188
- * @param entity the target entity
189
- * @return the estimated number of rows
101
+ * The table's row count as the engine's statistics state it, without reading a row: approximate, as
102
+ * stale as the last `ANALYZE`, and unfiltered, so soft-deleted rows count. SQLite has none and throws.
190
103
  */
191
104
  estimatedCount<E extends object>(entity: Type<E>): Promise<number>;
192
105
  }
@@ -4,14 +4,8 @@ export type MongoId = {
4
4
  toHexString: () => string;
5
5
  };
6
6
  /**
7
- * Every value type storable in an entity column. Superset of {@link QueryComparableScalar}
8
- * and {@link PrimaryKey}.
9
- *
10
- * `Uint8Array` rather than `Buffer`, which every `Buffer` still satisfies: naming an ambient Node
11
- * global here made the whole key-checking layer depend on `@types/node` being in scope. Without it
12
- * `Buffer` resolves to nothing, this union collapses to `any`, and `FieldKey` - the basis of
13
- * `$select`, `$where`, `$sort`, `@Index` and `defineEntity({ fields })` - silently stops checking
14
- * anything. A browser or edge project would have got no type safety and no error saying so.
7
+ * Every value a column may hold. `Uint8Array` rather than `Buffer`, which would need `@types/node` and
8
+ * collapse to `any` without it, switching every key check off.
15
9
  */
16
10
  export type Scalar = string | number | boolean | bigint | Date | RegExp | Uint8Array | MongoId;
17
11
  /**
@@ -23,16 +17,8 @@ export type QueryComparableScalar = string | number | bigint | Date;
23
17
  */
24
18
  export type PrimaryKey = string | number | bigint;
25
19
  /**
26
- * Marker type for JSON/JSONB fields.
27
- * Wrapping a field's TypeScript type with `Json<T>` ensures it is classified as a `FieldKey`
28
- * (not a `RelationKey`), enabling type-safe usage in `$where`, `$select`, and `$sort`. A column
29
- * holding a list of documents is `Json<T>[]`, also a field, whose dot-paths address the element.
30
- *
31
- * @example
32
- * ```ts
33
- * @Field({ type: 'jsonb' })
34
- * settings?: Json<{ isArchived?: boolean }>;
35
- * ```
20
+ * Brands a JSON field, `settings?: Json<{ isArchived?: boolean }>`, so it reads as a field rather than a
21
+ * relation; `Json<T>[]` is a list of documents.
36
22
  */
37
23
  export type Json<T = unknown> = T & {
38
24
  readonly __json?: never;
@@ -48,16 +34,18 @@ export type Writable<T> = {
48
34
  -readonly [K in keyof T]: T[K];
49
35
  };
50
36
  /**
51
- * `Omit`, fixed on three counts. Its key has to exist, where `Omit<T, K extends keyof any>` lets a
52
- * typo or a renamed property silently omit nothing. Being a homomorphic mapped type it distributes
53
- * over unions, where `Omit` intersects each member's keys and flattens a discriminated union (e.g.
54
- * `EntityIndexMeta`'s `type`/`distance` pairing) into one non-discriminated shape. And it removes
55
- * the key from types carrying an index signature, where `Exclude<keyof T, K>` widens back to
56
- * `string | number` and leaves the key in place.
37
+ * `Omit` whose key has to exist, which distributes over a union rather than flattening it, and removes
38
+ * a key from a type with an index signature.
57
39
  */
58
40
  export type Except<T, K extends keyof T> = {
59
41
  [P in keyof T as P extends K ? never : P]: T[P];
60
42
  };
43
+ /**
44
+ * Each key of `K` mapped to `never`, so an intersection with it makes naming one a compile error;
45
+ * `unknown`, inert, when there are none. A captured type parameter needs it: TypeScript skips the
46
+ * excess-property check on one.
47
+ */
48
+ export type RejectKeys<K> = [K] extends [never] ? unknown : Record<K & string, never>;
61
49
  export type Unpacked<T> = T extends readonly (infer U)[] ? U : T extends (...args: unknown[]) => infer U ? U : T extends Promise<infer U> ? U : T;
62
50
  /**
63
51
  * Whether the value a property holds is many rather than one: a to-many relation, a scalar array, a
@@ -1,14 +1,7 @@
1
1
  import type { IndexType } from '../schema/types.js';
2
2
  /**
3
- * Distance metrics supported by vector similarity search.
4
- * - `cosine` - best for text/LLM embeddings (default)
5
- * - `l2` - Euclidean distance
6
- * - `inner` - inner (dot) product
7
- * - `l1` - Manhattan distance
8
- *
9
- * @remarks Hamming distance is absent because no engine can express it over a float vector column:
10
- * pgvector's `<~>`/`bit_hamming_ops` and sqlite-vec's `vec_distance_hamming` both require a *bit*
11
- * vector, which no field type maps to. It would be a value that compiles and always throws.
3
+ * A vector search's metric: `cosine` (the default), `l2`, `inner` product or `l1`. No hamming: every
4
+ * engine's takes a bit vector, which no field type maps to.
12
5
  */
13
6
  export type VectorDistance = 'cosine' | 'l2' | 'inner' | 'l1';
14
7
  /**
@@ -24,42 +17,19 @@ export interface QueryVectorQuery {
24
17
  }
25
18
  /** The keys that describe the search rather than bound it, so `$near`'s bounds are what is left. */
26
19
  export declare const VECTOR_QUERY_KEYS: readonly ["$vector", "$distance"];
27
- /**
28
- * Vector similarity search options - used inside `$sort` on vector fields.
29
- *
30
- * @example
31
- * ```ts
32
- * querier.findMany(Article, {
33
- * $sort: { embedding: { $vector: queryVec } },
34
- * $limit: 10,
35
- * });
36
- * ```
37
- */
20
+ /** A vector search in `$sort`: `{ $sort: { embedding: { $vector: queryVec } }, $limit: 10 }`. */
38
21
  export interface QueryVectorSearch extends QueryVectorQuery {
39
22
  /** Project the computed distance as a named field in the result. */
40
23
  readonly $project?: string;
41
24
  }
42
25
  /**
43
- * Augments a row with the distance a vector-search `$sort.$project` computes, which is not
44
- * inferred. Wrap whatever the query returns - the entity, or a projected row:
45
- * ```ts
46
- * const results = (await querier.findMany(Article, {
47
- * $sort: { embedding: { $vector: queryVec, $project: 'similarity' } },
48
- * })) as WithDistance<Article, 'similarity'>[];
49
- * ```
26
+ * A row with the distance a `$sort` `$project` names, which is not inferred:
27
+ * `(await querier.findMany(Article, q)) as WithDistance<Article, 'similarity'>[]`.
50
28
  */
51
29
  export type WithDistance<E, K extends string = '_distance'> = E & Record<K, number>;
52
30
  /**
53
- * How one dialect spells one distance metric. Two shapes exist across engines - an infix operator
54
- * (`"col" <=> $1`, pgvector) or a function call (`VEC_DISTANCE_COSINE(col, ?)`, MariaDB and the
55
- * SQLite family) - so they are one discriminated map rather than two parallel ones. That is what
56
- * lets a single `appendVectorSort` serve every engine, and makes the map's key set the one answer
57
- * to "does this dialect have this metric".
58
- *
59
- * `opsSuffix` rides along on the operator form because pgvector's index operator class is named from
60
- * the same metric (`vector_cosine_ops`): keeping them together is what stops a dialect from having
61
- * the operator but not the class it indexes with. `metricArg` is for the engine with one function
62
- * taking the metric by name: SQL Server's `VECTOR_DISTANCE('cosine', a, b)`.
31
+ * How a dialect spells a metric: an operator with the operator class an index names from it, or a
32
+ * function, `metricArg` where it takes the metric by name. One map, so its keys say which metrics exist.
63
33
  */
64
34
  export type VectorMetric = {
65
35
  readonly op: string;
@@ -72,7 +42,7 @@ export type VectorMetric = {
72
42
  export type VectorOperatorMetric = Extract<VectorMetric, {
73
43
  op: string;
74
44
  }>;
75
- /** Every dialect words this the same, and one of them used to throw a bare `Error` for it. */
45
+ /** The error every dialect throws for a metric it lacks. */
76
46
  export declare function unsupportedVectorMetric(dialectName: string, distance: VectorDistance, indexName?: string): TypeError;
77
47
  /**
78
48
  * Vector-specific tuning options shared by `@Index` decorator, entity metadata, and migration schema.
@@ -1,6 +1,6 @@
1
1
  /** The keys that describe the search rather than bound it, so `$near`'s bounds are what is left. */
2
2
  export const VECTOR_QUERY_KEYS = ['$vector', '$distance'];
3
- /** Every dialect words this the same, and one of them used to throw a bare `Error` for it. */
3
+ /** The error every dialect throws for a metric it lacks. */
4
4
  export function unsupportedVectorMetric(dialectName, distance, indexName) {
5
5
  const where = indexName === undefined ? '' : ` (index "${indexName}")`;
6
6
  return new TypeError(`${dialectName} does not support vector distance metric: ${distance}${where}`);
@@ -17,11 +17,8 @@ export type RequestCountedSuccessResponse<E> = RequestSuccessResponse<E> & {
17
17
  */
18
18
  export type QuerierTransport = 'server' | 'client';
19
19
  /**
20
- * A querier method's result on the given transport, so one signature serves both:
21
- * `QuerierResult<'server', User[]>` is `Promise<User[]>` and `QuerierResult<'client', User[]>` is
22
- * `Promise<RequestSuccessResponse<User[]>>`. Indexing a map by the transport is what stands in for
23
- * the higher-kinded wrapper TypeScript cannot express, and it resolves away: errors and hovers show
24
- * the `Promise<User[]>` it picked, never this indirection.
20
+ * A querier method's result on a transport: `Promise<User[]>` on the server, the response envelope on
21
+ * the client. A map indexed by the transport, which resolves away in hovers.
25
22
  */
26
23
  export type QuerierResult<W extends QuerierTransport, T> = {
27
24
  server: Promise<T>;
@@ -1,7 +1,7 @@
1
1
  import { type CascadeType, type EntityData, type EntityId, type EntityIndexMeta, type EntityMeta, type FieldKey, type FieldOptions, type JsonUpdateOp, type OnFieldCallback, type Query, type QueryAggMap, type QueryAggregateOp, type QueryExclude, type QueryGroupMap, type QueryOptions, QueryRaw, type QuerySearch, type QuerySelect, type QuerySelectValue, type QuerySizeComparisonOps, type QuerySortMap, type QueryTextSearchOptions, type QueryVectorSearch, type QueryWhere, type RelationKey, type UpdatePayload } from '../type/index.js';
2
2
  export type CallbackKey = keyof Pick<FieldOptions, 'onInsert' | 'onUpdate'>;
3
- export declare function filterFieldKeys<E>(meta: EntityMeta<E>, payload: EntityData<E>, callbackKey: CallbackKey): FieldKey<E>[];
4
- /** Appends `record`'s not-yet-`seen` insertable keys (real, caller-written, defined value) to `keys`. */
3
+ /** The keys of `payload` a write persists as columns. */
4
+ export declare function filterFieldKeys<E>(meta: EntityMeta<E>, payload: EntityData<E> | UpdatePayload<E>, callbackKey: CallbackKey): FieldKey<E>[];
5
5
  /**
6
6
  * The insertable keys `record` itself carries, as a string, for grouping rows by the statement they
7
7
  * can share. Only the row's own keys: the `onInsert` columns {@link getInsertFieldKeys} appends are a
@@ -9,16 +9,8 @@ export declare function filterFieldKeys<E>(meta: EntityMeta<E>, payload: EntityD
9
9
  */
10
10
  export declare function insertShapeOf<E>(meta: EntityMeta<E>, record: EntityData<E>): string;
11
11
  /**
12
- * Resolves the columns of an INSERT statement: the union of the persistable fields provided by
13
- * any record (in first-seen order), plus every `onInsert` field. Records missing one of these
14
- * columns insert its database default.
15
- *
16
- * The column list is seeded from the first record, then extended by every other record's
17
- * not-yet-seen columns - a no-op scan for a homogeneous batch (every record the same shape).
18
- *
19
- * `onInsert` fields are always included so the column set is stable whether or not the caller
20
- * has run {@link fillOnFields} first (it stamps them on every record, but the querier's
21
- * chunk-size estimate inspects the raw payload).
12
+ * An insert's columns: every record's writable fields in first-seen order, plus every `onInsert` field,
13
+ * whether or not it was filled yet. A record missing one writes its default.
22
14
  */
23
15
  export declare function getInsertFieldKeys<E>(meta: EntityMeta<E>, payloads: EntityData<E>[]): FieldKey<E>[];
24
16
  export declare function getFieldCallbackValue(val: OnFieldCallback): string | number | bigint | boolean | Date | QueryRaw | readonly {
@@ -104,17 +96,9 @@ export declare function assertWhere<E>(meta: EntityMeta<E>, where: unknown): voi
104
96
  /** Returns a `QueryOptions.filters` value with the built-in soft-delete filter disabled (used by hard delete). */
105
97
  export declare function withoutSoftDeleteFilter(filters: QueryOptions['filters']): QueryOptions['filters'];
106
98
  /**
107
- * Returns a new `$where` map with every active entity filter's condition merged in, resolving
108
- * parameterized conditions against the explicit or ambient {@link UqlContext}. Never mutates the input.
109
- *
110
- * Convenience filters are active by default (unless `opts.filters === false` or bypassed by name), and
111
- * their keys are applied only when absent from the map, so an explicit `$where` on that key opts out.
112
- *
113
- * `security` filters are always active (bypass is ignored) and AND-merged, so a client `$where` on the
114
- * same field can't override them. A security condition that returns `undefined` fails the query closed
115
- * (throws {@link UqlSecurityError}) unless its `onMissing` is `skip`; one that returns an empty object
116
- * (`{}`) resolved to "no restriction" and adds nothing - the escape hatch for trusted cross-tenant
117
- * work (e.g. a maintenance job running under a `system` context).
99
+ * `$where` with the entity's active filters merged in, against the ambient {@link UqlContext}. A convenience
100
+ * filter yields to a `$where` on its key; a `security` one is always ANDed, and throws where its condition
101
+ * resolves to nothing, unless `onMissing: 'skip'`.
118
102
  */
119
103
  export declare function applyFilters<E>(meta: EntityMeta<E>, whereMap: QueryWhere<E>, opts?: QueryOptions): QueryWhere<E>;
120
104
  /**
@@ -132,10 +116,8 @@ export type ParsedGroupEntry = {
132
116
  readonly distinct: boolean;
133
117
  };
134
118
  /**
135
- * The `$size` of a relation condition (`{ comments: { $size: { $gte: 2 } } }`), or `undefined` when the
136
- * condition constrains the target's fields instead. Shared so every dialect agrees on which of the two
137
- * a relation key means, and so the ambiguous mix fails loudly: `{ $size: 2, name: 'x' }` used to fall
138
- * through to field filtering and emit a condition on a `$size` *column*.
119
+ * The `$size` of a relation condition, `{ comments: { $size: { $gte: 2 } } }`, or `undefined` where it
120
+ * filters the target's fields; a mix of the two, `{ $size: 2, name: 'x' }`, is refused.
139
121
  */
140
122
  export declare function parseRelationSize(val: unknown): number | QuerySizeComparisonOps | undefined;
141
123
  /**
@@ -4,6 +4,7 @@ import { QueryRaw, resolveAggregateOp, SOFT_DELETE_FILTER, } from '../type/index
4
4
  import { VECTOR_INDEX_TYPES } from '../type/vector.js';
5
5
  import { isDatabaseWritten } from './field.util.js';
6
6
  import { entityName, getFieldKeys, getKeys, hasKeys, isScalarId, isRecord, isWhereMap, someKey, } from './object.util.js';
7
+ /** The keys of `payload` a write persists as columns. */
7
8
  export function filterFieldKeys(meta, payload, callbackKey) {
8
9
  return getKeys(payload).filter((key) => {
9
10
  const fieldOpts = meta.fields[key];
@@ -15,7 +16,6 @@ function isInsertableField(meta, record, key) {
15
16
  const field = meta.fields[key];
16
17
  return !!field && !isDatabaseWritten(field) && record[key] !== undefined;
17
18
  }
18
- /** Appends `record`'s not-yet-`seen` insertable keys (real, caller-written, defined value) to `keys`. */
19
19
  /**
20
20
  * The insertable keys `record` itself carries, as a string, for grouping rows by the statement they
21
21
  * can share. Only the row's own keys: the `onInsert` columns {@link getInsertFieldKeys} appends are a
@@ -30,6 +30,7 @@ export function insertShapeOf(meta, record) {
30
30
  }
31
31
  return shape;
32
32
  }
33
+ /** Appends `record`'s not-yet-`seen` insertable keys to `keys`. */
33
34
  function addInsertFieldKeys(meta, record, seen, keys) {
34
35
  for (const key of getKeys(record)) {
35
36
  if (!seen.has(key) && isInsertableField(meta, record, key)) {
@@ -39,16 +40,8 @@ function addInsertFieldKeys(meta, record, seen, keys) {
39
40
  }
40
41
  }
41
42
  /**
42
- * Resolves the columns of an INSERT statement: the union of the persistable fields provided by
43
- * any record (in first-seen order), plus every `onInsert` field. Records missing one of these
44
- * columns insert its database default.
45
- *
46
- * The column list is seeded from the first record, then extended by every other record's
47
- * not-yet-seen columns - a no-op scan for a homogeneous batch (every record the same shape).
48
- *
49
- * `onInsert` fields are always included so the column set is stable whether or not the caller
50
- * has run {@link fillOnFields} first (it stamps them on every record, but the querier's
51
- * chunk-size estimate inspects the raw payload).
43
+ * An insert's columns: every record's writable fields in first-seen order, plus every `onInsert` field,
44
+ * whether or not it was filled yet. A record missing one writes its default.
52
45
  */
53
46
  export function getInsertFieldKeys(meta, payloads) {
54
47
  const seen = new Set();
@@ -186,7 +179,7 @@ export function isVectorSearch(value) {
186
179
  * which, as they did when one took the first and the other the last.
187
180
  */
188
181
  export function findVectorSort(sort) {
189
- for (const key of getKeys(sort ?? {})) {
182
+ for (const key of getKeys(sort)) {
190
183
  const search = sort?.[key];
191
184
  // The guard narrows here, where a `.find()` over entries would hand back an untyped tuple.
192
185
  if (isVectorSearch(search)) {
@@ -259,17 +252,9 @@ export function withoutSoftDeleteFilter(filters) {
259
252
  return filters === false ? false : { ...filters, [SOFT_DELETE_FILTER]: false };
260
253
  }
261
254
  /**
262
- * Returns a new `$where` map with every active entity filter's condition merged in, resolving
263
- * parameterized conditions against the explicit or ambient {@link UqlContext}. Never mutates the input.
264
- *
265
- * Convenience filters are active by default (unless `opts.filters === false` or bypassed by name), and
266
- * their keys are applied only when absent from the map, so an explicit `$where` on that key opts out.
267
- *
268
- * `security` filters are always active (bypass is ignored) and AND-merged, so a client `$where` on the
269
- * same field can't override them. A security condition that returns `undefined` fails the query closed
270
- * (throws {@link UqlSecurityError}) unless its `onMissing` is `skip`; one that returns an empty object
271
- * (`{}`) resolved to "no restriction" and adds nothing - the escape hatch for trusted cross-tenant
272
- * work (e.g. a maintenance job running under a `system` context).
255
+ * `$where` with the entity's active filters merged in, against the ambient {@link UqlContext}. A convenience
256
+ * filter yields to a `$where` on its key; a `security` one is always ANDed, and throws where its condition
257
+ * resolves to nothing, unless `onMissing: 'skip'`.
273
258
  */
274
259
  export function applyFilters(meta, whereMap, opts) {
275
260
  if (!meta.filters) {
@@ -323,10 +308,8 @@ export function applyFilters(meta, whereMap, opts) {
323
308
  return result;
324
309
  }
325
310
  /**
326
- * The `$size` of a relation condition (`{ comments: { $size: { $gte: 2 } } }`), or `undefined` when the
327
- * condition constrains the target's fields instead. Shared so every dialect agrees on which of the two
328
- * a relation key means, and so the ambiguous mix fails loudly: `{ $size: 2, name: 'x' }` used to fall
329
- * through to field filtering and emit a condition on a `$size` *column*.
311
+ * The `$size` of a relation condition, `{ comments: { $size: { $gte: 2 } } }`, or `undefined` where it
312
+ * filters the target's fields; a mix of the two, `{ $size: 2, name: 'x' }`, is refused.
330
313
  */
331
314
  export function parseRelationSize(val) {
332
315
  if (!val || typeof val !== 'object' || !('$size' in val)) {