turbine-orm 0.50.0 → 0.51.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 (186) hide show
  1. package/README.md +66 -66
  2. package/dist/adapters/cockroachdb.d.ts +5 -5
  3. package/dist/adapters/cockroachdb.js +10 -10
  4. package/dist/adapters/index.d.ts +5 -5
  5. package/dist/adapters/index.js +7 -7
  6. package/dist/adapters/yugabytedb.d.ts +7 -7
  7. package/dist/adapters/yugabytedb.js +10 -10
  8. package/dist/cjs/adapters/cockroachdb.d.ts +5 -5
  9. package/dist/cjs/adapters/cockroachdb.js +10 -10
  10. package/dist/cjs/adapters/index.d.ts +5 -5
  11. package/dist/cjs/adapters/index.js +7 -7
  12. package/dist/cjs/adapters/yugabytedb.d.ts +7 -7
  13. package/dist/cjs/adapters/yugabytedb.js +10 -10
  14. package/dist/cjs/cli/config.d.ts +13 -2
  15. package/dist/cjs/cli/config.js +3 -2
  16. package/dist/cjs/cli/destructive.d.ts +1 -1
  17. package/dist/cjs/cli/destructive.js +1 -1
  18. package/dist/cjs/cli/index.d.ts +10 -10
  19. package/dist/cjs/cli/index.js +49 -45
  20. package/dist/cjs/cli/loader.d.ts +7 -7
  21. package/dist/cjs/cli/loader.js +9 -9
  22. package/dist/cjs/cli/mcp.js +4 -4
  23. package/dist/cjs/cli/migrate.d.ts +5 -5
  24. package/dist/cjs/cli/migrate.js +11 -11
  25. package/dist/cjs/cli/studio-ui.generated.js +1 -1
  26. package/dist/cjs/cli/ui.d.ts +2 -2
  27. package/dist/cjs/cli/ui.js +2 -2
  28. package/dist/cjs/client.d.ts +49 -38
  29. package/dist/cjs/client.js +57 -56
  30. package/dist/cjs/dialect.d.ts +62 -18
  31. package/dist/cjs/dialect.js +40 -2
  32. package/dist/cjs/errors.d.ts +5 -5
  33. package/dist/cjs/errors.js +11 -11
  34. package/dist/cjs/generate.d.ts +6 -6
  35. package/dist/cjs/generate.js +31 -29
  36. package/dist/cjs/index-advisor.d.ts +5 -5
  37. package/dist/cjs/index-advisor.js +0 -0
  38. package/dist/cjs/index.d.ts +1 -1
  39. package/dist/cjs/index.js +7 -7
  40. package/dist/cjs/introspect.d.ts +35 -9
  41. package/dist/cjs/introspect.js +83 -32
  42. package/dist/cjs/mssql.d.ts +11 -11
  43. package/dist/cjs/mssql.js +64 -29
  44. package/dist/cjs/mysql.d.ts +8 -8
  45. package/dist/cjs/mysql.js +61 -23
  46. package/dist/cjs/nested-write.d.ts +21 -2
  47. package/dist/cjs/nested-write.js +51 -14
  48. package/dist/cjs/optional-peer-import.cjs +7 -7
  49. package/dist/cjs/optional-peer-import.d.cts +7 -7
  50. package/dist/cjs/pipeline-submittable.d.ts +2 -2
  51. package/dist/cjs/pipeline-submittable.js +6 -6
  52. package/dist/cjs/pipeline.d.ts +1 -1
  53. package/dist/cjs/pipeline.js +4 -4
  54. package/dist/cjs/powdb-introspect.d.ts +1 -1
  55. package/dist/cjs/powdb-introspect.js +1 -1
  56. package/dist/cjs/powdb.d.ts +28 -28
  57. package/dist/cjs/powdb.js +66 -66
  58. package/dist/cjs/powql.d.ts +27 -27
  59. package/dist/cjs/powql.js +73 -52
  60. package/dist/cjs/query/aggregates.d.ts +1 -1
  61. package/dist/cjs/query/aggregates.js +5 -5
  62. package/dist/cjs/query/batched-loader.d.ts +11 -11
  63. package/dist/cjs/query/batched-loader.js +24 -24
  64. package/dist/cjs/query/builder.d.ts +39 -21
  65. package/dist/cjs/query/builder.js +99 -57
  66. package/dist/cjs/query/compound-unique.d.ts +1 -1
  67. package/dist/cjs/query/compound-unique.js +0 -0
  68. package/dist/cjs/query/deferred.d.ts +12 -6
  69. package/dist/cjs/query/deferred.js +1 -1
  70. package/dist/cjs/query/filters.d.ts +31 -11
  71. package/dist/cjs/query/filters.js +67 -14
  72. package/dist/cjs/query/index.d.ts +1 -1
  73. package/dist/cjs/query/index.js +1 -1
  74. package/dist/cjs/query/relations.d.ts +9 -9
  75. package/dist/cjs/query/relations.js +164 -57
  76. package/dist/cjs/query/types.d.ts +86 -35
  77. package/dist/cjs/query/types.js +1 -1
  78. package/dist/cjs/query/utils.d.ts +27 -10
  79. package/dist/cjs/query/utils.js +86 -14
  80. package/dist/cjs/query/where.d.ts +47 -28
  81. package/dist/cjs/query/where.js +130 -31
  82. package/dist/cjs/query/writes.d.ts +24 -5
  83. package/dist/cjs/query/writes.js +102 -13
  84. package/dist/cjs/realtime.d.ts +7 -7
  85. package/dist/cjs/realtime.js +9 -9
  86. package/dist/cjs/schema-builder.d.ts +18 -7
  87. package/dist/cjs/schema-builder.js +17 -10
  88. package/dist/cjs/schema-metadata.d.ts +3 -3
  89. package/dist/cjs/schema-metadata.js +9 -9
  90. package/dist/cjs/schema-sql.d.ts +9 -9
  91. package/dist/cjs/schema-sql.js +20 -20
  92. package/dist/cjs/schema.d.ts +19 -9
  93. package/dist/cjs/schema.js +6 -6
  94. package/dist/cjs/serverless.d.ts +15 -15
  95. package/dist/cjs/serverless.js +16 -16
  96. package/dist/cjs/sqlite.d.ts +8 -8
  97. package/dist/cjs/sqlite.js +53 -22
  98. package/dist/cjs/typed-sql.d.ts +4 -4
  99. package/dist/cjs/typed-sql.js +5 -5
  100. package/dist/cli/config.d.ts +13 -2
  101. package/dist/cli/config.js +3 -2
  102. package/dist/cli/destructive.d.ts +1 -1
  103. package/dist/cli/destructive.js +1 -1
  104. package/dist/cli/index.d.ts +10 -10
  105. package/dist/cli/index.js +49 -45
  106. package/dist/cli/loader.d.ts +7 -7
  107. package/dist/cli/loader.js +9 -9
  108. package/dist/cli/mcp.js +4 -4
  109. package/dist/cli/migrate.d.ts +5 -5
  110. package/dist/cli/migrate.js +11 -11
  111. package/dist/cli/studio-ui.generated.js +1 -1
  112. package/dist/cli/ui.d.ts +2 -2
  113. package/dist/cli/ui.js +2 -2
  114. package/dist/client.d.ts +49 -38
  115. package/dist/client.js +57 -56
  116. package/dist/dialect.d.ts +62 -18
  117. package/dist/dialect.js +40 -2
  118. package/dist/errors.d.ts +5 -5
  119. package/dist/errors.js +11 -11
  120. package/dist/generate.d.ts +6 -6
  121. package/dist/generate.js +31 -29
  122. package/dist/index-advisor.d.ts +5 -5
  123. package/dist/index-advisor.js +0 -0
  124. package/dist/index.d.ts +1 -1
  125. package/dist/index.js +7 -7
  126. package/dist/introspect.d.ts +35 -9
  127. package/dist/introspect.js +82 -32
  128. package/dist/mssql.d.ts +11 -11
  129. package/dist/mssql.js +64 -29
  130. package/dist/mysql.d.ts +8 -8
  131. package/dist/mysql.js +61 -23
  132. package/dist/nested-write.d.ts +21 -2
  133. package/dist/nested-write.js +51 -14
  134. package/dist/optional-peer-import.cjs +7 -7
  135. package/dist/optional-peer-import.d.cts +7 -7
  136. package/dist/pipeline-submittable.d.ts +2 -2
  137. package/dist/pipeline-submittable.js +6 -6
  138. package/dist/pipeline.d.ts +1 -1
  139. package/dist/pipeline.js +4 -4
  140. package/dist/powdb-introspect.d.ts +1 -1
  141. package/dist/powdb-introspect.js +1 -1
  142. package/dist/powdb.d.ts +28 -28
  143. package/dist/powdb.js +66 -66
  144. package/dist/powql.d.ts +27 -27
  145. package/dist/powql.js +73 -52
  146. package/dist/query/aggregates.d.ts +1 -1
  147. package/dist/query/aggregates.js +5 -5
  148. package/dist/query/batched-loader.d.ts +11 -11
  149. package/dist/query/batched-loader.js +24 -24
  150. package/dist/query/builder.d.ts +39 -21
  151. package/dist/query/builder.js +100 -58
  152. package/dist/query/compound-unique.d.ts +1 -1
  153. package/dist/query/compound-unique.js +0 -0
  154. package/dist/query/deferred.d.ts +12 -6
  155. package/dist/query/deferred.js +1 -1
  156. package/dist/query/filters.d.ts +31 -11
  157. package/dist/query/filters.js +66 -13
  158. package/dist/query/index.d.ts +1 -1
  159. package/dist/query/index.js +1 -1
  160. package/dist/query/relations.d.ts +9 -9
  161. package/dist/query/relations.js +165 -58
  162. package/dist/query/types.d.ts +86 -35
  163. package/dist/query/types.js +1 -1
  164. package/dist/query/utils.d.ts +27 -10
  165. package/dist/query/utils.js +84 -14
  166. package/dist/query/where.d.ts +47 -28
  167. package/dist/query/where.js +129 -32
  168. package/dist/query/writes.d.ts +24 -5
  169. package/dist/query/writes.js +101 -13
  170. package/dist/realtime.d.ts +7 -7
  171. package/dist/realtime.js +9 -9
  172. package/dist/schema-builder.d.ts +18 -7
  173. package/dist/schema-builder.js +17 -10
  174. package/dist/schema-metadata.d.ts +3 -3
  175. package/dist/schema-metadata.js +9 -9
  176. package/dist/schema-sql.d.ts +9 -9
  177. package/dist/schema-sql.js +20 -20
  178. package/dist/schema.d.ts +19 -9
  179. package/dist/schema.js +6 -6
  180. package/dist/serverless.d.ts +15 -15
  181. package/dist/serverless.js +16 -16
  182. package/dist/sqlite.d.ts +8 -8
  183. package/dist/sqlite.js +53 -22
  184. package/dist/typed-sql.d.ts +4 -4
  185. package/dist/typed-sql.js +5 -5
  186. package/package.json +2 -2
