@prisma-next/sql-orm-client 0.5.0-dev.9 → 0.5.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.
package/src/types.ts CHANGED
@@ -16,14 +16,12 @@ import {
16
16
  OrderByItem,
17
17
  ParamRef,
18
18
  } from '@prisma-next/sql-relational-core/ast';
19
+ import type { Expression } from '@prisma-next/sql-relational-core/expression';
19
20
  import type { ExecutionContext } from '@prisma-next/sql-relational-core/query-lane-context';
20
21
  import type { ComputeColumnJsType, RuntimeScope } from '@prisma-next/sql-relational-core/types';
22
+ import { ifDefined } from '@prisma-next/utils/defined';
21
23
  import type { RowSelection } from './collection-internal-types';
22
24
 
23
- // ---------------------------------------------------------------------------
24
- // Comparison / Filter / Order / Include
25
- // ---------------------------------------------------------------------------
26
-
27
25
  export type AggregateFn = 'count' | 'sum' | 'avg' | 'min' | 'max';
28
26
 
29
27
  export interface IncludeScalar<Result> extends RowSelection<Result> {
@@ -63,10 +61,6 @@ export interface IncludeExpr {
63
61
  readonly combine: Readonly<Record<string, IncludeCombineBranch>> | undefined;
64
62
  }
65
63
 
66
- // ---------------------------------------------------------------------------
67
- // CollectionState — plain data, no query builder types
68
- // ---------------------------------------------------------------------------
69
-
70
64
  export interface CollectionState {
71
65
  readonly filters: readonly AnyExpression[];
72
66
  readonly includes: readonly IncludeExpr[];
@@ -111,10 +105,6 @@ export type DefaultCollectionTypeState = {
111
105
  readonly variantName: undefined;
112
106
  };
113
107
 
114
- // ---------------------------------------------------------------------------
115
- // CollectionContext — bundles lane context + runtime
116
- // ---------------------------------------------------------------------------
117
-
118
108
  export interface RuntimeConnection extends RuntimeScope {
119
109
  release?(): Promise<void>;
120
110
  transaction?(): Promise<RuntimeTransaction>;
@@ -135,10 +125,6 @@ export interface CollectionContext<TContract extends Contract<SqlStorage>> {
135
125
  readonly context: ExecutionContext<TContract>;
136
126
  }
137
127
 
138
- // ---------------------------------------------------------------------------
139
- // ModelAccessor — type-safe proxy for where() callbacks
140
- // ---------------------------------------------------------------------------
141
-
142
128
  export type ComparisonMethodFns<T> = {
143
129
  eq(value: T): AnyExpression;
144
130
  neq(value: T): AnyExpression;
@@ -156,8 +142,7 @@ export type ComparisonMethodFns<T> = {
156
142
  };
157
143
 
158
144
  /**
159
- * Trait-gated comparison methods. Only methods whose required traits are
160
- * all present in `Traits` are included.
145
+ * Trait-gated comparison methods. Only methods whose required traits are all present in `Traits` are included.
161
146
  *
162
147
  * - `traits: []` → always available (isNull, isNotNull)
163
148
  */
@@ -167,10 +152,6 @@ export type ComparisonMethods<T, Traits> = {
167
152
  : never]: ComparisonMethodFns<T>[K];
168
153
  };
169
154
 
170
- // ---------------------------------------------------------------------------
171
- // Extension operation result — returned by calling an extension method
172
- // ---------------------------------------------------------------------------
173
-
174
155
  type QueryOperationReturnTraits<
175
156
  Returns,
176
157
  TCodecTypes extends Record<string, unknown>,
@@ -195,26 +176,6 @@ type QueryOperationReturnJsType<
195
176
  : unknown
196
177
  : unknown;
197
178
 
198
- type CodecArgJsType<Arg, TCodecTypes extends Record<string, unknown>> = Arg extends {
199
- readonly codecId: infer CId extends string;
200
- readonly nullable: infer N;
201
- }
202
- ? CId extends keyof TCodecTypes
203
- ? TCodecTypes[CId] extends { readonly output: infer O }
204
- ? N extends true
205
- ? O | null
206
- : O
207
- : unknown
208
- : unknown
209
- : unknown;
210
-
211
- type MapArgsToJsTypes<
212
- Args extends readonly unknown[],
213
- TCodecTypes extends Record<string, unknown>,
214
- > = Args extends readonly [infer Head, ...infer Tail]
215
- ? [CodecArgJsType<Head, TCodecTypes>, ...MapArgsToJsTypes<Tail, TCodecTypes>]
216
- : [];
217
-
218
179
  type IsBooleanReturn<Returns, TCodecTypes extends Record<string, unknown>> = Returns extends {
219
180
  readonly codecId: infer Id extends string;
220
181
  }
@@ -227,22 +188,41 @@ type IsBooleanReturn<Returns, TCodecTypes extends Record<string, unknown>> = Ret
227
188
  : false
228
189
  : false;
229
190
 
191
+ /**
192
+ * Extract the `{codecId, nullable}` spec carried inside an `Expression<T>`. Used to recover the op's return spec from its impl signature so the pre-existing `QueryOperationReturn*` helpers can consume it unchanged.
193
+ */
194
+ type SpecOf<E> = E extends Expression<infer T> ? T : never;
195
+
196
+ type ImplReturnSpec<Impl> = Impl extends (...args: never[]) => infer Ret ? SpecOf<Ret> : never;
197
+
198
+ /**
199
+ * Builds the ORM column-method signature for an operation.
200
+ *
201
+ * - User args: drops the impl's first parameter (the column is bound at access time) and forwards the rest unchanged. Each remaining arg keeps its authored `CodecExpression` / `TraitExpression` shape — so callers can pass a raw JS value, another column handle (which itself implements `Expression`), or `null` when nullable.
202
+ * - Return: predicate ops (boolean-traited return) yield `AnyExpression`; non-predicate ops yield `ComparisonMethods<JsType, Traits>` of the return codec.
203
+ */
230
204
  type QueryOperationMethod<Op, TCodecTypes extends Record<string, unknown>> = Op extends {
231
- readonly args: readonly [unknown, ...infer UserArgs];
232
- readonly returns: infer Returns;
205
+ readonly impl: (...args: never[]) => unknown;
233
206
  }
234
- ? IsBooleanReturn<Returns, TCodecTypes> extends true
235
- ? (...args: MapArgsToJsTypes<UserArgs, TCodecTypes>) => AnyExpression
236
- : (
237
- ...args: MapArgsToJsTypes<UserArgs, TCodecTypes>
238
- ) => ComparisonMethods<
239
- QueryOperationReturnJsType<Returns, TCodecTypes>,
240
- QueryOperationReturnTraits<Returns, TCodecTypes>
241
- >
207
+ ? Op['impl'] extends (first: never, ...rest: infer UserArgs extends readonly unknown[]) => unknown
208
+ ? ImplReturnSpec<Op['impl']> extends infer Returns
209
+ ? IsBooleanReturn<Returns, TCodecTypes> extends true
210
+ ? (...args: UserArgs) => AnyExpression
211
+ : (
212
+ ...args: UserArgs
213
+ ) => ComparisonMethods<
214
+ QueryOperationReturnJsType<Returns, TCodecTypes>,
215
+ QueryOperationReturnTraits<Returns, TCodecTypes>
216
+ >
217
+ : never
218
+ : never
242
219
  : never;
243
220
 
221
+ /**
222
+ * Tests whether an operation's `self` dispatch hint reaches a field with the given codec identity. Codec hints match by identity; trait hints match when every required trait is present in the field codec's trait set.
223
+ */
244
224
  type OpMatchesField<Op, CodecId extends string, CT extends Record<string, unknown>> = Op extends {
245
- readonly args: readonly [infer Self, ...(readonly unknown[])];
225
+ readonly self: infer Self;
246
226
  }
247
227
  ? Self extends { readonly codecId: CodecId }
248
228
  ? true
@@ -275,21 +255,43 @@ type FieldOperations<
275
255
  : unknown
276
256
  : unknown;
277
257
 
278
- // ---------------------------------------------------------------------------
279
- // COMPARISON_METHODS_METAsingle source of truth for traits + factories
280
- // ---------------------------------------------------------------------------
258
+ /**
259
+ * Resolve the unique column ref carried by `left` so its surrounding `ParamRef` can dispatch through `forColumn`. The previous implementation only matched bare `column-ref` expressions, which lost refs the moment the expression got wrapped (`upper(f.id)`, `BinaryExpr`, function-call expressions, etc.) and column-aware dispatch (AC-5) silently degraded for any predicate that touched a computed column.
260
+ *
261
+ * Walking via `collectColumnRefs()` and accepting only a single unambiguous ref gives `eq(upper(f.id), value)` and friends correct dispatch without inventing refs for ambiguous shapes (e.g. `eq(concat(f.firstName, f.lastName), value)` returns `undefined` and falls through to the codec-id path, which is correct for non-parameterized comparisons).
262
+ */
263
+ function refsFromLeft(left: AnyExpression): { table: string; column: string } | undefined {
264
+ if (left.kind === 'column-ref') {
265
+ return { table: left.table, column: left.column };
266
+ }
267
+ const columnRefs = left.collectColumnRefs();
268
+ if (columnRefs.length !== 1) return undefined;
269
+ const single = columnRefs[0];
270
+ if (!single) return undefined;
271
+ return { table: single.table, column: single.column };
272
+ }
281
273
 
282
- function param(codecId: string | undefined, value: unknown): ParamRef {
283
- return codecId ? ParamRef.of(value, { codecId }) : ParamRef.of(value);
274
+ function param(
275
+ codecId: string | undefined,
276
+ value: unknown,
277
+ refs: { table: string; column: string } | undefined,
278
+ ): ParamRef {
279
+ if (codecId === undefined && refs === undefined) return ParamRef.of(value);
280
+ return ParamRef.of(value, {
281
+ ...ifDefined('codecId', codecId),
282
+ ...ifDefined('refs', refs),
283
+ });
284
284
  }
285
285
 
286
- function paramList(codecId: string | undefined, values: readonly unknown[]): ListExpression {
287
- return ListExpression.of(values.map((value) => param(codecId, value)));
286
+ function paramList(
287
+ codecId: string | undefined,
288
+ values: readonly unknown[],
289
+ refs: { table: string; column: string } | undefined,
290
+ ): ListExpression {
291
+ return ListExpression.of(values.map((value) => param(codecId, value, refs)));
288
292
  }
289
293
 
290
- // never[] is intentional: factories have heterogeneous signatures (value: unknown,
291
- // values: readonly unknown[], pattern: string, etc.) but are only called through
292
- // the typed ComparisonMethodFns interface, never through this type directly.
294
+ // never[] is intentional: factories have heterogeneous signatures (value: unknown, values: readonly unknown[], pattern: string, etc.) but are only called through the typed ComparisonMethodFns interface, never through this type directly.
293
295
  type MethodFactory = (
294
296
  left: AnyExpression,
295
297
  codecId: string | undefined,
@@ -302,12 +304,16 @@ type ComparisonMethodMeta = {
302
304
 
303
305
  function scalarComparisonMethod(op: BinaryOp) {
304
306
  return ((left, codecId) => (value: unknown) =>
305
- new BinaryExpr(op, left, param(codecId, value))) satisfies MethodFactory;
307
+ new BinaryExpr(op, left, param(codecId, value, refsFromLeft(left)))) satisfies MethodFactory;
306
308
  }
307
309
 
308
310
  function listComparisonMethod(op: BinaryOp) {
309
311
  return ((left, codecId) => (values: readonly unknown[]) =>
310
- new BinaryExpr(op, left, paramList(codecId, values))) satisfies MethodFactory;
312
+ new BinaryExpr(
313
+ op,
314
+ left,
315
+ paramList(codecId, values, refsFromLeft(left)),
316
+ )) satisfies MethodFactory;
311
317
  }
312
318
 
313
319
  /**
@@ -392,10 +398,11 @@ export type RelationFilterAccessor<
392
398
  };
393
399
 
394
400
  type ScalarModelAccessor<TContract extends Contract<SqlStorage>, ModelName extends string> = {
395
- [K in keyof FieldsOf<TContract, ModelName> & string]: ComparisonMethods<
396
- FieldJsType<TContract, ModelName, K>,
397
- FieldTraits<TContract, ModelName, K>
398
- > &
401
+ [K in keyof FieldsOf<TContract, ModelName> & string]: Expression<{
402
+ codecId: FieldCodecId<TContract, ModelName, K>;
403
+ nullable: FieldNullable<TContract, ModelName, K>;
404
+ }> &
405
+ ComparisonMethods<FieldJsType<TContract, ModelName, K>, FieldTraits<TContract, ModelName, K>> &
399
406
  FieldOperations<TContract, ModelName, K>;
400
407
  };
401
408
 
@@ -411,18 +418,10 @@ export type ModelAccessor<
411
418
  ModelName extends string,
412
419
  > = ScalarModelAccessor<TContract, ModelName> & RelationModelAccessor<TContract, ModelName>;
413
420
 
414
- // ---------------------------------------------------------------------------
415
- // DefaultModelRow — all scalar fields with JS types
416
- // ---------------------------------------------------------------------------
417
-
418
421
  export type DefaultModelRow<TContract extends Contract<SqlStorage>, ModelName extends string> = {
419
422
  [K in keyof FieldsOf<TContract, ModelName> & string]: FieldJsType<TContract, ModelName, K>;
420
423
  };
421
424
 
422
- // ---------------------------------------------------------------------------
423
- // InferRootRow — discriminated union for polymorphic base models
424
- // ---------------------------------------------------------------------------
425
-
426
425
  type Simplify<T> = { [K in keyof T]: T[K] } & {};
427
426
 
428
427
  type VariantRow<TContract extends Contract<SqlStorage>, ModelName extends string> = ModelDef<
@@ -543,10 +542,6 @@ export type ShorthandWhereFilter<
543
542
  | undefined;
544
543
  }>;
545
544
 
546
- // ---------------------------------------------------------------------------
547
- // Helpers for extracting fields / types from the contract
548
- // ---------------------------------------------------------------------------
549
-
550
545
  type ModelsOf<TContract extends Contract<SqlStorage>> =
551
546
  TContract['models'] extends Record<string, unknown> ? TContract['models'] : Record<string, never>;
552
547
 
@@ -660,10 +655,6 @@ type FieldStorageColumn<
660
655
  FieldName extends string,
661
656
  > = ResolvedStorageColumn<TContract, ModelName, FieldName>;
662
657
 
663
- // ---------------------------------------------------------------------------
664
- // Field trait resolution from contract CodecTypes
665
- // ---------------------------------------------------------------------------
666
-
667
658
  type FieldCodecId<
668
659
  TContract extends Contract<SqlStorage>,
669
660
  ModelName extends string,
@@ -674,6 +665,16 @@ type FieldCodecId<
674
665
  ? Id
675
666
  : never;
676
667
 
668
+ type FieldNullable<
669
+ TContract extends Contract<SqlStorage>,
670
+ ModelName extends string,
671
+ FieldName extends string,
672
+ > = FieldStorageColumn<TContract, ModelName, FieldName> extends {
673
+ readonly nullable: infer N extends boolean;
674
+ }
675
+ ? N
676
+ : false;
677
+
677
678
  type FieldTraits<
678
679
  TContract extends Contract<SqlStorage>,
679
680
  ModelName extends string,
@@ -774,10 +775,6 @@ export type CreateInput<TContract extends Contract<SqlStorage>, ModelName extend
774
775
  > &
775
776
  RelationMutationFields<TContract, ModelName>;
776
777
 
777
- // ---------------------------------------------------------------------------
778
- // Polymorphic write gating
779
- // ---------------------------------------------------------------------------
780
-
781
778
  type IsPolymorphicBase<TContract extends Contract<SqlStorage>, ModelName extends string> = ModelDef<
782
779
  TContract,
783
780
  ModelName
@@ -1049,10 +1046,6 @@ export type MutationUpdateInput<
1049
1046
  ModelName extends string,
1050
1047
  > = Partial<DefaultModelRow<TContract, ModelName>> & RelationMutationFields<TContract, ModelName>;
1051
1048
 
1052
- // ---------------------------------------------------------------------------
1053
- // Relation helpers
1054
- // ---------------------------------------------------------------------------
1055
-
1056
1049
  type ModelRelations<TContract extends Contract<SqlStorage>, ModelName extends string> = ModelDef<
1057
1050
  TContract,
1058
1051
  ModelName
@@ -122,7 +122,10 @@ function createParamRef(
122
122
  if (!codecId) {
123
123
  throw new Error(`Unknown column "${columnRef.column}" in table "${columnRef.table}"`);
124
124
  }
125
- return ParamRef.of(value, { codecId });
125
+ return ParamRef.of(value, {
126
+ codecId,
127
+ refs: { table: columnRef.table, column: columnRef.column },
128
+ });
126
129
  }
127
130
 
128
131
  function createExpressionBinder(contract: Contract<SqlStorage>): ExpressionRewriter {
@@ -170,7 +173,12 @@ function bindSelectAst(contract: Contract<SqlStorage>, ast: SelectAst): SelectAs
170
173
  joins: ast.joins?.map((join) => bindJoin(contract, join)),
171
174
  projection: ast.projection.map(
172
175
  (projection) =>
173
- new ProjectionItem(projection.alias, bindProjectionExpr(contract, projection.expr)),
176
+ new ProjectionItem(
177
+ projection.alias,
178
+ bindProjectionExpr(contract, projection.expr),
179
+ projection.codecId,
180
+ projection.refs,
181
+ ),
174
182
  ),
175
183
  where: ast.where ? bindWhereExprNode(contract, ast.where) : undefined,
176
184
  orderBy: ast.orderBy?.map((orderItem) => bindOrderByItem(contract, orderItem)),