@prisma-next/sql-lane 0.3.0-dev.3 → 0.3.0-dev.30

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 (61) hide show
  1. package/README.md +2 -2
  2. package/dist/{chunk-WA646VY6.js → chunk-72PNERR5.js} +133 -161
  3. package/dist/chunk-72PNERR5.js.map +1 -0
  4. package/dist/exports/sql.d.ts +5 -5
  5. package/dist/exports/sql.d.ts.map +1 -0
  6. package/dist/exports/sql.js +1 -1
  7. package/dist/index.d.ts +5 -116
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +1 -1
  10. package/dist/raw.d.ts +11 -0
  11. package/dist/raw.d.ts.map +1 -0
  12. package/dist/sql/builder.d.ts +11 -0
  13. package/dist/sql/builder.d.ts.map +1 -0
  14. package/dist/sql/context.d.ts +5 -0
  15. package/dist/sql/context.d.ts.map +1 -0
  16. package/dist/sql/include-builder.d.ts +35 -0
  17. package/dist/sql/include-builder.d.ts.map +1 -0
  18. package/dist/sql/join-builder.d.ts +4 -0
  19. package/dist/sql/join-builder.d.ts.map +1 -0
  20. package/dist/sql/mutation-builder.d.ts +64 -0
  21. package/dist/sql/mutation-builder.d.ts.map +1 -0
  22. package/dist/sql/plan.d.ts +4 -0
  23. package/dist/sql/plan.d.ts.map +1 -0
  24. package/dist/sql/predicate-builder.d.ts +11 -0
  25. package/dist/sql/predicate-builder.d.ts.map +1 -0
  26. package/dist/sql/projection.d.ts +18 -0
  27. package/dist/sql/projection.d.ts.map +1 -0
  28. package/dist/sql/select-builder.d.ts +35 -0
  29. package/dist/sql/select-builder.d.ts.map +1 -0
  30. package/dist/types/internal.d.ts +35 -0
  31. package/dist/types/internal.d.ts.map +1 -0
  32. package/dist/types/public.d.ts +18 -0
  33. package/dist/types/public.d.ts.map +1 -0
  34. package/dist/utils/assertions.d.ts +28 -0
  35. package/dist/utils/assertions.d.ts.map +1 -0
  36. package/dist/utils/capabilities.d.ts +4 -0
  37. package/dist/utils/capabilities.d.ts.map +1 -0
  38. package/dist/utils/errors.d.ts +30 -0
  39. package/dist/utils/errors.d.ts.map +1 -0
  40. package/dist/utils/state.d.ts +30 -0
  41. package/dist/utils/state.d.ts.map +1 -0
  42. package/package.json +21 -21
  43. package/src/exports/sql.ts +12 -0
  44. package/src/index.ts +13 -0
  45. package/src/raw.ts +230 -0
  46. package/src/sql/builder.ts +66 -0
  47. package/src/sql/context.ts +10 -0
  48. package/src/sql/include-builder.ts +248 -0
  49. package/src/sql/join-builder.ts +18 -0
  50. package/src/sql/mutation-builder.ts +494 -0
  51. package/src/sql/plan.ts +290 -0
  52. package/src/sql/predicate-builder.ts +127 -0
  53. package/src/sql/projection.ts +117 -0
  54. package/src/sql/select-builder.ts +430 -0
  55. package/src/types/internal.ts +41 -0
  56. package/src/types/public.ts +36 -0
  57. package/src/utils/assertions.ts +34 -0
  58. package/src/utils/capabilities.ts +39 -0
  59. package/src/utils/errors.ts +168 -0
  60. package/src/utils/state.ts +38 -0
  61. package/dist/chunk-WA646VY6.js.map +0 -1
