@prisma-next/sql-builder 0.0.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.
@@ -0,0 +1,317 @@
1
+ import type { StorageTable } from '@prisma-next/sql-contract/types';
2
+ import {
3
+ type AnyExpression as AstExpression,
4
+ ColumnRef,
5
+ DeleteAst,
6
+ InsertAst,
7
+ ParamRef,
8
+ TableSource,
9
+ UpdateAst,
10
+ } from '@prisma-next/sql-relational-core/ast';
11
+ import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
12
+ import type { MutationDefaultsOp } from '@prisma-next/sql-relational-core/query-lane-context';
13
+ import type { ExpressionBuilder } from '../expression';
14
+ import type { ResolveRow } from '../resolve';
15
+ import type { QueryContext, Scope, ScopeField } from '../scope';
16
+ import type {
17
+ DeleteQuery,
18
+ InsertQuery,
19
+ ReturningCapability,
20
+ UpdateQuery,
21
+ } from '../types/mutation-query';
22
+ import {
23
+ BuilderBase,
24
+ type BuilderContext,
25
+ buildQueryPlan,
26
+ combineWhereExprs,
27
+ } from './builder-base';
28
+ import { createFieldProxy } from './field-proxy';
29
+ import { createFunctions } from './functions';
30
+
31
+ type WhereCallback = ExpressionBuilder<Scope, QueryContext>;
32
+
33
+ function buildParamValues(
34
+ values: Record<string, unknown>,
35
+ table: StorageTable,
36
+ tableName: string,
37
+ op: MutationDefaultsOp,
38
+ ctx: BuilderContext,
39
+ ): Record<string, ParamRef> {
40
+ const params: Record<string, ParamRef> = {};
41
+ for (const [col, value] of Object.entries(values)) {
42
+ const column = table.columns[col];
43
+ params[col] = ParamRef.of(value, column ? { codecId: column.codecId } : undefined);
44
+ }
45
+ for (const def of ctx.applyMutationDefaults({ op, table: tableName, values })) {
46
+ const column = table.columns[def.column];
47
+ params[def.column] = ParamRef.of(def.value, column ? { codecId: column.codecId } : undefined);
48
+ }
49
+ return params;
50
+ }
51
+
52
+ function buildReturningColumnRefs(tableName: string, columns: string[]): ColumnRef[] {
53
+ return columns.map((col) => ColumnRef.of(tableName, col));
54
+ }
55
+
56
+ function evaluateWhere(
57
+ whereCallback: WhereCallback,
58
+ scope: Scope,
59
+ queryOperationTypes: BuilderContext['queryOperationTypes'],
60
+ ): AstExpression {
61
+ const fieldProxy = createFieldProxy(scope);
62
+ const fns = createFunctions(queryOperationTypes);
63
+ const result = whereCallback(fieldProxy, fns as never);
64
+ return result.buildAst();
65
+ }
66
+
67
+ export class InsertQueryImpl<
68
+ QC extends QueryContext = QueryContext,
69
+ AvailableScope extends Scope = Scope,
70
+ RowType extends Record<string, ScopeField> = Record<string, ScopeField>,
71
+ >
72
+ extends BuilderBase<QC['capabilities']>
73
+ implements InsertQuery<QC, AvailableScope, RowType>
74
+ {
75
+ readonly #tableName: string;
76
+ readonly #table: StorageTable;
77
+ readonly #scope: Scope;
78
+ readonly #values: Record<string, unknown>;
79
+ readonly #returningColumns: string[];
80
+ readonly #rowFields: Record<string, ScopeField>;
81
+
82
+ constructor(
83
+ tableName: string,
84
+ table: StorageTable,
85
+ scope: Scope,
86
+ values: Record<string, unknown>,
87
+ ctx: BuilderContext,
88
+ returningColumns: string[] = [],
89
+ rowFields: Record<string, ScopeField> = {},
90
+ ) {
91
+ super(ctx);
92
+ this.#tableName = tableName;
93
+ this.#table = table;
94
+ this.#scope = scope;
95
+ this.#values = values;
96
+ this.#returningColumns = returningColumns;
97
+ this.#rowFields = rowFields;
98
+ }
99
+
100
+ returning = this._gate<ReturningCapability, string[], InsertQuery<QC, AvailableScope, never>>(
101
+ { sql: { returning: true } },
102
+ 'returning',
103
+ (...columns: string[]) => {
104
+ const newRowFields: Record<string, ScopeField> = {};
105
+ for (const col of columns) {
106
+ const field = this.#scope.topLevel[col];
107
+ if (!field) throw new Error(`Column "${col}" not found in scope`);
108
+ newRowFields[col] = field;
109
+ }
110
+ return new InsertQueryImpl(
111
+ this.#tableName,
112
+ this.#table,
113
+ this.#scope,
114
+ this.#values,
115
+ this.ctx,
116
+ columns,
117
+ newRowFields,
118
+ ) as unknown as InsertQuery<QC, AvailableScope, never>;
119
+ },
120
+ );
121
+
122
+ build(): SqlQueryPlan<ResolveRow<RowType, QC['codecTypes']>> {
123
+ const paramValues = buildParamValues(
124
+ this.#values,
125
+ this.#table,
126
+ this.#tableName,
127
+ 'create',
128
+ this.ctx,
129
+ );
130
+
131
+ let ast = InsertAst.into(TableSource.named(this.#tableName)).withValues(paramValues);
132
+
133
+ if (this.#returningColumns.length > 0) {
134
+ ast = ast.withReturning(buildReturningColumnRefs(this.#tableName, this.#returningColumns));
135
+ }
136
+
137
+ return buildQueryPlan<ResolveRow<RowType, QC['codecTypes']>>(ast, this.#rowFields, this.ctx);
138
+ }
139
+ }
140
+
141
+ export class UpdateQueryImpl<
142
+ QC extends QueryContext = QueryContext,
143
+ AvailableScope extends Scope = Scope,
144
+ RowType extends Record<string, ScopeField> = Record<string, ScopeField>,
145
+ >
146
+ extends BuilderBase<QC['capabilities']>
147
+ implements UpdateQuery<QC, AvailableScope, RowType>
148
+ {
149
+ readonly #tableName: string;
150
+ readonly #table: StorageTable;
151
+ readonly #scope: Scope;
152
+ readonly #setValues: Record<string, unknown>;
153
+ readonly #whereCallbacks: readonly WhereCallback[];
154
+ readonly #returningColumns: string[];
155
+ readonly #rowFields: Record<string, ScopeField>;
156
+
157
+ constructor(
158
+ tableName: string,
159
+ table: StorageTable,
160
+ scope: Scope,
161
+ setValues: Record<string, unknown>,
162
+ ctx: BuilderContext,
163
+ whereCallbacks: readonly WhereCallback[] = [],
164
+ returningColumns: string[] = [],
165
+ rowFields: Record<string, ScopeField> = {},
166
+ ) {
167
+ super(ctx);
168
+ this.#tableName = tableName;
169
+ this.#table = table;
170
+ this.#scope = scope;
171
+ this.#setValues = setValues;
172
+ this.#whereCallbacks = whereCallbacks;
173
+ this.#returningColumns = returningColumns;
174
+ this.#rowFields = rowFields;
175
+ }
176
+
177
+ where(expr: ExpressionBuilder<AvailableScope, QC>): UpdateQuery<QC, AvailableScope, RowType> {
178
+ return new UpdateQueryImpl(
179
+ this.#tableName,
180
+ this.#table,
181
+ this.#scope,
182
+ this.#setValues,
183
+ this.ctx,
184
+ [...this.#whereCallbacks, expr as unknown as WhereCallback],
185
+ this.#returningColumns,
186
+ this.#rowFields,
187
+ );
188
+ }
189
+
190
+ returning = this._gate<ReturningCapability, string[], UpdateQuery<QC, AvailableScope, never>>(
191
+ { sql: { returning: true } },
192
+ 'returning',
193
+ (...columns: string[]) => {
194
+ const newRowFields: Record<string, ScopeField> = {};
195
+ for (const col of columns) {
196
+ const field = this.#scope.topLevel[col];
197
+ if (!field) throw new Error(`Column "${col}" not found in scope`);
198
+ newRowFields[col] = field;
199
+ }
200
+ return new UpdateQueryImpl(
201
+ this.#tableName,
202
+ this.#table,
203
+ this.#scope,
204
+ this.#setValues,
205
+ this.ctx,
206
+ this.#whereCallbacks,
207
+ columns,
208
+ newRowFields,
209
+ ) as unknown as UpdateQuery<QC, AvailableScope, never>;
210
+ },
211
+ );
212
+
213
+ build(): SqlQueryPlan<ResolveRow<RowType, QC['codecTypes']>> {
214
+ const setParams = buildParamValues(
215
+ this.#setValues,
216
+ this.#table,
217
+ this.#tableName,
218
+ 'update',
219
+ this.ctx,
220
+ );
221
+
222
+ const whereExpr = combineWhereExprs(
223
+ this.#whereCallbacks.map((cb) =>
224
+ evaluateWhere(cb, this.#scope, this.ctx.queryOperationTypes),
225
+ ),
226
+ );
227
+
228
+ let ast = UpdateAst.table(TableSource.named(this.#tableName))
229
+ .withSet(setParams)
230
+ .withWhere(whereExpr);
231
+
232
+ if (this.#returningColumns.length > 0) {
233
+ ast = ast.withReturning(buildReturningColumnRefs(this.#tableName, this.#returningColumns));
234
+ }
235
+
236
+ return buildQueryPlan<ResolveRow<RowType, QC['codecTypes']>>(ast, this.#rowFields, this.ctx);
237
+ }
238
+ }
239
+
240
+ export class DeleteQueryImpl<
241
+ QC extends QueryContext = QueryContext,
242
+ AvailableScope extends Scope = Scope,
243
+ RowType extends Record<string, ScopeField> = Record<string, ScopeField>,
244
+ >
245
+ extends BuilderBase<QC['capabilities']>
246
+ implements DeleteQuery<QC, AvailableScope, RowType>
247
+ {
248
+ readonly #tableName: string;
249
+ readonly #scope: Scope;
250
+ readonly #whereCallbacks: readonly WhereCallback[];
251
+ readonly #returningColumns: string[];
252
+ readonly #rowFields: Record<string, ScopeField>;
253
+
254
+ constructor(
255
+ tableName: string,
256
+ scope: Scope,
257
+ ctx: BuilderContext,
258
+ whereCallbacks: readonly WhereCallback[] = [],
259
+ returningColumns: string[] = [],
260
+ rowFields: Record<string, ScopeField> = {},
261
+ ) {
262
+ super(ctx);
263
+ this.#tableName = tableName;
264
+ this.#scope = scope;
265
+ this.#whereCallbacks = whereCallbacks;
266
+ this.#returningColumns = returningColumns;
267
+ this.#rowFields = rowFields;
268
+ }
269
+
270
+ where(expr: ExpressionBuilder<AvailableScope, QC>): DeleteQuery<QC, AvailableScope, RowType> {
271
+ return new DeleteQueryImpl(
272
+ this.#tableName,
273
+ this.#scope,
274
+ this.ctx,
275
+ [...this.#whereCallbacks, expr as unknown as WhereCallback],
276
+ this.#returningColumns,
277
+ this.#rowFields,
278
+ );
279
+ }
280
+
281
+ returning = this._gate<ReturningCapability, string[], DeleteQuery<QC, AvailableScope, never>>(
282
+ { sql: { returning: true } },
283
+ 'returning',
284
+ (...columns: string[]) => {
285
+ const newRowFields: Record<string, ScopeField> = {};
286
+ for (const col of columns) {
287
+ const field = this.#scope.topLevel[col];
288
+ if (!field) throw new Error(`Column "${col}" not found in scope`);
289
+ newRowFields[col] = field;
290
+ }
291
+ return new DeleteQueryImpl(
292
+ this.#tableName,
293
+ this.#scope,
294
+ this.ctx,
295
+ this.#whereCallbacks,
296
+ columns,
297
+ newRowFields,
298
+ ) as unknown as DeleteQuery<QC, AvailableScope, never>;
299
+ },
300
+ );
301
+
302
+ build(): SqlQueryPlan<ResolveRow<RowType, QC['codecTypes']>> {
303
+ const whereExpr = combineWhereExprs(
304
+ this.#whereCallbacks.map((cb) =>
305
+ evaluateWhere(cb, this.#scope, this.ctx.queryOperationTypes),
306
+ ),
307
+ );
308
+
309
+ let ast = DeleteAst.from(TableSource.named(this.#tableName)).withWhere(whereExpr);
310
+
311
+ if (this.#returningColumns.length > 0) {
312
+ ast = ast.withReturning(buildReturningColumnRefs(this.#tableName, this.#returningColumns));
313
+ }
314
+
315
+ return buildQueryPlan<ResolveRow<RowType, QC['codecTypes']>>(ast, this.#rowFields, this.ctx);
316
+ }
317
+ }
@@ -0,0 +1,254 @@
1
+ import { DerivedTableSource, type SelectAst } from '@prisma-next/sql-relational-core/ast';
2
+ import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
3
+ import type {
4
+ AggregateFunctions,
5
+ BooleanCodecType,
6
+ Expression,
7
+ ExpressionBuilder,
8
+ ExtractScopeFields,
9
+ FieldProxy,
10
+ Functions,
11
+ OrderByOptions,
12
+ OrderByScope,
13
+ WithField,
14
+ WithFields,
15
+ } from '../expression';
16
+ import type { ResolveRow } from '../resolve';
17
+ import type {
18
+ Expand,
19
+ JoinOuterScope,
20
+ JoinSource,
21
+ QueryContext,
22
+ Scope,
23
+ ScopeField,
24
+ // biome-ignore lint/correctness/noUnusedImports: used in `declare` property
25
+ SubqueryMarker,
26
+ } from '../scope';
27
+ import type { GroupedQuery } from '../types/grouped-query';
28
+ import type { SelectQuery } from '../types/select-query';
29
+ import {
30
+ BuilderBase,
31
+ type BuilderContext,
32
+ type BuilderState,
33
+ buildPlan,
34
+ buildSelectAst,
35
+ cloneState,
36
+ orderByScopeOf,
37
+ resolveDistinctOn,
38
+ resolveGroupBy,
39
+ resolveOrderBy,
40
+ resolveSelectArgs,
41
+ } from './builder-base';
42
+ import { createFieldProxy } from './field-proxy';
43
+ import { createAggregateFunctions, createFunctions } from './functions';
44
+
45
+ abstract class QueryBase<
46
+ QC extends QueryContext = QueryContext,
47
+ AvailableScope extends Scope = Scope,
48
+ RowType extends Record<string, ScopeField> = Record<string, ScopeField>,
49
+ > extends BuilderBase<QC['capabilities']> {
50
+ protected readonly state: BuilderState;
51
+
52
+ constructor(state: BuilderState, ctx: BuilderContext) {
53
+ super(ctx);
54
+ this.state = state;
55
+ }
56
+
57
+ protected abstract clone(state: BuilderState): this;
58
+
59
+ distinctOn = this._gate(
60
+ { postgres: { distinctOn: true } },
61
+ 'distinctOn',
62
+ (...args: unknown[]) => {
63
+ const exprs = resolveDistinctOn(args, this.state.scope, this.state.rowFields, this.ctx);
64
+ return this.clone(
65
+ cloneState(this.state, {
66
+ distinctOn: [...(this.state.distinctOn ?? []), ...exprs],
67
+ }),
68
+ );
69
+ },
70
+ );
71
+
72
+ limit(count: number): this {
73
+ return this.clone(cloneState(this.state, { limit: count }));
74
+ }
75
+
76
+ offset(count: number): this {
77
+ return this.clone(cloneState(this.state, { offset: count }));
78
+ }
79
+
80
+ distinct(): this {
81
+ return this.clone(cloneState(this.state, { distinct: true }));
82
+ }
83
+
84
+ groupBy(
85
+ ...fields: ((keyof RowType | keyof AvailableScope['topLevel']) & string)[]
86
+ ): GroupedQuery<QC, AvailableScope, RowType>;
87
+ groupBy(
88
+ expr: (
89
+ fields: FieldProxy<OrderByScope<AvailableScope, RowType>>,
90
+ fns: Functions<QC>,
91
+ ) => Expression<ScopeField>,
92
+ ): GroupedQuery<QC, AvailableScope, RowType>;
93
+ groupBy(...args: unknown[]): unknown {
94
+ const exprs = resolveGroupBy(args, this.state.scope, this.state.rowFields, this.ctx);
95
+ return new GroupedQueryImpl<QC, AvailableScope, RowType>(
96
+ cloneState(this.state, { groupBy: [...this.state.groupBy, ...exprs] }),
97
+ this.ctx,
98
+ );
99
+ }
100
+
101
+ as<Alias extends string>(alias: Alias): JoinSource<RowType, Alias> {
102
+ const ast = buildSelectAst(this.state);
103
+ const derivedSource = DerivedTableSource.as(alias, ast);
104
+ const scope = {
105
+ topLevel: this.state.rowFields as RowType,
106
+ namespaces: { [alias]: this.state.rowFields } as Record<Alias, RowType>,
107
+ };
108
+ return {
109
+ getJoinOuterScope: () => scope,
110
+ buildAst: () => derivedSource,
111
+
112
+ // `as unknown` is necessary, because JoinOuterScope is a phantom type-only property that does not exist at runtime
113
+ } satisfies Omit<JoinSource<RowType, Alias>, typeof JoinOuterScope> as unknown as JoinSource<
114
+ RowType,
115
+ Alias
116
+ >;
117
+ }
118
+
119
+ getRowFields(): Record<string, ScopeField> {
120
+ return this.state.rowFields;
121
+ }
122
+
123
+ buildAst(): SelectAst {
124
+ return buildSelectAst(this.state);
125
+ }
126
+
127
+ build(): SqlQueryPlan<ResolveRow<RowType, QC['codecTypes']>> {
128
+ return buildPlan<ResolveRow<RowType, QC['codecTypes']>>(this.state, this.ctx);
129
+ }
130
+ }
131
+
132
+ export class SelectQueryImpl<
133
+ QC extends QueryContext = QueryContext,
134
+ AvailableScope extends Scope = Scope,
135
+ RowType extends Record<string, ScopeField> = Record<string, ScopeField>,
136
+ >
137
+ extends QueryBase<QC, AvailableScope, RowType>
138
+ implements SelectQuery<QC, AvailableScope, RowType>
139
+ {
140
+ declare readonly [SubqueryMarker]: RowType;
141
+
142
+ protected clone(state: BuilderState): this {
143
+ return new SelectQueryImpl<QC, AvailableScope, RowType>(state, this.ctx) as this;
144
+ }
145
+
146
+ select<Columns extends (keyof AvailableScope['topLevel'] & string)[]>(
147
+ ...columns: Columns
148
+ ): SelectQuery<QC, AvailableScope, WithFields<RowType, AvailableScope['topLevel'], Columns>>;
149
+ select<Alias extends string, Field extends ScopeField>(
150
+ alias: Alias,
151
+ expr: (fields: FieldProxy<AvailableScope>, fns: AggregateFunctions<QC>) => Expression<Field>,
152
+ ): SelectQuery<QC, AvailableScope, WithField<RowType, Field, Alias>>;
153
+ select<Result extends Record<string, Expression<ScopeField>>>(
154
+ callback: (fields: FieldProxy<AvailableScope>, fns: AggregateFunctions<QC>) => Result,
155
+ ): SelectQuery<QC, AvailableScope, Expand<RowType & ExtractScopeFields<Result>>>;
156
+ select(...args: unknown[]): unknown {
157
+ const { projections, newRowFields } = resolveSelectArgs(args, this.state.scope, this.ctx);
158
+ return new SelectQueryImpl(
159
+ cloneState(this.state, {
160
+ projections: [...this.state.projections, ...projections],
161
+ rowFields: { ...this.state.rowFields, ...newRowFields },
162
+ }),
163
+ this.ctx,
164
+ );
165
+ }
166
+
167
+ where(expr: ExpressionBuilder<AvailableScope, QC>): SelectQuery<QC, AvailableScope, RowType> {
168
+ const fieldProxy = createFieldProxy(this.state.scope);
169
+ const fns = createFunctions<QC>(this.ctx.queryOperationTypes);
170
+ const result = (expr as ExpressionBuilder<Scope, QueryContext>)(fieldProxy, fns as never);
171
+ return new SelectQueryImpl(
172
+ cloneState(this.state, {
173
+ where: [...this.state.where, result.buildAst()],
174
+ }),
175
+ this.ctx,
176
+ );
177
+ }
178
+
179
+ orderBy(
180
+ field: (keyof RowType | keyof AvailableScope['topLevel']) & string,
181
+ options?: OrderByOptions,
182
+ ): SelectQuery<QC, AvailableScope, RowType>;
183
+ orderBy(
184
+ expr: (
185
+ fields: FieldProxy<OrderByScope<AvailableScope, RowType>>,
186
+ fns: Functions<QC>,
187
+ ) => Expression<ScopeField>,
188
+ options?: OrderByOptions,
189
+ ): SelectQuery<QC, AvailableScope, RowType>;
190
+ orderBy(arg: unknown, options?: OrderByOptions): unknown {
191
+ const item = resolveOrderBy(
192
+ arg,
193
+ options,
194
+ this.state.scope,
195
+ this.state.rowFields,
196
+ this.ctx,
197
+ false,
198
+ );
199
+ return this.clone(cloneState(this.state, { orderBy: [...this.state.orderBy, item] }));
200
+ }
201
+ }
202
+
203
+ export class GroupedQueryImpl<
204
+ QC extends QueryContext = QueryContext,
205
+ AvailableScope extends Scope = Scope,
206
+ RowType extends Record<string, ScopeField> = Record<string, ScopeField>,
207
+ >
208
+ extends QueryBase<QC, AvailableScope, RowType>
209
+ implements GroupedQuery<QC, AvailableScope, RowType>
210
+ {
211
+ declare readonly [SubqueryMarker]: RowType;
212
+
213
+ protected clone(state: BuilderState): this {
214
+ return new GroupedQueryImpl<QC, AvailableScope, RowType>(state, this.ctx) as this;
215
+ }
216
+
217
+ having(
218
+ expr: (
219
+ fields: FieldProxy<OrderByScope<AvailableScope, RowType>>,
220
+ fns: AggregateFunctions<QC>,
221
+ ) => Expression<BooleanCodecType>,
222
+ ): GroupedQuery<QC, AvailableScope, RowType> {
223
+ const combined = orderByScopeOf(
224
+ this.state.scope as AvailableScope,
225
+ this.state.rowFields as RowType,
226
+ );
227
+ const fns = createAggregateFunctions(this.ctx.queryOperationTypes);
228
+ const result = expr(createFieldProxy(combined), fns);
229
+ return new GroupedQueryImpl(cloneState(this.state, { having: result.buildAst() }), this.ctx);
230
+ }
231
+
232
+ orderBy(
233
+ field: (keyof RowType | keyof AvailableScope['topLevel']) & string,
234
+ options?: OrderByOptions,
235
+ ): GroupedQuery<QC, AvailableScope, RowType>;
236
+ orderBy(
237
+ expr: (
238
+ fields: FieldProxy<OrderByScope<AvailableScope, RowType>>,
239
+ fns: AggregateFunctions<QC>,
240
+ ) => Expression<ScopeField>,
241
+ options?: OrderByOptions,
242
+ ): GroupedQuery<QC, AvailableScope, RowType>;
243
+ orderBy(arg: unknown, options?: OrderByOptions): unknown {
244
+ const item = resolveOrderBy(
245
+ arg,
246
+ options,
247
+ this.state.scope,
248
+ this.state.rowFields,
249
+ this.ctx,
250
+ true,
251
+ );
252
+ return this.clone(cloneState(this.state, { orderBy: [...this.state.orderBy, item] }));
253
+ }
254
+ }
@@ -0,0 +1,31 @@
1
+ import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
2
+ import type { ExecutionContext } from '@prisma-next/sql-relational-core/query-lane-context';
3
+ import type { Db } from '../types/db';
4
+ import type { BuilderContext } from './builder-base';
5
+ import { TableProxyImpl } from './table-proxy-impl';
6
+
7
+ export interface SqlOptions<C extends SqlContract<SqlStorage>> {
8
+ readonly context: ExecutionContext<C>;
9
+ }
10
+
11
+ export function sql<C extends SqlContract<SqlStorage>>(options: SqlOptions<C>): Db<C> {
12
+ const { context } = options;
13
+ const ctx: BuilderContext = {
14
+ capabilities: context.contract.capabilities,
15
+ queryOperationTypes: context.queryOperations.entries(),
16
+ target: context.contract.target ?? 'unknown',
17
+ storageHash: context.contract.storageHash ?? 'unknown',
18
+ applyMutationDefaults: (options) => context.applyMutationDefaults(options),
19
+ };
20
+
21
+ return new Proxy({} as Db<C>, {
22
+ get(_target, prop: string) {
23
+ const tables = context.contract.storage.tables;
24
+ const table = Object.hasOwn(tables, prop) ? tables[prop] : undefined;
25
+ if (table) {
26
+ return new TableProxyImpl(prop, table, prop, ctx);
27
+ }
28
+ return undefined;
29
+ },
30
+ });
31
+ }