@@ -1,19 +1,19 @@
1
1
  /**
2
- * turbine-orm Batched relation loader (the `relationLoadStrategy: 'batched'` path)
2
+ * turbine-orm, Batched relation loader (the `relationLoadStrategy: 'batched'` path)
3
3
  *
4
4
  * ## Why this exists
5
5
  *
6
6
  * Turbine's default `with`-clause strategy resolves nested relations in ONE SQL
7
- * statement using correlated `json_agg(json_build_object(...))` subqueries one
7
+ * statement using correlated `json_agg(json_build_object(...))` subqueries, one
8
8
  * probe per parent row (see `buildRelationSubquery` in builder.ts). That is the
9
9
  * right default: a single round-trip, and when the child FK columns are indexed
10
10
  * each probe is an index seek. But it degrades in two situations:
11
11
  *
12
- * 1. **Missing FK index** a correlated probe per parent row becomes
12
+ * 1. **Missing FK index**, a correlated probe per parent row becomes
13
13
  * N-parents × full-table-scan. A batched-loader ORM pays that missing index
14
14
  * only ONCE (a single `WHERE fk = ANY($1)` seq-scan), which is why schemas
15
15
  * migrated from those ORMs often lack the index the json_agg path needs.
16
- * 2. **Huge unpaginated result sets** the JSON wire format
16
+ * 2. **Huge unpaginated result sets**, the JSON wire format
17
17
  * (`json_build_object` per row, re-serialized inside `json_agg`) is heavy to
18
18
  * encode/decode compared with flat rows.
19
19
  *
@@ -29,19 +29,19 @@
29
29
  * - **Same executor / connection path.** Every follow-up query runs through the
30
30
  * caller's own executor ({@link RelationLoadContext.exec}) and child query
31
31
  * interfaces built on the caller's pool. Inside a `$transaction` that pool is
32
- * the pinned-connection `txPool`, so batched loads join the transaction no
32
+ * the pinned-connection `txPool`, so batched loads join the transaction, no
33
33
  * separate pool checkout per query.
34
34
  * - **Identical output shape.** The stitched result is byte-for-byte the same
35
35
  * shape the join strategy produces: relation arrays for hasMany/manyToMany
36
36
  * (`[]` when empty), single-or-null for hasOne/belongsTo, with the same
37
- * camelCase keys and Date coercion because the child rows are parsed by the
37
+ * camelCase keys and Date coercion, because the child rows are parsed by the
38
38
  * very same `parseRow`/`buildFindMany` machinery via a child QueryInterface.
39
39
  * - **Stitch keys never leak.** To stitch, the follow-up query must select the
40
40
  * FK/PK it joins on even when the caller's `select`/`omit` excluded it; the
41
41
  * loader adds those columns for the query and strips them from the returned
42
42
  * entities afterwards ({@link includeKeysForBatching}).
43
43
  *
44
- * PowDB (powql.ts) has its own batched loaders for the same reasons this is the
44
+ * PowDB (powql.ts) has its own batched loaders for the same reasons, this is the
45
45
  * clean Postgres/SQL implementation, deliberately NOT shared with PowQL.
46
46
  *
47
47
  * @module
@@ -52,14 +52,14 @@ import { isRelationPickOrderBy } from './filters.js';
52
52
  import { ownLookup } from './utils.js';
53
53
  /**
54
54
  * Max parent keys per follow-up query. On Postgres the whole key set travels as
55
- * ONE array parameter (`= ANY($1)`), so this is not a bind-parameter limit it
55
+ * ONE array parameter (`= ANY($1)`), so this is not a bind-parameter limit, it
56
56
  * only bounds planner/memory cost per statement. Keep it large: every extra
57
57
  * chunk is an extra network round-trip, and round-trips are exactly what the
58
58
  * batched strategy exists to minimize (a 9-chunk load was measured 2× slower
59
59
  * than a single-statement one over a WAN link).
60
60
  */