@@ -0,0 +1,430 @@
1
+ import type { ParamDescriptor } from '@prisma-next/contract/types';
2
+ import type { SqlContract, SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';
3
+ import type {
4
+ BinaryExpr,
5
+ ColumnRef,
6
+ Direction,
7
+ IncludeAst,
8
+ IncludeRef,
9
+ JoinAst,
10
+ OperationExpr,
11
+ TableRef,
12
+ } from '@prisma-next/sql-relational-core/ast';
13
+ import {
14
+ createJoinOnBuilder,
15
+ createOrderByItem,
16
+ createSelectAst,
17
+ createTableRef,
18
+ } from '@prisma-next/sql-relational-core/ast';
19
+ import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
20
+ import type { QueryLaneContext } from '@prisma-next/sql-relational-core/query-lane-context';
21
+ import type {
22
+ AnyBinaryBuilder,
23
+ AnyOrderBuilder,
24
+ BinaryBuilder,
25
+ BuildOptions,
26
+ InferNestedProjectionRow,
27
+ JoinOnBuilder,
28
+ JoinOnPredicate,
29
+ NestedProjection,
30
+ OrderBuilder,
31
+ SqlBuilderOptions,
32
+ } from '@prisma-next/sql-relational-core/types';
33
+ import { isExpressionBuilder } from '@prisma-next/sql-relational-core/utils/guards';
34
+ import type { ProjectionInput } from '../types/internal';
35
+ import { checkIncludeCapabilities } from '../utils/capabilities';
36
+ import {
37
+ errorChildProjectionEmpty,
38
+ errorFromMustBeCalled,
39
+ errorIncludeAliasCollision,
40
+ errorLimitMustBeNonNegativeInteger,
41
+ errorMissingAlias,
42
+ errorSelectMustBeCalled,
43
+ errorSelfJoinNotSupported,
44
+ errorUnknownTable,
45
+ } from '../utils/errors';
46
+ import type { BuilderState, IncludeState, JoinState, ProjectionState } from '../utils/state';
47
+ import {
48
+ buildIncludeAst,
49
+ type IncludeChildBuilder,
50
+ IncludeChildBuilderImpl,
51
+ } from './include-builder';
52
+ import { buildJoinAst } from './join-builder';
53
+ import { buildMeta } from './plan';
54
+ import { buildWhereExpr } from './predicate-builder';
55
+ import { buildProjectionState } from './projection';
56
+
57
+ export class SelectBuilderImpl<
58
+ TContract extends SqlContract<SqlStorage> = SqlContract<SqlStorage>,
59
+ Row = unknown,
60
+ CodecTypes extends Record<string, { readonly output: unknown }> = Record<string, never>,
61
+ Includes extends Record<string, unknown> = Record<string, never>,
62
+ > {
63
+ private readonly contract: TContract;
64
+ private readonly codecTypes: CodecTypes;
65
+ private readonly context: QueryLaneContext<TContract>;
66
+ private state: BuilderState = {};
67
+
68
+ constructor(options: SqlBuilderOptions<TContract>, state?: BuilderState) {
69
+ this.context = options.context;
70
+ this.contract = options.context.contract;
71
+ this.codecTypes = options.context.contract.mappings.codecTypes as CodecTypes;
72
+ if (state) {
73
+ this.state = state;
74
+ }
75
+ }
76
+
77
+ from(table: TableRef): SelectBuilderImpl<TContract, unknown, CodecTypes, Record<string, never>> {
78
+ return new SelectBuilderImpl<TContract, unknown, CodecTypes, Record<string, never>>(
79
+ {
80
+ context: this.context,
81
+ },
82
+ { ...this.state, from: table },
83
+ );
84
+ }
85
+
86
+ innerJoin(
87
+ table: TableRef,
88
+ on: (on: JoinOnBuilder) => JoinOnPredicate,
89
+ ): SelectBuilderImpl<TContract, Row, CodecTypes, Includes> {
90
+ return this._addJoin('inner', table, on);
91
+ }
92
+
93
+ leftJoin(
94
+ table: TableRef,
95
+ on: (on: JoinOnBuilder) => JoinOnPredicate,
96
+ ): SelectBuilderImpl<TContract, Row, CodecTypes, Includes> {
97
+ return this._addJoin('left', table, on);
98
+ }
99
+
100
+ rightJoin(
101
+ table: TableRef,
102
+ on: (on: JoinOnBuilder) => JoinOnPredicate,
103
+ ): SelectBuilderImpl<TContract, Row, CodecTypes, Includes> {
104
+ return this._addJoin('right', table, on);
105
+ }
106
+
107
+ fullJoin(
108
+ table: TableRef,
109
+ on: (on: JoinOnBuilder) => JoinOnPredicate,
110
+ ): SelectBuilderImpl<TContract, Row, CodecTypes, Includes> {
111
+ return this._addJoin('full', table, on);
112
+ }
113
+
114
+ includeMany<
115
+ ChildProjection extends NestedProjection,
116
+ ChildRow = InferNestedProjectionRow<ChildProjection, CodecTypes>,
117
+ AliasName extends string = string,
118
+ >(
119
+ childTable: TableRef,
120
+ on: (on: JoinOnBuilder) => JoinOnPredicate,
121
+ childBuilder: (
122
+ child: IncludeChildBuilder<TContract, CodecTypes, unknown>,
123
+ ) => IncludeChildBuilder<TContract, CodecTypes, ChildRow>,
124
+ options?: { alias?: AliasName },
125
+ ): SelectBuilderImpl<TContract, Row, CodecTypes, Includes & { [K in AliasName]: ChildRow }> {
126
+ checkIncludeCapabilities(this.contract);
127
+
128
+ if (!this.contract.storage.tables[childTable.name]) {
129
+ errorUnknownTable(childTable.name);
130
+ }
131
+
132
+ const joinOnBuilder = createJoinOnBuilder();
133
+ const onPredicate = on(joinOnBuilder);
134
+
135
+ // Validate ON uses column equality
136
+ // TypeScript can't narrow ColumnBuilder properly, so we assert
137
+ const onLeft = onPredicate.left as { table: string; column: string };
138
+ const onRight = onPredicate.right as { table: string; column: string };
139
+ if (onLeft.table === onRight.table) {
140
+ errorSelfJoinNotSupported();
141
+ }
142
+
143
+ // Build child builder
144
+ const childBuilderImpl = new IncludeChildBuilderImpl<TContract, CodecTypes, unknown>(
145
+ this.contract,
146
+ this.codecTypes,
147
+ childTable,
148
+ );
149
+ const builtChild = childBuilder(
150
+ childBuilderImpl as IncludeChildBuilder<TContract, CodecTypes, unknown>,
151
+ );
152
+ const childState = (
153
+ builtChild as IncludeChildBuilderImpl<TContract, CodecTypes, ChildRow>
154
+ ).getState();
155
+
156
+ // Validate child projection is non-empty
157
+ if (childState.childProjection.aliases.length === 0) {
158
+ errorChildProjectionEmpty();
159
+ }
160
+
161
+ // Determine alias
162
+ const alias = options?.alias ?? childTable.name;
163
+
164
+ // Check for alias collisions with existing projection
165
+ if (this.state.projection) {
166
+ if (this.state.projection.aliases.includes(alias)) {
167
+ errorIncludeAliasCollision(alias, 'projection');
168
+ }
169
+ }
170
+
171
+ // Check for alias collisions with existing includes
172
+ const existingIncludes = this.state.includes ?? [];
173
+ if (existingIncludes.some((inc) => inc.alias === alias)) {
174
+ errorIncludeAliasCollision(alias, 'include');
175
+ }
176
+
177
+ const includeState: IncludeState = {
178
+ alias,
179
+ table: childTable,
180
+ on: onPredicate,
181
+ childProjection: childState.childProjection,
182
+ ...(childState.childWhere !== undefined ? { childWhere: childState.childWhere } : {}),
183
+ ...(childState.childOrderBy !== undefined ? { childOrderBy: childState.childOrderBy } : {}),
184
+ ...(childState.childLimit !== undefined ? { childLimit: childState.childLimit } : {}),
185
+ };
186
+
187
+ const newIncludes = [...existingIncludes, includeState];
188
+
189
+ // Type-level: Update Includes map with new include
190
+ // The AliasName generic parameter is inferred from options.alias, allowing TypeScript
191
+ // to track include definitions across multiple includeMany() calls and infer correct
192
+ // array types when select() includes boolean true for include references
193
+ type NewIncludes = Includes & { [K in AliasName]: ChildRow };
194
+
195
+ return new SelectBuilderImpl<TContract, Row, CodecTypes, NewIncludes>(
196
+ {
197
+ context: this.context,
198
+ },
199
+ { ...this.state, includes: newIncludes },
200
+ );
201
+ }
202
+
203
+ private _addJoin(
204
+ joinType: 'inner' | 'left' | 'right' | 'full',
205
+ table: TableRef,
206
+ on: (on: JoinOnBuilder) => JoinOnPredicate,
207
+ ): SelectBuilderImpl<TContract, Row, CodecTypes, Includes> {
208
+ const fromTable = this.ensureFrom();
209
+
210
+ if (!this.contract.storage.tables[table.name]) {
211
+ errorUnknownTable(table.name);
212
+ }
213
+
214
+ if (table.name === fromTable.name) {
215
+ errorSelfJoinNotSupported();
216
+ }
217
+
218
+ const joinOnBuilder = createJoinOnBuilder();
219
+ const onPredicate = on(joinOnBuilder);
220
+
221
+ const joinState: JoinState = {
222
+ joinType,
223
+ table,
224
+ on: onPredicate,
225
+ };
226
+
227
+ const existingJoins = this.state.joins ?? [];
228
+ const newJoins = [...existingJoins, joinState];
229
+
230
+ return new SelectBuilderImpl<TContract, Row, CodecTypes, Includes>(
231
+ {
232
+ context: this.context,
233
+ },
234
+ { ...this.state, joins: newJoins },
235
+ );
236
+ }
237
+
238
+ where(expr: AnyBinaryBuilder): SelectBuilderImpl<TContract, Row, CodecTypes, Includes> {
239
+ return new SelectBuilderImpl<TContract, Row, CodecTypes, Includes>(
240
+ {
241
+ context: this.context,
242
+ },
243
+ { ...this.state, where: expr },
244
+ );
245
+ }
246
+
247
+ select<P extends ProjectionInput>(
248
+ projection: P,
249
+ ): SelectBuilderImpl<
250
+ TContract,
251
+ InferNestedProjectionRow<P, CodecTypes, Includes>,
252
+ CodecTypes,
253
+ Includes
254
+ > {
255
+ const table = this.ensureFrom();
256
+ const projectionState = buildProjectionState(table, projection, this.state.includes);
257
+
258
+ return new SelectBuilderImpl<
259
+ TContract,
260
+ InferNestedProjectionRow<P, CodecTypes, Includes>,
261
+ CodecTypes,
262
+ Includes
263
+ >(
264
+ {
265
+ context: this.context,
266
+ },
267
+ { ...this.state, projection: projectionState },
268
+ );
269
+ }
270
+
271
+ orderBy(order: AnyOrderBuilder): SelectBuilderImpl<TContract, Row, CodecTypes, Includes> {
272
+ return new SelectBuilderImpl<TContract, Row, CodecTypes, Includes>(
273
+ {
274
+ context: this.context,
275
+ },
276
+ { ...this.state, orderBy: order },
277
+ );
278
+ }
279
+
280
+ limit(count: number): SelectBuilderImpl<TContract, Row, CodecTypes, Includes> {
281
+ if (!Number.isInteger(count) || count < 0) {
282
+ errorLimitMustBeNonNegativeInteger();
283
+ }
284
+
285
+ return new SelectBuilderImpl<TContract, Row, CodecTypes, Includes>(
286
+ {
287
+ context: this.context,
288
+ },
289
+ { ...this.state, limit: count },
290
+ );
291
+ }
292
+
293
+ build(options?: BuildOptions): SqlQueryPlan<Row> {
294
+ const table = this.ensureFrom();
295
+ const projection = this.ensureProjection();
296
+
297
+ const paramsMap = (options?.params ?? {}) as Record<string, unknown>;
298
+ const contractTable = this.contract.storage.tables[table.name];
299
+
300
+ if (!contractTable) {
301
+ errorUnknownTable(table.name);
302
+ }
303
+
304
+ const paramDescriptors: ParamDescriptor[] = [];
305
+ const paramValues: unknown[] = [];
306
+ const paramCodecs: Record<string, string> = {};
307
+
308
+ const whereResult = this.state.where
309
+ ? buildWhereExpr(this.contract, this.state.where, paramsMap, paramDescriptors, paramValues)
310
+ : undefined;
311
+ const whereExpr = whereResult?.expr;
312
+
313
+ if (whereResult?.codecId && whereResult.paramName) {
314
+ paramCodecs[whereResult.paramName] = whereResult.codecId;
315
+ }
316
+
317
+ const orderByClause = this.state.orderBy
318
+ ? (() => {
319
+ const orderBy = this.state.orderBy as OrderBuilder<string, StorageColumn, unknown>;
320
+ // orderBy.expr is already an Expression (ColumnRef or OperationExpr)
321
+ return [createOrderByItem(orderBy.expr, orderBy.dir)];
322
+ })()
323
+ : undefined;
324
+
325
+ const joins = this.state.joins?.map((join) => buildJoinAst(join));
326
+
327
+ const includes = this.state.includes?.map((include) =>
328
+ buildIncludeAst(include, this.contract, paramsMap, paramDescriptors, paramValues),
329
+ );
330
+
331
+ // Build projection with support for includeRef and OperationExpr
332
+ const projectEntries: Array<{ alias: string; expr: ColumnRef | IncludeRef | OperationExpr }> =
333
+ [];
334
+ for (let i = 0; i < projection.aliases.length; i++) {
335
+ const alias = projection.aliases[i];
336
+ if (!alias) {
337
+ errorMissingAlias(i);
338
+ }
339
+ const column = projection.columns[i];
340
+
341
+ // Check if this alias matches an include alias first
342
+ // Include placeholders have null columns
343
+ const matchingInclude = this.state.includes?.find((inc) => inc.alias === alias);
344
+ if (matchingInclude) {
345
+ // This is an include reference - column can be null for placeholders
346
+ projectEntries.push({
347
+ alias,
348
+ expr: { kind: 'includeRef', alias },
349
+ });
350
+ } else if (column && isExpressionBuilder(column)) {
351
+ // This is an ExpressionBuilder (operation result) - use its expr
352
+ projectEntries.push({
353
+ alias,
354
+ expr: column.expr,
355
+ });
356
+ } else if (column) {
357
+ // This is a regular ColumnBuilder - use toExpr() to get ColumnRef
358
+ const columnRef = column.toExpr();
359
+ projectEntries.push({
360
+ alias,
361
+ expr: columnRef,
362
+ });
363
+ }
364
+ }
365
+
366
+ const ast = createSelectAst({
367
+ from: createTableRef(table.name),
368
+ joins,
369
+ includes,
370
+ project: projectEntries,
371
+ where: whereExpr,
372
+ orderBy: orderByClause,
373
+ limit: this.state.limit,
374
+ } as {
375
+ from: TableRef;
376
+ joins?: ReadonlyArray<JoinAst>;
377
+ includes?: ReadonlyArray<IncludeAst>;
378
+ project: ReadonlyArray<{ alias: string; expr: ColumnRef | IncludeRef | OperationExpr }>;
379
+ where?: BinaryExpr;
380
+ orderBy?: ReadonlyArray<{ expr: ColumnRef | OperationExpr; dir: Direction }>;
381
+ limit?: number;
382
+ });
383
+
384
+ const planMeta = buildMeta({
385
+ contract: this.contract,
386
+ table,
387
+ projection,
388
+ joins: this.state.joins,
389
+ includes: this.state.includes,
390
+ paramDescriptors,
391
+ paramCodecs,
392
+ where: this.state.where,
393
+ orderBy: this.state.orderBy,
394
+ } as {
395
+ contract: SqlContract<SqlStorage>;
396
+ table: TableRef;
397
+ projection: ProjectionState;
398
+ joins?: ReadonlyArray<JoinState>;
399
+ includes?: ReadonlyArray<IncludeState>;
400
+ where?: BinaryBuilder;
401
+ orderBy?: AnyOrderBuilder;
402
+ paramDescriptors: ParamDescriptor[];
403
+ paramCodecs?: Record<string, string>;
404
+ });
405
+
406
+ const queryPlan: SqlQueryPlan<Row> = Object.freeze({
407
+ ast,
408
+ params: paramValues,
409
+ meta: planMeta,
410
+ });
411
+
412
+ return queryPlan;
413
+ }
414
+
415
+ private ensureFrom() {
416
+ if (!this.state.from) {
417
+ errorFromMustBeCalled();
418
+ }
419
+
420
+ return this.state.from;
421
+ }
422
+
423
+ private ensureProjection() {
424
+ if (!this.state.projection) {
425
+ errorSelectMustBeCalled();
426
+ }
427
+
428
+ return this.state.projection;
429
+ }
430
+ }
@@ -0,0 +1,41 @@
1
+ import type { ParamDescriptor } from '@prisma-next/contract/types';
2
+ import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
3
+ import type { TableRef } from '@prisma-next/sql-relational-core/ast';
4
+ import type {
5
+ AnyBinaryBuilder,
6
+ AnyExpressionSource,
7
+ AnyOrderBuilder,
8
+ NestedProjection,
9
+ } from '@prisma-next/sql-relational-core/types';
10
+ import type { ProjectionState } from '../utils/state';
11
+
12
+ export type ProjectionInput = Record<string, AnyExpressionSource | boolean | NestedProjection>;
13
+
14
+ export interface MetaBuildArgs {
15
+ readonly contract: SqlContract<SqlStorage>;
16
+ readonly table: TableRef;
17
+ readonly projection: ProjectionState;
18
+ readonly joins?: ReadonlyArray<{
19
+ readonly joinType: 'inner' | 'left' | 'right' | 'full';
20
+ readonly table: TableRef;
21
+ readonly on: {
22
+ readonly left: unknown;
23
+ readonly right: unknown;
24
+ };
25
+ }>;
26
+ readonly includes?: ReadonlyArray<{
27
+ readonly alias: string;
28
+ readonly table: TableRef;
29
+ readonly on: {
30
+ readonly left: unknown;
31
+ readonly right: unknown;
32
+ };
33
+ readonly childProjection: ProjectionState;
34
+ readonly childWhere?: AnyBinaryBuilder;
35
+ readonly childOrderBy?: AnyOrderBuilder;
36
+ }>;
37
+ readonly where?: AnyBinaryBuilder;
38
+ readonly orderBy?: AnyOrderBuilder;
39
+ readonly paramDescriptors: ParamDescriptor[];
40
+ readonly paramCodecs?: Record<string, string>;
41
+ }
@@ -0,0 +1,36 @@
1
+ import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
2
+ import type { TableRef } from '@prisma-next/sql-relational-core/ast';
3
+ import type { ParamPlaceholder, RawFactory } from '@prisma-next/sql-relational-core/types';
4
+ import type { DeleteBuilder, InsertBuilder, UpdateBuilder } from '../sql/mutation-builder';
5
+ import type { SelectBuilderImpl } from '../sql/select-builder';
6
+
7
+ export type { TableRef } from '@prisma-next/sql-relational-core/ast';
8
+ export type {
9
+ AnyColumnBuilder,
10
+ BuildOptions,
11
+ InferReturningRow,
12
+ ParamPlaceholder,
13
+ RawFactory,
14
+ SqlBuilderOptions,
15
+ } from '@prisma-next/sql-relational-core/types';
16
+
17
+ export type SelectBuilder<
18
+ TContract extends SqlContract<SqlStorage> = SqlContract<SqlStorage>,
19
+ Row = unknown,
20
+ CodecTypes extends Record<string, { readonly output: unknown }> = Record<string, never>,
21
+ Includes extends Record<string, unknown> = Record<string, never>,
22
+ > = SelectBuilderImpl<TContract, Row, CodecTypes, Includes> & {
23
+ readonly raw: RawFactory;
24
+ insert(
25
+ table: TableRef,
26
+ values: Record<string, ParamPlaceholder>,
27
+ ): InsertBuilder<TContract, CodecTypes>;
28
+ update(
29
+ table: TableRef,
30
+ set: Record<string, ParamPlaceholder>,
31
+ ): UpdateBuilder<TContract, CodecTypes>;
32
+ delete(table: TableRef): DeleteBuilder<TContract, CodecTypes>;
33
+ };
34
+
35
+ export type { IncludeChildBuilder } from '../sql/include-builder';
36
+ export type { DeleteBuilder, InsertBuilder, UpdateBuilder } from '../sql/mutation-builder';
@@ -0,0 +1,34 @@
1
+ import { planInvalid } from '@prisma-next/plan';
2
+ import type { AnyColumnBuilder } from '@prisma-next/sql-relational-core/types';
3
+
4
+ /**
5
+ * Asserts that a ColumnBuilder has table and column properties.
6
+ */
7
+ export function assertColumnBuilder(col: unknown, context: string): AnyColumnBuilder {
8
+ if (
9
+ typeof col === 'object' &&
10
+ col !== null &&
11
+ 'table' in col &&
12
+ 'column' in col &&
13
+ typeof (col as { table: unknown }).table === 'string' &&
14
+ typeof (col as { column: unknown }).column === 'string'
15
+ ) {
16
+ return col as AnyColumnBuilder;
17
+ }
18
+ throw planInvalid(`ColumnBuilder missing table/column in ${context}`);
19
+ }
20
+
21
+ /**
22
+ * Asserts that a JoinOnPredicate has valid left and right columns.
23
+ */
24
+ export function assertJoinOnPredicate(on: {
25
+ left?: { table?: string; column?: string };
26
+ right?: { table?: string; column?: string };
27
+ }): asserts on is {
28
+ left: { table: string; column: string };
29
+ right: { table: string; column: string };
30
+ } {
31
+ if (!on.left?.table || !on.left?.column || !on.right?.table || !on.right?.column) {
32
+ throw planInvalid('JoinOnPredicate missing required table/column properties');
33
+ }
34
+ }
@@ -0,0 +1,39 @@
1
+ import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
2
+ import {
3
+ errorIncludeCapabilitiesNotTrue,
4
+ errorIncludeRequiresCapabilities,
5
+ errorReturningCapabilityNotTrue,
6
+ errorReturningRequiresCapability,
7
+ } from './errors';
8
+
9
+ export function checkIncludeCapabilities(contract: SqlContract<SqlStorage>): void {
10
+ const target = contract.target;
11
+ const contractCapabilities = contract.capabilities;
12
+ const declaredTargetCapabilities = contractCapabilities?.[target];
13
+
14
+ if (!contractCapabilities || !declaredTargetCapabilities) {
15
+ errorIncludeRequiresCapabilities(target);
16
+ }
17
+
18
+ if (
19
+ declaredTargetCapabilities['lateral'] !== true ||
20
+ declaredTargetCapabilities['jsonAgg'] !== true
21
+ ) {
22
+ errorIncludeCapabilitiesNotTrue(target, {
23
+ lateral: declaredTargetCapabilities['lateral'],
24
+ jsonAgg: declaredTargetCapabilities['jsonAgg'],
25
+ });
26
+ }
27
+ }
28
+
29
+ export function checkReturningCapability(contract: SqlContract<SqlStorage>): void {
30
+ const target = contract.target;
31
+ const capabilities = contract.capabilities;
32
+ if (!capabilities || !capabilities[target]) {
33
+ errorReturningRequiresCapability(target);
34
+ }
35
+ const targetCapabilities = capabilities[target];
36
+ if (targetCapabilities['returning'] !== true) {
37
+ errorReturningCapabilityNotTrue(target, targetCapabilities['returning']);
38
+ }
39
+ }