@prisma-next/sql-lane 0.3.0-dev.4 → 0.3.0-dev.6

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