turbine-orm 0.49.0 → 0.50.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 (157) hide show
  1. package/README.md +122 -39
  2. package/dist/cjs/adapters/cockroachdb.d.ts +39 -0
  3. package/dist/cjs/adapters/index.d.ts +110 -0
  4. package/dist/cjs/adapters/yugabytedb.d.ts +51 -0
  5. package/dist/cjs/cli/config.d.ts +181 -0
  6. package/dist/cjs/cli/config.js +32 -6
  7. package/dist/cjs/cli/destructive.d.ts +38 -0
  8. package/dist/cjs/cli/index.d.ts +359 -0
  9. package/dist/cjs/cli/index.js +228 -56
  10. package/dist/cjs/cli/loader.d.ts +61 -0
  11. package/dist/cjs/cli/mcp.d.ts +42 -0
  12. package/dist/cjs/cli/migrate.d.ts +356 -0
  13. package/dist/cjs/cli/migrate.js +131 -40
  14. package/dist/cjs/cli/observe-ui.d.ts +1 -0
  15. package/dist/cjs/cli/observe-ui.js +14 -5
  16. package/dist/cjs/cli/observe.d.ts +25 -0
  17. package/dist/cjs/cli/observe.js +49 -12
  18. package/dist/cjs/cli/pii-tags.d.ts +53 -0
  19. package/dist/cjs/cli/prisma-report.d.ts +33 -0
  20. package/dist/cjs/cli/prisma-report.js +73 -0
  21. package/dist/cjs/cli/prisma-resolve.d.ts +106 -0
  22. package/dist/cjs/cli/prisma-resolve.js +1 -0
  23. package/dist/cjs/cli/prisma-schema.d.ts +176 -0
  24. package/dist/cjs/cli/prisma-schema.js +82 -4
  25. package/dist/cjs/cli/rate-limit.d.ts +32 -0
  26. package/dist/cjs/cli/rate-limit.js +45 -0
  27. package/dist/cjs/cli/studio-demo.d.ts +43 -0
  28. package/dist/cjs/cli/studio-ui.generated.d.ts +1 -0
  29. package/dist/cjs/cli/studio.d.ts +207 -0
  30. package/dist/cjs/cli/studio.js +136 -71
  31. package/dist/cjs/cli/ui.d.ts +73 -0
  32. package/dist/cjs/cli/ui.js +51 -9
  33. package/dist/cjs/client.d.ts +837 -0
  34. package/dist/cjs/client.js +3 -0
  35. package/dist/cjs/dialect.d.ts +516 -0
  36. package/dist/cjs/dialect.js +37 -12
  37. package/dist/cjs/errors.d.ts +370 -0
  38. package/dist/cjs/generate.d.ts +137 -0
  39. package/dist/cjs/generate.js +39 -6
  40. package/dist/cjs/index-advisor.d.ts +153 -0
  41. package/dist/cjs/index-stats.d.ts +384 -0
  42. package/dist/cjs/index.d.ts +55 -0
  43. package/dist/cjs/index.js +7 -2
  44. package/dist/cjs/introspect.d.ts +269 -0
  45. package/dist/cjs/mssql.d.ts +232 -0
  46. package/dist/cjs/mssql.js +6 -0
  47. package/dist/cjs/mysql.d.ts +173 -0
  48. package/dist/cjs/mysql.js +16 -0
  49. package/dist/cjs/nested-write.d.ts +96 -0
  50. package/dist/cjs/nested-write.js +414 -24
  51. package/dist/cjs/observe.d.ts +115 -0
  52. package/dist/cjs/optional-peer-import.d.cts +72 -0
  53. package/dist/cjs/pipeline-submittable.d.ts +93 -0
  54. package/dist/cjs/pipeline.d.ts +71 -0
  55. package/dist/cjs/powdb-introspect.d.ts +84 -0
  56. package/dist/cjs/powdb.d.ts +931 -0
  57. package/dist/cjs/powdb.js +106 -21
  58. package/dist/cjs/powql.d.ts +592 -0
  59. package/dist/cjs/powql.js +42 -6
  60. package/dist/cjs/prisma-compat.d.ts +283 -0
  61. package/dist/cjs/prisma-compat.js +167 -9
  62. package/dist/cjs/query/aggregates.d.ts +92 -0
  63. package/dist/cjs/query/aggregates.js +7 -3
  64. package/dist/cjs/query/batched-loader.d.ts +193 -0
  65. package/dist/cjs/query/builder.d.ts +849 -0
  66. package/dist/cjs/query/builder.js +571 -65
  67. package/dist/cjs/query/compound-unique.d.ts +51 -0
  68. package/dist/cjs/query/deferred.d.ts +223 -0
  69. package/dist/cjs/query/filters.d.ts +201 -0
  70. package/dist/cjs/query/index.d.ts +14 -0
  71. package/dist/cjs/query/index.js +6 -1
  72. package/dist/cjs/query/relations.d.ts +609 -0
  73. package/dist/cjs/query/relations.js +693 -46
  74. package/dist/cjs/query/types.d.ts +1300 -0
  75. package/dist/cjs/query/utils.d.ts +209 -0
  76. package/dist/cjs/query/utils.js +208 -1
  77. package/dist/cjs/query/warn-registry.d.ts +68 -0
  78. package/dist/cjs/query/warn-registry.js +9 -0
  79. package/dist/cjs/query/where-compile.d.ts +139 -0
  80. package/dist/cjs/query/where.d.ts +548 -0
  81. package/dist/cjs/query/where.js +58 -22
  82. package/dist/cjs/query/writes.d.ts +172 -0
  83. package/dist/cjs/query/writes.js +105 -12
  84. package/dist/cjs/realtime.d.ts +70 -0
  85. package/dist/cjs/schema-builder.d.ts +354 -0
  86. package/dist/cjs/schema-metadata.d.ts +83 -0
  87. package/dist/cjs/schema-sql.d.ts +217 -0
  88. package/dist/cjs/schema-sql.js +23 -5
  89. package/dist/cjs/schema.d.ts +356 -0
  90. package/dist/cjs/schema.js +125 -0
  91. package/dist/cjs/seed.d.ts +15 -0
  92. package/dist/cjs/serverless.d.ts +142 -0
  93. package/dist/cjs/sqlite.d.ts +143 -0
  94. package/dist/cjs/sqlite.js +4 -0
  95. package/dist/cjs/typed-sql.d.ts +102 -0
  96. package/dist/cli/config.d.ts +18 -4
  97. package/dist/cli/config.js +31 -6
  98. package/dist/cli/index.d.ts +123 -0
  99. package/dist/cli/index.js +223 -58
  100. package/dist/cli/migrate.d.ts +59 -10
  101. package/dist/cli/migrate.js +128 -41
  102. package/dist/cli/observe-ui.d.ts +1 -1
  103. package/dist/cli/observe-ui.js +14 -5
  104. package/dist/cli/observe.d.ts +7 -1
  105. package/dist/cli/observe.js +48 -12
  106. package/dist/cli/prisma-report.d.ts +14 -0
  107. package/dist/cli/prisma-report.js +72 -0
  108. package/dist/cli/prisma-resolve.d.ts +6 -0
  109. package/dist/cli/prisma-resolve.js +1 -0
  110. package/dist/cli/prisma-schema.d.ts +62 -2
  111. package/dist/cli/prisma-schema.js +81 -4
  112. package/dist/cli/rate-limit.d.ts +32 -0
  113. package/dist/cli/rate-limit.js +40 -0
  114. package/dist/cli/studio.d.ts +5 -5
  115. package/dist/cli/studio.js +135 -70
  116. package/dist/cli/ui.d.ts +1 -1
  117. package/dist/cli/ui.js +51 -9
  118. package/dist/client.d.ts +40 -0
  119. package/dist/client.js +3 -0
  120. package/dist/dialect.d.ts +17 -1
  121. package/dist/dialect.js +37 -12
  122. package/dist/generate.js +40 -7
  123. package/dist/index.d.ts +1 -1
  124. package/dist/index.js +1 -1
  125. package/dist/mssql.js +6 -0
  126. package/dist/mysql.js +16 -0
  127. package/dist/nested-write.d.ts +2 -0
  128. package/dist/nested-write.js +415 -25
  129. package/dist/powdb.d.ts +4 -2
  130. package/dist/powdb.js +106 -21
  131. package/dist/powql.d.ts +5 -0
  132. package/dist/powql.js +42 -6
  133. package/dist/prisma-compat.d.ts +2 -0
  134. package/dist/prisma-compat.js +166 -8
  135. package/dist/query/aggregates.js +7 -3
  136. package/dist/query/builder.d.ts +292 -21
  137. package/dist/query/builder.js +570 -64
  138. package/dist/query/deferred.d.ts +39 -0
  139. package/dist/query/index.d.ts +1 -1
  140. package/dist/query/index.js +1 -1
  141. package/dist/query/relations.d.ts +173 -5
  142. package/dist/query/relations.js +688 -47
  143. package/dist/query/types.d.ts +123 -39
  144. package/dist/query/utils.d.ts +116 -0
  145. package/dist/query/utils.js +198 -0
  146. package/dist/query/warn-registry.d.ts +9 -0
  147. package/dist/query/warn-registry.js +9 -0
  148. package/dist/query/where.d.ts +38 -1
  149. package/dist/query/where.js +58 -23
  150. package/dist/query/writes.d.ts +42 -1
  151. package/dist/query/writes.js +104 -13
  152. package/dist/schema-sql.d.ts +14 -0
  153. package/dist/schema-sql.js +23 -5
  154. package/dist/schema.d.ts +38 -0
  155. package/dist/schema.js +123 -0
  156. package/dist/sqlite.js +4 -0
  157. package/package.json +77 -28