61
61
  const MAX_RELATION_KEYS = 32_000;
62
- /** Nesting cap parity with the join strategy's depth-10 guard. */
62
+ /** Nesting cap, parity with the join strategy's depth-10 guard. */
63
63
  const MAX_DEPTH = 10;
64
64
  /**
65
65
  * The default projection of `meta` expressed in FIELD names: which fields the
@@ -91,7 +91,7 @@ export function defaultProjectionFields(meta, includePii) {
91
91
  *
92
92
  * Used both for the base query (parent keys) and each follow-up query (child
93
93
  * keys) so a caller's `select: { title: true }` on a relation still stitches even
94
- * though the FK was not requested and the FK never appears in the output.
94
+ * though the FK was not requested, and the FK never appears in the output.
95
95
  */
96
96
  export function includeKeysForBatching(select, omit, fields,
97
97
  /**
@@ -113,7 +113,7 @@ defaultProjection) {
113
113
  for (const f of unique) {
114
114
  if (!next[f]) {
115
115
  next[f] = true;
116
- strip.push(f); // not requested by the caller added only to stitch
116
+ strip.push(f); // not requested by the caller, added only to stitch
117
117
  }
118
118
  }
119
119
  return { select: next, omit, strip };
@@ -173,7 +173,7 @@ export function neededParentKeyFields(parentMeta, withClause) {
173
173
  }
174
174
  const rel = ownLookup(parentMeta.relations, relName);
175
175
  if (!rel)
176
- continue; // unknown relation the join path throws; let the loader surface it
176
+ continue; // unknown relation, the join path throws; let the loader surface it
177
177
  for (const col of localKeyColumns(rel)) {
178
178
  fields.add(parentMeta.reverseColumnMap[col] ?? col);
179
179
  }
@@ -222,18 +222,18 @@ export function resolveCountRelations(parentMeta, countSpec) {
222
222
  }
223
223
  return out;
224
224
  }
225
- /** Stringified stitch key robust to number/uuid/bigint type drift across a join. */
225
+ /** Stringified stitch key, robust to number/uuid/bigint type drift across a join. */
226
226
  function keyOf(value) {
227
227
  return String(value);
228
228
  }
229
229
  /**
230
- * Reject pick-row relation ordering anywhere inside a `with` tree's orderBy
230
+ * Reject pick-row relation ordering anywhere inside a `with` tree's orderBy -
231
231
  * strategy parity with the join path, which throws this exact E003 at SQL
232
232
  * build time (`pickOrderNestedError` in builder.ts). Without this guard the
233
233
  * loaders would forward `options.orderBy` as the child reader's TOP-LEVEL
234
- * findMany orderBy, where the pick shape compiles fine so the same query
234
+ * findMany orderBy, where the pick shape compiles fine, so the same query
235
235
  * would execute on 'batched' but throw on 'join'. Walks the whole tree up
236
- * front so acceptance never depends on which levels have rows the batched
236
+ * front so acceptance never depends on which levels have rows, the batched
237
237
  * runners in builder.ts call this BEFORE the base query (a zero-row base
238
238
  * result must still reject, exactly like the join strategy's build-time throw).
239
239
  */
@@ -269,13 +269,13 @@ export async function loadRelationsBatched(ctx, parents, withClause, timeout, de
269
269
  if (parents.length === 0)
270
270
  return;
271
271
  // Sibling relations are independent (each writes only its own parent[relName]
272
- // and reads only parent keys), so load them concurrently on a pool that's
272
+ // and reads only parent keys), so load them concurrently, on a pool that's
273
273
  // real parallelism, inside a transaction pg queues them on the one connection.
274
274
  const loads = [];
275
275
  for (const [relName, spec] of Object.entries(withClause)) {
276
276
  if (!spec)
277
277
  continue;
278
- // Reserved `_count` key one grouped COUNT(*) follow-up per counted relation.
278
+ // Reserved `_count` key, one grouped COUNT(*) follow-up per counted relation.
279
279
  if (relName === '_count') {
280
280
  loads.push(loadCounts(ctx, parents, spec));
281
281
  continue;
@@ -300,7 +300,7 @@ async function loadToOneOrMany(ctx, parents, rel, relName, options, timeout, dep
300
300
  const fk = normalizeKeyColumns(rel.foreignKey);
301
301
  const rk = normalizeKeyColumns(rel.referenceKey);
302
302
  if (fk.length > 1 || rk.length > 1) {
303
- throw new UnsupportedFeatureError('composite-key batched relation loading', 'relationLoadStrategy: "batched"', `relation "${relName}" use the default 'join' strategy for composite-key relations`);
303
+ throw new UnsupportedFeatureError('composite-key batched relation loading', 'relationLoadStrategy: "batched"', `relation "${relName}", use the default 'join' strategy for composite-key relations`);
304
304
  }
305
305
  const targetMeta = requireTable(ctx.schema, rel.to, relName);
306
306
  // Local key lives on the parent; the correlating key lives on the child.
@@ -375,7 +375,7 @@ async function loadManyToMany(ctx, parents, rel, relName, options, timeout, dept
375
375
  const sourceRef = normalizeKeyColumns(rel.referenceKey);
376
376
  const targetMeta = requireTable(ctx.schema, rel.to, relName);
377
377
  if (sourceJ.length > 1 || targetJ.length > 1 || sourceRef.length > 1 || targetMeta.primaryKey.length !== 1) {
378
- throw new UnsupportedFeatureError('composite-key batched manyToMany loading', 'relationLoadStrategy: "batched"', `relation "${relName}" use the default 'join' strategy for composite-key m2m relations`);
378
+ throw new UnsupportedFeatureError('composite-key batched manyToMany loading', 'relationLoadStrategy: "batched"', `relation "${relName}", use the default 'join' strategy for composite-key m2m relations`);
379
379
  }
380
380
  const sourceJCol = sourceJ[0];
381
381
  const targetJCol = targetJ[0];
@@ -474,7 +474,7 @@ async function loadManyToMany(ctx, parents, rel, relName, options, timeout, dept
474
474
  * Load correlated `_count` values for the counted relations. One grouped
475
475
  * follow-up per relation (`SELECT key, COUNT(*) … WHERE key = ANY($1) GROUP BY
476
476
  * key`), attached onto each parent's `_count` object (0 when a parent has no
477
- * matching rows) byte-identical to the join strategy's `_count` output.
477
+ * matching rows), byte-identical to the join strategy's `_count` output.
478
478
  */
479
479
  async function loadCounts(ctx, parents, countSpec) {
480
480
  const rels = resolveCountRelations(ctx.parentMeta, countSpec);
@@ -499,7 +499,7 @@ async function loadOneCount(ctx, parents, rel) {
499
499
  const sourceRef = normalizeKeyColumns(rel.referenceKey);
500
500
  const sourceJ = normalizeKeyColumns(through.sourceKey);
501
501
  if (sourceRef.length > 1 || sourceJ.length > 1) {
502
- throw new UnsupportedFeatureError('composite-key batched _count', 'relationLoadStrategy: "batched"', `relation "${rel.name}" use the default 'join' strategy for composite-key m2m _count`);
502
+ throw new UnsupportedFeatureError('composite-key batched _count', 'relationLoadStrategy: "batched"', `relation "${rel.name}", use the default 'join' strategy for composite-key m2m _count`);
503
503
  }
504
504
  parentKeyCol = sourceRef[0];
505
505
  childTable = through.table;
@@ -510,7 +510,7 @@ async function loadOneCount(ctx, parents, rel) {
510
510
  const fk = normalizeKeyColumns(rel.foreignKey);
511
511
  const rk = normalizeKeyColumns(rel.referenceKey);
512
512
  if (fk.length > 1 || rk.length > 1) {
513
- throw new UnsupportedFeatureError('composite-key batched _count', 'relationLoadStrategy: "batched"', `relation "${rel.name}" use the default 'join' strategy for composite-key _count`);
513
+ throw new UnsupportedFeatureError('composite-key batched _count', 'relationLoadStrategy: "batched"', `relation "${rel.name}", use the default 'join' strategy for composite-key _count`);
514
514
  }
515
515
  parentKeyCol = rk[0];
516
516
  childTable = rel.to;
@@ -529,7 +529,7 @@ async function loadOneCount(ctx, parents, rel) {
529
529
  // two strategies return identical counts under a filter. hasMany filters
530
530
  // the counted table directly; m2m counts junction rows but restricts them
531
531
  // to junction rows whose TARGET survives the target table's filter via
532
- // EXISTS mirroring buildRelationCountExpr's EXISTS-on-target (which also
532
+ // EXISTS, mirroring buildRelationCountExpr's EXISTS-on-target (which also
533
533
  // skips the filter when the junction targetKey arity doesn't match the
534
534
  // target PK). Rendered after the $1 key array.
535
535
  let gf = null;
@@ -1,14 +1,14 @@
1
1
  /**
2
- * turbine-orm Query builder
2
+ * turbine-orm, Query builder
3
3
  *
4
4
  * Each table accessor (db.users, db.posts, etc.) returns a QueryInterface<T>
5
5
  * that builds parameterized SQL and executes it through the connection pool.
6
6
  *
7
7
  * Nested relations use json_build_object + json_agg subqueries for single-query
8
- * resolution a PostgreSQL-native approach that eliminates N+1 query patterns.
8
+ * resolution, a PostgreSQL-native approach that eliminates N+1 query patterns.
9
9
  *
10
10
  * Schema-driven: all column names, types, and relations come from introspected
11
- * metadata nothing is hardcoded.
11
+ * metadata, nothing is hardcoded.
12
12
  */
13
13
  import type pg from 'pg';
14
14
  import type { SchemaMetadata } from '../schema.js';
@@ -103,6 +103,7 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
103
103
  private readonly middlewares;
104
104
  private readonly defaultLimit?;
105
105
  private readonly warnOnUnlimited;
106
+ private readonly scopedConnect;
106
107
  private readonly utcTimestamps;
107
108
  private readonly preparedStatementsEnabled;
108
109
  /**
@@ -150,7 +151,7 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
150
151
  private readonly globalFilters?;
151
152
  /**
152
153
  * Tracks tables that have already triggered an unlimited-query warning so
153
- * the user is not spammed once per row. Per-instance state each
154
+ * the user is not spammed once per row. Per-instance state, each
154
155
  * QueryInterface is bound to a single table, so this set will only ever
155
156
  * contain at most one entry, but using a Set keeps the API consistent with
156
157
  * the audit's "Set<string>" guidance and leaves room for future
@@ -165,7 +166,7 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
165
166
  private readonly columnArrayTypeMap;
166
167
  /**
167
168
  * Columns whose type lives in a DIFFERENT schema than the introspected one
168
- * (ColumnMetadata.pgTypeSchema is recorded only in that case) such columns
169
+ * (ColumnMetadata.pgTypeSchema is recorded only in that case), such columns
169
170
  * must never receive this schema's `::"enum"` cast (see enumTypeForColumn).
170
171
  */
171
172
  private readonly crossSchemaTypeColumns;
@@ -179,7 +180,7 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
179
180
  private readonly camelDateFieldCache;
180
181
  /** True when this QI runs inside an active transaction (set via _txScoped option). */
181
182
  private readonly txScoped;
182
- /** Original options reference forwarded to child QIs in nested writes. */
183
+ /** Original options reference, forwarded to child QIs in nested writes. */
183
184
  private readonly options?;
184
185
  /** Set by executeWithMiddleware so queryWithTimeout can include it in events. */
185
186
  private currentAction;
@@ -195,7 +196,7 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
195
196
  /**
196
197
  * The active query's `skipGlobalFilters` opt-out, set at the top of each
197
198
  * `build*` method and read deep in the (synchronous) SQL-build + param-collect
198
- * tree so relation subqueries, relation filters, `_count`, and relation
199
+ * tree, so relation subqueries, relation filters, `_count`, and relation
199
200
  * `orderBy` all see it without threading it through dozens of signatures.
200
201
  * Only load-bearing when {@link globalFilters} is configured; build+collect are
201
202
  * synchronous per call, so this transient is never observed across an await.
@@ -240,7 +241,7 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
240
241
  * SQLite use ` LIMIT <ph>` and/or ` OFFSET <ph>`. SQL Server has no `LIMIT`, so
241
242
  * its dialect implements {@link Dialect.buildLimitOffset} to emit
242
243
  * `[ORDER BY (SELECT NULL)] OFFSET <off> ROWS [FETCH NEXT <lim> ROWS ONLY]`.
243
- * Param-push order (limit before offset) is owned by the caller and unchanged
244
+ * Param-push order (limit before offset) is owned by the caller and unchanged -
244
245
  * this only varies the SQL text, so PG output stays byte-identical.
245
246
  */
246
247
  private buildPagination;
@@ -431,7 +432,7 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
431
432
  * join for a to-one relation.
432
433
  *
433
434
  * Resolution order:
434
- * 1. an explicit `autoToOneJoinMaxRows` an instruction, used verbatim
435
+ * 1. an explicit `autoToOneJoinMaxRows`, an instruction, used verbatim
435
436
  * (no clamping: the caller has measured their own workload);
436
437
  * 2. the configured `autoRoundTripMs` divided by
437
438
  * {@link AUTO_JOIN_PENALTY_MS_PER_ROW}, clamped to
@@ -445,7 +446,7 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
445
446
  * put, so any single constant is wrong for someone by more than the margin it
446
447
  * is trying to save. Placing the switch AT the break-even is also what removes
447
448
  * the old cliff: two plans that cost the same at the boundary make the regret
448
- * there ~1.0x, rising only as the true row count moves away from it where
449
+ * there ~1.0x, rising only as the true row count moves away from it, where
449
450
  * the previous fixed 1000 put its WORST case (1.44x measured) immediately
450
451
  * below its own switch point.
451
452
  *
@@ -492,7 +493,7 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
492
493
  * Build the {@link RelationLoadContext} the batched loader needs, closing over
493
494
  * this interface's pool/dialect/executor. Child readers are constructed on the
494
495
  * SAME pool (so they join an active transaction) with `defaultLimit` cleared
495
- * and unlimited-warnings silenced a relation load must fetch every matching
496
+ * and unlimited-warnings silenced, a relation load must fetch every matching
496
497
  * child, and the per-relation `limit` is applied client-side by the loader.
497
498
  */
498
499
  private batchedContext;
@@ -582,7 +583,7 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
582
583
  *
583
584
  * - `'returning'` / `'output'`: the statement returns its own affected rows
584
585
  * (`RETURNING *` / `OUTPUT INSERTED.*`). Byte-identical to the historical
585
- * single `queryWithTimeout` + `transform(result)` path the PostgreSQL
586
+ * single `queryWithTimeout` + `transform(result)` path, the PostgreSQL
586
587
  * route is unchanged.
587
588
  * - `'reselect'`: the engine cannot return rows from a write, so the build
588
589
  * method attached a {@link DeferredQuery.reselect} plan that runs the
@@ -612,7 +613,7 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
612
613
  *
613
614
  * Middleware can inspect and log query parameters, measure timing, and
614
615
  * transform the result returned by `next()`. Note: query SQL is generated
615
- * BEFORE middleware runs `params.args` is a read-only snapshot, and
616
+ * BEFORE middleware runs, `params.args` is a read-only snapshot, and
616
617
  * mutating it does NOT change the executed SQL. Cross-cutting filters
617
618
  * (e.g. soft deletes) belong in the query itself: pass an explicit
618
619
  * `where: { deletedAt: null }` or wrap the table accessor in a small helper.
@@ -626,7 +627,7 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
626
627
  * strategy's shape for the one row.
627
628
  */
628
629
  private runFindUniqueBatched;
629
- buildFindUnique<W extends TypedWithClause<R> = {}>(args: FindUniqueArgs<T, R, W, Record<string, boolean> | undefined, Record<string, boolean> | undefined>): DeferredQuery<T | null>;
630
+ buildFindUnique<W extends TypedWithClause<R> = {}, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args: FindUniqueArgs<T, R, W, S, O>): DeferredQuery<QueryResult<T, R, W, S, O> | null>;
630
631
  findMany<W extends TypedWithClause<R> = {}, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args?: FindManyArgs<T, R, W, S, O>): Promise<QueryResult<T, R, W, S, O>[]>;
631
632
  /**
632
633
  * Return the engine's query plan for a {@link findMany}-shaped query as plain
@@ -672,12 +673,29 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
672
673
  * disabled in config).
673
674
  */
674
675
  private maybeWarnUnlimited;
676
+ /**
677
+ * Whether `where` can match at most one row, because it pins every column of
678
+ * the primary key or of some unique column set to a literal value.
679
+ *
680
+ * The unlimited-read warning is about accidentally fetching a whole table,
681
+ * so firing it on `findMany({ where: { id: 1 } })` is noise: that query is
682
+ * bounded by a uniqueness constraint just as firmly as by a `limit`, and a
683
+ * warning that cries wolf on correct code trains people to disable it.
684
+ *
685
+ * Deliberately conservative. Only DIRECT equality on a literal counts: an
686
+ * operator object (`{ id: { in: [...] } }`, `{ id: { gt: 1 } }`) can match
687
+ * many rows, and any `OR` / `NOT` / relation filter can widen the result, so
688
+ * anything that is not a plain scalar equality leaves the warning in place.
689
+ * A compound-unique SELECTOR (`{ orgId_userId: {...} }`) is expanded first,
690
+ * so both spellings are recognized.
691
+ */
692
+ private whereMatchesAtMostOneRow;
675
693
  /**
676
694
  * Recursively measure the maximum depth of a `with` clause tree.
677
695
  * Used by the dev-only deep-with warning guard.
678
696
  */
679
697
  private measureWithDepth;
680
- buildFindMany<W extends TypedWithClause<R> = {}>(args?: FindManyArgs<T, R, W, Record<string, boolean> | undefined, Record<string, boolean> | undefined>): DeferredQuery<T[]>;
698
+ buildFindMany<W extends TypedWithClause<R> = {}, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args?: FindManyArgs<T, R, W, S, O>): DeferredQuery<QueryResult<T, R, W, S, O>[]>;
681
699
  /**
682
700
  * Stream rows from a findMany query using PostgreSQL cursors.
683
701
  * Returns an AsyncIterable that yields individual rows, fetching in batches internally.
@@ -712,11 +730,11 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
712
730
  */
713
731
  findManyStream<W extends TypedWithClause<R> = {}, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args?: FindManyStreamArgs<T, R, W, S, O>): AsyncGenerator<QueryResult<T, R, W, S, O>, void, undefined>;
714
732
  findFirst<W extends TypedWithClause<R> = {}, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args?: FindManyArgs<T, R, W, S, O>): Promise<QueryResult<T, R, W, S, O> | null>;
715
- buildFindFirst<W extends TypedWithClause<R> = {}>(args?: FindManyArgs<T, R, W, Record<string, boolean> | undefined, Record<string, boolean> | undefined>): DeferredQuery<T | null>;
733
+ buildFindFirst<W extends TypedWithClause<R> = {}, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args?: FindManyArgs<T, R, W, S, O>): DeferredQuery<QueryResult<T, R, W, S, O> | null>;
716
734
  findFirstOrThrow<W extends TypedWithClause<R> = {}, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args?: FindManyArgs<T, R, W, S, O>): Promise<QueryResult<T, R, W, S, O>>;
717
- buildFindFirstOrThrow<W extends TypedWithClause<R> = {}>(args?: FindManyArgs<T, R, W, Record<string, boolean> | undefined, Record<string, boolean> | undefined>): DeferredQuery<T>;
735
+ buildFindFirstOrThrow<W extends TypedWithClause<R> = {}, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args?: FindManyArgs<T, R, W, S, O>): DeferredQuery<QueryResult<T, R, W, S, O>>;
718
736
  findUniqueOrThrow<W extends TypedWithClause<R> = {}, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args: FindUniqueArgs<T, R, W, S, O>): Promise<QueryResult<T, R, W, S, O>>;