@@ -38,7 +38,7 @@ export type OrderDirection = 'asc' | 'desc';
38
38
  * PowDB's own existing default (loaders / nested projections); it never selects
39
39
  * a distinct PowQL code path.
40
40
  */
41
- export type RelationLoadStrategy = 'join' | 'batched' | 'auto';
41
+ export type RelationLoadStrategy = 'join' | 'batched' | 'auto' | 'flatten';
42
42
  /**
43
43
  * Reference to ANOTHER COLUMN of the same table inside a where operator,
44
44
  * enabling column-to-column comparison:
@@ -100,34 +100,79 @@ export interface WhereOperator<V = unknown, F extends string = string> {
100
100
  * - A vector distance filter object ({ distance: { to, metric, lt } }) for pgvector columns
101
101
  */
102
102
  export type WhereValue<V = unknown, F extends string = string> = (V extends Array<infer U> ? TypedRelationFilter<U> : V extends Date ? V : V extends object ? V | TypedToOneFilter<V> | WhereClause<V> : V) | WhereOperator<V, F> | JsonFilter | ArrayFilter | TextSearchFilter | VectorFilter | null;
103
- /** Relation filter on a to-many relation property. */
104
- export interface TypedRelationFilter<U> {
105
- some?: WhereClause<U>;
106
- every?: WhereClause<U>;
107
- none?: WhereClause<U>;
103
+ /**
104
+ * Relation filter on a to-many relation property.
105
+ *
106
+ * `UR` is the target entity's own relations map, so a nested relation filter
107
+ * (`posts: { some: { comments: { some: ... } } }`) stays key-checked at depth.
108
+ * It defaults to `{}` so the legacy single-generic spelling still works.
109
+ */
110
+ export interface TypedRelationFilter<U, UR extends object = {}> {
111
+ some?: WhereClause<U, UR>;
112
+ every?: WhereClause<U, UR>;
113
+ none?: WhereClause<U, UR>;
108
114
  }
109
115
  /**
110
116
  * Relation filter on a to-one relation property. A bare object on a to-one
111
117
  * relation key is also accepted at runtime (implicit `is`, Prisma-compatible).
118
+ *
119
+ * `VR` is the target entity's own relations map (see {@link TypedRelationFilter}).
112
120
  */
113
- export interface TypedToOneFilter<V> {
114
- is?: WhereClause<V>;
115
- isNot?: WhereClause<V>;
121
+ export interface TypedToOneFilter<V, VR extends object = {}> {
122
+ is?: WhereClause<V, VR>;
123
+ isNot?: WhereClause<V, VR>;
116
124
  }
125
+ /**
126
+ * The where-value accepted on a RELATION key, derived from the generated
127
+ * {@link RelationDescriptor} brand: to-many relations take `some`/`every`/`none`,
128
+ * to-one relations take `is`/`isNot` or a bare sub-where (implicit `is`).
129
+ *
130
+ * Legacy generated shapes (`posts: Post[]`, `profile: Profile | null`) carry no
131
+ * brand, so they degrade to `unknown` — the key is still recognised (no typo
132
+ * false-positive) but its value is not checked.
133
+ */
134
+ type RelationWhereValue<Rel> = Rel extends RelationDescriptor<infer Target, infer Cardinality, infer TR> ? Cardinality extends 'many' ? TypedRelationFilter<Target, TR & object> : TypedToOneFilter<Target, TR & object> | WhereClause<Target, TR & object> : unknown;
117
135
  /**
118
136
  * Where clause type: each field can be a plain value, null, or operator object.
119
- * Special keys: OR for disjunctive conditions.
120
- * Relation names can be used with some/every/none sub-filters.
137
+ * Special keys: OR / AND / NOT.
138
+ *
139
+ * **Key checking.** Historically this type carried a
140
+ * `[relationName: string]: unknown` index signature so relation filters
141
+ * (`where: { posts: { some: ... } }`) would typecheck — the relation names are
142
+ * NOT keys of the entity `T`. That index signature also annihilated
143
+ * excess-property checking, so `where: { emial: 'x' }` compiled silently.
144
+ *
145
+ * Passing `R` (the generated `*Relations` map, which every typed client
146
+ * already threads through `QueryInterface<T, R>`) removes the need for the
147
+ * index signature: the relation keys become real, explicitly enumerated
148
+ * properties, so a misspelled column OR relation is a compile error at the
149
+ * offending key while every legitimate filter shape still typechecks.
150
+ *
151
+ * When `R` is omitted / `{}` (the untyped escape hatch, `defineSchema`-only
152
+ * clients, and any call site that does not thread the relations map) the type
153
+ * degrades to exactly its historical permissive form.
121
154
  */
122
- export type WhereClause<T> = {
155
+ export type WhereClause<T, R extends object = {}> = [keyof R] extends [never] ? LooseWhereClause<T> : StrictWhereClause<T, R>;
156
+ /** The historical, index-signature-carrying where clause. See {@link WhereClause}. */
157
+ export type LooseWhereClause<T> = {
123
158
  [K in keyof T]?: WhereValue<T[K], Extract<keyof T, string>>;
124
159
  } & {
125
- OR?: WhereClause<T>[];
126
- AND?: WhereClause<T>[];
127
- NOT?: WhereClause<T>;
160
+ OR?: LooseWhereClause<T>[];
161
+ AND?: LooseWhereClause<T>[];
162
+ NOT?: LooseWhereClause<T>;
128
163
  /** Relation filters — keyed by relation name, value is { some, every, none } */
129
164
  [relationName: string]: unknown;
130
165
  };
166
+ /** Key-checked where clause: column keys from `T`, relation keys from `R`, no index signature. */
167
+ type StrictWhereClause<T, R extends object> = {
168
+ [K in keyof T]?: WhereValue<T[K], Extract<keyof T, string>>;
169
+ } & {
170
+ [K in keyof R]?: RelationWhereValue<R[K]>;
171
+ } & {
172
+ OR?: WhereClause<T, R>[];
173
+ AND?: WhereClause<T, R>[];
174
+ NOT?: WhereClause<T, R>;
175
+ };
131
176
  /**
132
177
  * Client-level automatic WHERE filters, keyed by table accessor (the name used
133
178
  * in `db[name]` / `client.table(name)`). Each value is AND-merged into the
@@ -178,7 +223,7 @@ export interface WithClause {
178
223
  * `WithResult` inference.
179
224
  */
180
225
  export type TypedWithClause<R extends object = {}> = [keyof R] extends [never] ? WithClause : {
181
- [K in keyof R]?: true | WithOptions<RelationRelations<R[K]> & object>;
226
+ [K in keyof R]?: true | WithOptions<RelationRelations<R[K]> & object, RelationTarget<R[K]>>;
182
227
  } & {
183
228
  /** Reserved: correlated relation counts. `true` counts all to-many relations. */
184
229
  _count?: true | {
@@ -200,9 +245,19 @@ export type TypedWithClause<R extends object = {}> = [keyof R] extends [never] ?
200
245
  * {@link WithOptions.orderBy} is `WithOrderByObject[]`.
201
246
  */
202
247
  export type WithOrderByObject = Record<string, OrderDirection | OrderBySpec | JsonPathOrderBy | RelationOrderBy>;
203
- export interface WithOptions<NestedR extends object = {}> {
248
+ /**
249
+ * The `where` accepted inside a relation `with` block. When the relation
250
+ * target entity is known (a typed client, via the {@link RelationDescriptor}
251
+ * brand the generator emits) this is a key-checked {@link WhereClause} over the
252
+ * TARGET entity and its own relations, so `with: { posts: { where: { titel } } }`
253
+ * is a compile error. When the target is unknown (the untyped
254
+ * {@link WithClause} escape hatch) it degrades to the historical open record.
255
+ */
256
+ export type WithWhere<NestedT, NestedR extends object> = [unknown] extends [NestedT] ? Record<string, unknown> : WhereClause<NestedT & object, NestedR>;
257
+ export interface WithOptions<NestedR extends object = {}, NestedT = unknown> {
204
258
  with?: TypedWithClause<NestedR>;
205
- where?: Record<string, unknown>;
259
+ /** Filter the related rows. Keys are checked against the relation target when it is known (see {@link WithWhere}). */
260
+ where?: WithWhere<NestedT, NestedR>;
206
261
  /**
207
262
  * Order the related rows. Accepts a single object (`{ a: 'asc', b: 'desc' }`)
208
263
  * or a Prisma-style array of objects (`[{ a: 'asc' }, { b: 'desc' }]`, whose
@@ -316,6 +371,28 @@ type WithRelationAdditions<T, R extends object, W> = [keyof W & keyof R] extends
316
371
  type CountResult<C> = C extends true ? Record<string, number> : C extends object ? {
317
372
  [K in keyof C]: number;
318
373
  } : Record<string, number>;
374
+ /**
375
+ * Key-validating companion for an inferred `select` / `omit` flag map.
376
+ *
377
+ * `select?: S` alone can never reject a typo: `S` is inferred FROM the object
378
+ * literal, so `{ emial: true }` simply becomes the inferred type and a
379
+ * misspelled column silently narrows the result to `Pick<T, never>`. Writing
380
+ * the property as `S & FieldFlags<T, S>` keeps `S` inferred exactly as before
381
+ * (the naked `S` member is still the inference site, so literal `true` values
382
+ * and the `TrueKeys` result narrowing are unchanged) while the mapped member
383
+ * maps every key that is NOT a field of `T` to `never`, which makes the
384
+ * supplied `true` unassignable and reports the typo at the offending key.
385
+ *
386
+ * A key that IS a field of `T` maps to plain `boolean`, so every legitimate
387
+ * flag map (`{ id: true }`, `{ id: true, email: false }`, `{}`) is unaffected.
388
+ *
389
+ * An OPEN flag map (`Record<string, boolean>`, which is what the internal
390
+ * pass-through signatures instantiate `S` with) has nothing to validate: it is
391
+ * returned unchanged so those instantiations stay assignable to each other.
392
+ */
393
+ export type FieldFlags<T, S> = S extends object ? string extends keyof S ? S : {
394
+ [K in keyof S]: K extends keyof T ? boolean : never;
395
+ } : S;
319
396
  /** Extract keys from a boolean record where the value is `true`. */
320
397
  type TrueKeys<S extends Record<string, boolean>> = {
321
398
  [K in keyof S]: S[K] extends true ? K : never;
@@ -339,9 +416,12 @@ export type FieldResult<T, S extends Record<string, boolean> | undefined, O exte
339
416
  */
340
417
  export type QueryResult<T, R extends object, W, S extends Record<string, boolean> | undefined, O extends Record<string, boolean> | undefined> = S extends undefined ? O extends undefined ? WithResult<T, R, W> : O extends Record<string, boolean> ? Omit<WithResult<T, R, W>, Exclude<Extract<keyof T, TrueKeys<O>>, keyof W>> : WithResult<T, R, W> : S extends Record<string, boolean> ? Pick<WithResult<T, R, W>, Extract<keyof T, TrueKeys<S>> | Exclude<keyof WithResult<T, R, W>, keyof T> | Extract<keyof W, keyof WithResult<T, R, W>>> : WithResult<T, R, W>;
341
418
  export interface FindUniqueArgs<T, R extends object = {}, W extends TypedWithClause<R> = TypedWithClause<R>, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined> {
342
- where: WhereClause<T>;
343
- select?: S;
344
- omit?: O;
419
+ /** Row selector. Keys are checked against `T` and `R` (see {@link WhereClause}). */
420
+ where: WhereClause<T, R>;
421
+ /** Only return these fields. Keys are checked against `T` (see {@link FieldFlags}). */
422
+ select?: S & FieldFlags<T, S>;
423
+ /** Exclude these fields. Keys are checked against `T` (see {@link FieldFlags}). */
424
+ omit?: O & FieldFlags<T, O>;
345
425
  with?: W;
346
426
  /** Query timeout in milliseconds. Rejects with an error if exceeded. */
347
427
  timeout?: number;
@@ -355,9 +435,12 @@ export interface FindUniqueArgs<T, R extends object = {}, W extends TypedWithCla
355
435
  includePii?: boolean;
356
436
  }
357
437
  export interface FindManyArgs<T, R extends object = {}, W extends TypedWithClause<R> = TypedWithClause<R>, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined> {
358
- where?: WhereClause<T>;
359
- select?: S;
360
- omit?: O;
438
+ /** Row filter. Keys are checked against `T` and `R` (see {@link WhereClause}). */
439
+ where?: WhereClause<T, R>;
440
+ /** Only return these fields. Keys are checked against `T` (see {@link FieldFlags}). */
441
+ select?: S & FieldFlags<T, S>;
442
+ /** Exclude these fields. Keys are checked against `T` (see {@link FieldFlags}). */
443
+ omit?: O & FieldFlags<T, O>;
361
444
  orderBy?: OrderByClause;
362
445
  limit?: number;
363
446
  offset?: number;
@@ -465,7 +548,8 @@ export type UpdateInput<T> = {
465
548
  [K in keyof T]?: T[K] | UpdateOperatorInput<T[K]>;
466
549
  };
467
550
  export interface UpdateArgs<T, R extends object = {}> {
468
- where: WhereClause<T>;
551
+ /** Row selector. Keys are checked against `T` and `R` (see {@link WhereClause}). */
552
+ where: WhereClause<T, R>;
469
553
  /**
470
554
  * Update data. On typed clients, relation names additionally accept nested
471
555
  * write ops ({@link NestedUpdateOp}): `create` / `connect` / `connectOrCreate`
@@ -506,8 +590,8 @@ export interface UpdateArgs<T, R extends object = {}> {
506
590
  /** Opt out of configured {@link GlobalFilters}. See {@link SkipGlobalFilters}. */
507
591
  skipGlobalFilters?: SkipGlobalFilters;
508
592
  }
509
- export interface UpdateManyArgs<T> {
510
- where: WhereClause<T>;
593
+ export interface UpdateManyArgs<T, R extends object = {}> {
594
+ where: WhereClause<T, R>;
511
595
  data: UpdateInput<T>;
512
596
  /** Query timeout in milliseconds. Rejects with an error if exceeded. */
513
597
  timeout?: number;
@@ -516,8 +600,8 @@ export interface UpdateManyArgs<T> {
516
600
  /** Opt out of configured {@link GlobalFilters}. See {@link SkipGlobalFilters}. */
517
601
  skipGlobalFilters?: SkipGlobalFilters;
518
602
  }
519
- export interface DeleteArgs<T> {
520
- where: WhereClause<T>;
603
+ export interface DeleteArgs<T, R extends object = {}> {
604
+ where: WhereClause<T, R>;
521
605
  /** Query timeout in milliseconds. Rejects with an error if exceeded. */
522
606
  timeout?: number;
523
607
  /** See {@link UpdateArgs.allowFullTableScan}. */
@@ -525,8 +609,8 @@ export interface DeleteArgs<T> {
525
609
  /** Opt out of configured {@link GlobalFilters}. See {@link SkipGlobalFilters}. */
526
610
  skipGlobalFilters?: SkipGlobalFilters;
527
611
  }
528
- export interface DeleteManyArgs<T> {
529
- where: WhereClause<T>;
612
+ export interface DeleteManyArgs<T, R extends object = {}> {
613
+ where: WhereClause<T, R>;
530
614
  /** Query timeout in milliseconds. Rejects with an error if exceeded. */
531
615
  timeout?: number;
532
616
  /** See {@link UpdateArgs.allowFullTableScan}. */
@@ -534,8 +618,8 @@ export interface DeleteManyArgs<T> {
534
618
  /** Opt out of configured {@link GlobalFilters}. See {@link SkipGlobalFilters}. */
535
619
  skipGlobalFilters?: SkipGlobalFilters;
536
620
  }
537
- export interface UpsertArgs<T> {
538
- where: WhereClause<T>;
621
+ export interface UpsertArgs<T, R extends object = {}> {
622
+ where: WhereClause<T, R>;
539
623
  create: Partial<T>;
540
624
  update: Partial<T>;
541
625
  /** Query timeout in milliseconds. Rejects with an error if exceeded. */
@@ -594,8 +678,8 @@ export type CreateDataInput<T, R extends object = {}> = [keyof R] extends [never
594
678
  export type UpdateDataInput<T, R extends object = {}> = [keyof R] extends [never] ? UpdateInput<T> : UpdateInput<T> & {
595
679
  [K in keyof R]?: NestedUpdateOp<RelationTarget<R[K]> & object, RelationRelations<R[K]> & object>;
596
680
  };
597
- export interface CountArgs<T> {
598
- where?: WhereClause<T>;
681
+ export interface CountArgs<T, R extends object = {}> {
682
+ where?: WhereClause<T, R>;
599
683
  /** Query timeout in milliseconds. Rejects with an error if exceeded. */
600
684
  timeout?: number;
601
685
  /** Opt out of configured {@link GlobalFilters}. See {@link SkipGlobalFilters}. */
@@ -751,10 +835,10 @@ export interface GroupByOrderBy {
751
835
  /** A by-column field name or JSON group-key alias → direction or sort spec. */
752
836
  [key: string]: OrderDirection | OrderBySpec | GroupByAggregateOrderBy | undefined;
753
837
  }
754
- export interface GroupByArgs<T> {
838
+ export interface GroupByArgs<T, R extends object = {}> {
755
839
  /** Group keys: plain column field names and/or JSON-path keys ({@link JsonPathGroupKey}). */
756
840
  by: ((keyof T & string) | JsonPathGroupKey)[];
757
- where?: WhereClause<T>;
841
+ where?: WhereClause<T, R>;
758
842
  /**
759
843
  * PostgreSQL only: group over one representative row per column combination
760
844
  * (`SELECT DISTINCT ON … ORDER BY …` row source). See {@link GroupByDistinctOn}.
@@ -883,8 +967,8 @@ type GroupByCountPart<A> = A extends {
883
967
  _count: number;
884
968
  };
885
969
  /** Arguments for the standalone aggregate method */
886
- export interface AggregateArgs<T> {
887
- where?: WhereClause<T>;
970
+ export interface AggregateArgs<T, R extends object = {}> {
971
+ where?: WhereClause<T, R>;
888
972
  /**
889
973
  * Count rows. `true` → `_count: number` (COUNT(*)). The record form counts per
890
974
  * selection: the reserved `_all: true` key → COUNT(*), and each entity field
@@ -75,6 +75,61 @@ export declare const OPERATOR_KEYS: Set<string>;
75
75
  * For multi-column: `"alias"."col_a" = "parent"."ref_a" AND "alias"."col_b" = "parent"."ref_b"`
76
76
  */
77
77
  export declare function buildCorrelation(leftRef: string, leftColumns: string | string[], rightRef: string, rightColumns: string | string[]): string;
78
+ /**
79
+ * Render a JS `Date` as a TIME-OF-DAY literal for a `time` / `timetz` column.
80
+ *
81
+ * Which time of day? The **UTC** components of the Date, never the process
82
+ * local zone. That is what Prisma does (`new Date('1970-01-01T09:00:00Z')`
83
+ * written to a `@db.Time(6)` column stores `09:00:00`), and the affected
84
+ * consumers are porting from Prisma, so Prisma is the contract. It is also the
85
+ * only choice that round-trips: the same Date produces the same literal no
86
+ * matter where the process runs.
87
+ *
88
+ * `timetz` gets an explicit `+00:00`, because the value's zone IS UTC and
89
+ * omitting it would let Postgres attach the session's `TimeZone` instead.
90
+ * Fractional seconds are emitted only when non-zero, so an even-second Date
91
+ * binds the plain `HH:MM:SS` form.
92
+ */
93
+ export declare function toTimeOfDayLiteral(value: Date, kind: 'time' | 'timetz'): string;
94
+ /** The temporal column shapes that need a bound Date rewritten to a literal. */
95
+ export type TemporalBindKind = 'time' | 'timetz' | 'date' | 'timestamp';
96
+ /**
97
+ * Render a JS `Date` as a literal for a zone-less `date` / `timestamp` column,
98
+ * using the value's **UTC** components.
99
+ *
100
+ * This is the write-side mirror of `parseDbDate`, which reads an offset-less
101
+ * database value back as UTC. Without it the driver serializes the Date with
102
+ * the PROCESS's offset (`prepareValue` → `dateToString`), so a `timestamp`
103
+ * column is not round-trip stable outside a UTC process: writing
104
+ * `2026-07-25T00:00Z` from `America/Los_Angeles` stores
105
+ * `2026-07-24 17:00:00` and reads back as `2026-07-24T17:00Z`. It also matches
106
+ * the choice {@link toTimeOfDayLiteral} already makes for `time` columns, and
107
+ * Prisma, which writes UTC components to zone-less columns.
108
+ *
109
+ * `timestamptz` is NOT handled here (and must not be): it stores a real
110
+ * instant, so the driver's local-offset string is already correct.
111
+ */
112
+ export declare function toLocalDateTimeLiteral(value: Date, kind: 'date' | 'timestamp'): string;
113
+ /**
114
+ * Classify a column's database type for temporal bind rewriting.
115
+ *
116
+ * `utcDateTimes: false` restricts the classification to the time-of-day types,
117
+ * whose rewrite is a hard-error fix (Postgres rejects an ISO timestamp for a
118
+ * `time` column outright) rather than a value correction.
119
+ */
120
+ export declare function temporalBindKind(dbType: string | undefined, utcDateTimes?: boolean): TemporalBindKind | null;
121
+ /**
122
+ * Rewrite one bound value for a temporal column: a JS `Date` on a `time` /
123
+ * `timetz` / `date` / `timestamp` column becomes the corresponding UTC literal,
124
+ * and an array of Dates on such a column is rewritten element-wise (the
125
+ * per-element rewrite is what a `time[]` column needs, and matches the scalar
126
+ * case rather than silently binding an ISO timestamp).
127
+ *
128
+ * Everything else — every non-Date, every non-temporal column, and every
129
+ * `timestamptz` column — is returned by IDENTITY, so this is a byte-for-byte
130
+ * no-op outside the shapes above.
131
+ */
132
+ export declare function coerceTemporalValue(dbType: string | undefined, value: unknown, utcDateTimes?: boolean): unknown;
78
133
  /**
79
134
  * Parse a database date-time string deterministically.
80
135
  *
@@ -91,3 +146,64 @@ export declare function buildCorrelation(leftRef: string, leftColumns: string |
91
146
  * as-is.
92
147
  */
93
148
  export declare function parseDbDate(value: string): Date;
149
+ /**
150
+ * Postgres type name → OID, for every type family whose `json_build_object`
151
+ * rendering is NOT the value the pg driver produces for the same column.
152
+ *
153
+ * Why this table exists: the `'join'` strategy reads a relation through
154
+ * `json_agg(json_build_object(...))`, so its values are whatever
155
+ * `JSON.parse` makes of Postgres's JSON rendering. Every other read path in
156
+ * the library — a top-level row, `'batched'`, `'flatten'` — reads the column
157
+ * through the driver and gets the driver's representation. Measured against
158
+ * PostgreSQL 17, those two disagree for exactly the families below, which
159
+ * made the SAME query return a different JS type depending on which plan ran
160
+ * (and `'auto'` picks the plan from a row-count heuristic, so it could differ
161
+ * between two runs of one query). Three of these are lossy, not merely
162
+ * different:
163
+ *
164
+ * type driver (target) json_build_object
165
+ * ──────────── ──────────────────────────── ─────────────────────────────
166
+ * numeric '1000.50' (string) 1000.5 (number, LOSSY)
167
+ * int8 '9007199254740993' 9007199254740992 (LOSSY)
168
+ * bytea Buffer '\xdeadbeef' (string)
169
+ * date Date (local midnight) Date (UTC midnight, off by tz)
170
+ * interval { days, hours, … } '1 day 02:03:04' (string)
171
+ * point { x, y } '(1,2)' (string)
172
+ * circle { x, y, radius } '<(1,2),3>' (string)
173
+ *
174
+ * The array forms diverge the same way, plus `_timestamp`/`_timestamptz`
175
+ * (driver: `Date[]`; JSON: `string[]`) — the scalar `timestamp` /
176
+ * `timestamptz` are deliberately ABSENT because the existing `dateColumns`
177
+ * coercion in `parseRow` already lands them on the driver's value, and they
178
+ * are the hottest column type in a typical schema (no reason to add a cast to
179
+ * every `created_at`).
180
+ *
181
+ * The fix these OIDs drive: emit the column as `col::text` inside
182
+ * `json_build_object` so the JSON carries the same wire text the driver would
183
+ * receive, then run the DRIVER'S OWN parser for that OID over it. Parity is
184
+ * then by construction rather than by coincidence, and it automatically
185
+ * honours a caller's `pg.types.setTypeParser` (including the int8 parser
186
+ * TurbineClient itself registers) instead of second-guessing it.
187
+ *
188
+ * Postgres-only: the JSON functions and the divergence set are both
189
+ * engine-specific, so callers gate this on the postgres dialect.
190
+ */
191
+ export declare const JSON_WIRE_COERCION_OIDS: Readonly<Record<string, number>>;
192
+ /**
193
+ * The OID whose driver parser reproduces `pgType`'s driver representation from
194
+ * its text rendering, or `undefined` when the type's JSON rendering already
195
+ * matches the driver (the common case: text, uuid, bool, int4, float8, json,
196
+ * jsonb, arrays of those, …).
197
+ */
198
+ export declare function jsonWireCoercionOid(pgType: string | undefined): number | undefined;
199
+ /**
200
+ * Apply the driver's text parser for `oid` to a JSON-sourced wire string.
201
+ *
202
+ * Resolved through `pg.types.getTypeParser` on every call rather than
203
+ * memoized: parser registration is process-global and happens in the
204
+ * TurbineClient constructor (int8, and `timestamp` under `utcTimestamps`), and
205
+ * a caller may register their own at any point. A stale memo would silently
206
+ * reintroduce the very divergence this exists to remove. The lookup is a plain
207
+ * object index in pg-types, so it is not worth caching.
208
+ */
209
+ export declare function coerceJsonWireValue(oid: number, value: unknown): unknown;
@@ -3,6 +3,8 @@
3
3
  *
4
4
  * Standalone utility functions and classes used by the query builder.
5
5
  */
6
+ import pg from 'pg';
7
+ import { localDateTimeKind, timeOfDayKind } from '../schema.js';
6
8
  // ---------------------------------------------------------------------------
7
9
  // Identifier quoting — prevents SQL injection via table/column names
8
10
  // ---------------------------------------------------------------------------
@@ -141,6 +143,114 @@ export function buildCorrelation(leftRef, leftColumns, rightRef, rightColumns) {
141
143
  .map((col, i) => `${leftRef}.${quoteIdent(col)} = ${rightRef}.${quoteIdent(rightCols[i])}`)
142
144
  .join(' AND ');
143
145
  }
146
+ /**
147
+ * Render a JS `Date` as a TIME-OF-DAY literal for a `time` / `timetz` column.
148
+ *
149
+ * Which time of day? The **UTC** components of the Date, never the process
150
+ * local zone. That is what Prisma does (`new Date('1970-01-01T09:00:00Z')`
151
+ * written to a `@db.Time(6)` column stores `09:00:00`), and the affected
152
+ * consumers are porting from Prisma, so Prisma is the contract. It is also the
153
+ * only choice that round-trips: the same Date produces the same literal no
154
+ * matter where the process runs.
155
+ *
156
+ * `timetz` gets an explicit `+00:00`, because the value's zone IS UTC and
157
+ * omitting it would let Postgres attach the session's `TimeZone` instead.
158
+ * Fractional seconds are emitted only when non-zero, so an even-second Date
159
+ * binds the plain `HH:MM:SS` form.
160
+ */
161
+ export function toTimeOfDayLiteral(value, kind) {
162
+ const pad = (n, width = 2) => String(n).padStart(width, '0');
163
+ const ms = value.getUTCMilliseconds();
164
+ const literal = `${pad(value.getUTCHours())}:${pad(value.getUTCMinutes())}:${pad(value.getUTCSeconds())}` +
165
+ (ms === 0 ? '' : `.${pad(ms, 3)}`);
166
+ return kind === 'timetz' ? `${literal}+00:00` : literal;
167
+ }
168
+ /** Render the UTC calendar date of a `Date` as `YYYY-MM-DD`. */
169
+ function utcDatePart(value) {
170
+ const year = value.getUTCFullYear();
171
+ const y = year < 0 ? `-${String(-year).padStart(4, '0')}` : String(year).padStart(4, '0');
172
+ const pad = (n) => String(n).padStart(2, '0');
173
+ return `${y}-${pad(value.getUTCMonth() + 1)}-${pad(value.getUTCDate())}`;
174
+ }
175
+ /**
176
+ * Render a JS `Date` as a literal for a zone-less `date` / `timestamp` column,
177
+ * using the value's **UTC** components.
178
+ *
179
+ * This is the write-side mirror of `parseDbDate`, which reads an offset-less
180
+ * database value back as UTC. Without it the driver serializes the Date with
181
+ * the PROCESS's offset (`prepareValue` → `dateToString`), so a `timestamp`
182
+ * column is not round-trip stable outside a UTC process: writing
183
+ * `2026-07-25T00:00Z` from `America/Los_Angeles` stores
184
+ * `2026-07-24 17:00:00` and reads back as `2026-07-24T17:00Z`. It also matches
185
+ * the choice {@link toTimeOfDayLiteral} already makes for `time` columns, and
186
+ * Prisma, which writes UTC components to zone-less columns.
187
+ *
188
+ * `timestamptz` is NOT handled here (and must not be): it stores a real
189
+ * instant, so the driver's local-offset string is already correct.
190
+ */
191
+ export function toLocalDateTimeLiteral(value, kind) {
192
+ const datePart = utcDatePart(value);
193
+ if (kind === 'date')
194
+ return datePart;
195
+ return `${datePart} ${toTimeOfDayLiteral(value, 'time')}`;
196
+ }
197
+ /**
198
+ * Classify a column's database type for temporal bind rewriting.
199
+ *
200
+ * `utcDateTimes: false` restricts the classification to the time-of-day types,
201
+ * whose rewrite is a hard-error fix (Postgres rejects an ISO timestamp for a
202
+ * `time` column outright) rather than a value correction.
203
+ */
204
+ export function temporalBindKind(dbType, utcDateTimes = true) {
205
+ const timeKind = timeOfDayKind(dbType);
206
+ if (timeKind)
207
+ return timeKind;
208
+ return utcDateTimes ? localDateTimeKind(dbType) : null;
209
+ }
210
+ /**
211
+ * Rewrite one bound value for a temporal column: a JS `Date` on a `time` /
212
+ * `timetz` / `date` / `timestamp` column becomes the corresponding UTC literal,
213
+ * and an array of Dates on such a column is rewritten element-wise (the
214
+ * per-element rewrite is what a `time[]` column needs, and matches the scalar
215
+ * case rather than silently binding an ISO timestamp).
216
+ *
217
+ * Everything else — every non-Date, every non-temporal column, and every
218
+ * `timestamptz` column — is returned by IDENTITY, so this is a byte-for-byte
219
+ * no-op outside the shapes above.
220
+ */
221
+ export function coerceTemporalValue(dbType, value, utcDateTimes = true) {
222
+ const isDate = value instanceof Date;
223
+ if (!isDate && !Array.isArray(value))
224
+ return value;
225
+ if (isDate && Number.isNaN(value.getTime()))
226
+ return value;
227
+ // An array value is either an `in`/`notIn` list on a scalar temporal column
228
+ // (type already the element type) or the value of an array column, whose
229
+ // introspected type is the `_time` / `_timestamp` array spelling.
230
+ const kind = temporalBindKind(isDate ? dbType : arrayElementDbType(dbType), utcDateTimes);
231
+ if (!kind)
232
+ return value;
233
+ if (isDate)
234
+ return renderTemporal(value, kind);
235
+ // Rewrite only if the list actually holds a Date, so a string list stays
236
+ // byte-identical (and the same array instance is returned).
237
+ if (!value.some((v) => v instanceof Date))
238
+ return value;
239
+ return value.map((v) => (v instanceof Date && !Number.isNaN(v.getTime()) ? renderTemporal(v, kind) : v));
240
+ }
241
+ /** `_time` → `time`, `time[]` → `time`, anything else unchanged. */
242
+ function arrayElementDbType(dbType) {
243
+ if (!dbType)
244
+ return dbType;
245
+ if (dbType.startsWith('_'))
246
+ return dbType.slice(1);
247
+ return dbType.endsWith('[]') ? dbType.slice(0, -2) : dbType;
248
+ }
249
+ function renderTemporal(value, kind) {
250
+ return kind === 'date' || kind === 'timestamp'
251
+ ? toLocalDateTimeLiteral(value, kind)
252
+ : toTimeOfDayLiteral(value, kind);
253
+ }
144
254
  /**
145
255
  * Matches an explicit timezone suffix on a date-time string: a trailing `Z`
146
256
  * or a `±HH`, `±HHMM`, `±HH:MM` offset.
@@ -176,3 +286,91 @@ export function parseDbDate(value) {
176
286
  // normalize `YYYY-MM-DD HH:MM:SS` (driver form) to ISO before pinning UTC
177
287
  return new Date(`${value.replace(' ', 'T')}Z`);
178
288
  }
289
+ // ---------------------------------------------------------------------------
290
+ // JSON-wire value coercion (relationLoadStrategy: 'join')
291
+ // ---------------------------------------------------------------------------
292
+ /**
293
+ * Postgres type name → OID, for every type family whose `json_build_object`
294
+ * rendering is NOT the value the pg driver produces for the same column.
295
+ *
296
+ * Why this table exists: the `'join'` strategy reads a relation through
297
+ * `json_agg(json_build_object(...))`, so its values are whatever
298
+ * `JSON.parse` makes of Postgres's JSON rendering. Every other read path in
299
+ * the library — a top-level row, `'batched'`, `'flatten'` — reads the column
300
+ * through the driver and gets the driver's representation. Measured against
301
+ * PostgreSQL 17, those two disagree for exactly the families below, which
302
+ * made the SAME query return a different JS type depending on which plan ran
303
+ * (and `'auto'` picks the plan from a row-count heuristic, so it could differ
304
+ * between two runs of one query). Three of these are lossy, not merely
305
+ * different:
306
+ *
307
+ * type driver (target) json_build_object
308
+ * ──────────── ──────────────────────────── ─────────────────────────────
309
+ * numeric '1000.50' (string) 1000.5 (number, LOSSY)
310
+ * int8 '9007199254740993' 9007199254740992 (LOSSY)
311
+ * bytea Buffer '\xdeadbeef' (string)
312
+ * date Date (local midnight) Date (UTC midnight, off by tz)
313
+ * interval { days, hours, … } '1 day 02:03:04' (string)
314
+ * point { x, y } '(1,2)' (string)
315
+ * circle { x, y, radius } '<(1,2),3>' (string)
316
+ *
317
+ * The array forms diverge the same way, plus `_timestamp`/`_timestamptz`
318
+ * (driver: `Date[]`; JSON: `string[]`) — the scalar `timestamp` /
319
+ * `timestamptz` are deliberately ABSENT because the existing `dateColumns`
320
+ * coercion in `parseRow` already lands them on the driver's value, and they
321
+ * are the hottest column type in a typical schema (no reason to add a cast to
322
+ * every `created_at`).
323
+ *
324
+ * The fix these OIDs drive: emit the column as `col::text` inside
325
+ * `json_build_object` so the JSON carries the same wire text the driver would
326
+ * receive, then run the DRIVER'S OWN parser for that OID over it. Parity is
327
+ * then by construction rather than by coincidence, and it automatically
328
+ * honours a caller's `pg.types.setTypeParser` (including the int8 parser
329
+ * TurbineClient itself registers) instead of second-guessing it.
330
+ *
331
+ * Postgres-only: the JSON functions and the divergence set are both
332
+ * engine-specific, so callers gate this on the postgres dialect.
333
+ */
334
+ export const JSON_WIRE_COERCION_OIDS = {
335
+ numeric: 1700,
336
+ int8: 20,
337
+ bytea: 17,
338
+ date: 1082,
339
+ interval: 1186,
340
+ point: 600,
341
+ circle: 718,
342
+ _numeric: 1231,
343
+ _int8: 1016,
344
+ _bytea: 1001,
345
+ _date: 1182,
346
+ _interval: 1187,
347
+ _point: 1017,
348
+ _timestamp: 1115,
349
+ _timestamptz: 1185,
350
+ };
351
+ /**
352
+ * The OID whose driver parser reproduces `pgType`'s driver representation from
353
+ * its text rendering, or `undefined` when the type's JSON rendering already
354
+ * matches the driver (the common case: text, uuid, bool, int4, float8, json,
355
+ * jsonb, arrays of those, …).
356
+ */
357
+ export function jsonWireCoercionOid(pgType) {
358
+ if (!pgType)
359
+ return undefined;
360
+ return JSON_WIRE_COERCION_OIDS[pgType];
361
+ }
362
+ /**
363
+ * Apply the driver's text parser for `oid` to a JSON-sourced wire string.
364
+ *
365
+ * Resolved through `pg.types.getTypeParser` on every call rather than
366
+ * memoized: parser registration is process-global and happens in the
367
+ * TurbineClient constructor (int8, and `timestamp` under `utcTimestamps`), and
368
+ * a caller may register their own at any point. A stale memo would silently
369
+ * reintroduce the very divergence this exists to remove. The lookup is a plain
370
+ * object index in pg-types, so it is not worth caching.
371
+ */
372
+ export function coerceJsonWireValue(oid, value) {
373
+ if (typeof value !== 'string')
374
+ return value;
375
+ return pg.types.getTypeParser(oid, 'text')(value);
376
+ }
@@ -54,6 +54,15 @@ export declare const WARN_NS: {
54
54
  readonly autoStrategy: "autoStrategy";
55
55
  /** Deep-`with` (depth > 5) advisory (builder.ts `findMany`). */
56
56
  readonly deepWith: "deepWith";
57
+ /** Non-deterministic page advisory: paginating findMany with no orderBy. */
58
+ readonly unorderedPage: "unorderedPage";
57
59
  /** PowDB `emitLinks` DDL skips (name/column collision, endpoint drift). */
58
60
  readonly powdbLinks: "powdbLinks";
61
+ /**
62
+ * `relationLoadStrategy: 'flatten'` was asked for but a relation stayed on the
63
+ * correlated-subquery path (relations.ts `planFlattenWith`, builder.ts
64
+ * `planFlatten`). An explicitly requested strategy that quietly does not
65
+ * engage is indistinguishable from one that does nothing, so say so once.
66
+ */
67
+ readonly flattenFallback: "flattenFallback";
59
68
  };
@@ -89,6 +89,15 @@ export const WARN_NS = {
89
89
  autoStrategy: 'autoStrategy',
90
90
  /** Deep-`with` (depth > 5) advisory (builder.ts `findMany`). */
91
91
  deepWith: 'deepWith',
92
+ /** Non-deterministic page advisory: paginating findMany with no orderBy. */
93
+ unorderedPage: 'unorderedPage',
92
94
  /** PowDB `emitLinks` DDL skips (name/column collision, endpoint drift). */
93
95
  powdbLinks: 'powdbLinks',
96
+ /**
97
+ * `relationLoadStrategy: 'flatten'` was asked for but a relation stayed on the
98
+ * correlated-subquery path (relations.ts `planFlattenWith`, builder.ts
99
+ * `planFlatten`). An explicitly requested strategy that quietly does not
100
+ * engage is indistinguishable from one that does nothing, so say so once.
101
+ */
102
+ flattenFallback: 'flattenFallback',
94
103
  };