719
- buildFindUniqueOrThrow<W extends TypedWithClause<R> = {}>(args: FindUniqueArgs<T, R, W, Record<string, boolean> | undefined, Record<string, boolean> | undefined>): DeferredQuery<T>;
737
+ buildFindUniqueOrThrow<W extends TypedWithClause<R> = {}, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args: FindUniqueArgs<T, R, W, S, O>): DeferredQuery<QueryResult<T, R, W, S, O>>;
720
738
  create(args: CreateArgs<T, R>): Promise<T>;
721
739
  createMany(args: CreateManyArgs<T>): Promise<T[]>;
722
740
  update(args: UpdateArgs<T, R>): Promise<T>;
@@ -831,12 +849,12 @@ export declare class QueryInterface<T extends object, R extends object = {}> {
831
849
  /**
832
850
  * Build ORDER BY clause from an object.
833
851
  *
834
- * Each value is either a plain direction (`'asc'`/`'desc'`) or for pgvector
835
- * columns a `{ distance: { to, metric, direction? } }` KNN ordering object.
852
+ * Each value is either a plain direction (`'asc'`/`'desc'`) or, for pgvector
853
+ * columns, a `{ distance: { to, metric, direction? } }` KNN ordering object.
836
854
  * Vector ordering binds the query vector as a `$n::vector` param, so a `params`
837
855
  * array MUST be supplied when a vector ordering may be present (top-level
838
856
  * findMany path). When `params` is omitted (groupBy / relation path) a vector
839
- * ordering throws KNN ordering is only supported at the top level.
857
+ * ordering throws, KNN ordering is only supported at the top level.
840
858
  */
841
859
  /** Parse a flat row: convert snake_case to camelCase + Date coercion */
842
860
  /**