@prisma-next/sql-relational-core 0.3.0-dev.4 → 0.3.0-dev.41

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 (138) hide show
  1. package/README.md +5 -2
  2. package/dist/errors-7_V3El9I.mjs +3 -0
  3. package/dist/errors-DVufq9PT.d.mts +2 -0
  4. package/dist/exports/ast.d.mts +189 -0
  5. package/dist/exports/ast.d.mts.map +1 -0
  6. package/dist/exports/ast.mjs +360 -0
  7. package/dist/exports/ast.mjs.map +1 -0
  8. package/dist/exports/errors.d.mts +2 -0
  9. package/dist/exports/errors.mjs +3 -0
  10. package/dist/exports/guards.d.mts +5 -0
  11. package/dist/exports/guards.mjs +3 -0
  12. package/dist/exports/operations-registry.d.mts +5 -0
  13. package/dist/exports/operations-registry.mjs +3 -0
  14. package/dist/exports/param.d.mts +5 -0
  15. package/dist/exports/param.mjs +3 -0
  16. package/dist/exports/plan.d.mts +2 -0
  17. package/dist/exports/plan.mjs +17 -0
  18. package/dist/exports/plan.mjs.map +1 -0
  19. package/dist/exports/query-lane-context.d.mts +2 -0
  20. package/dist/exports/query-lane-context.mjs +1 -0
  21. package/dist/exports/schema.d.mts +5 -0
  22. package/dist/exports/schema.mjs +4 -0
  23. package/dist/exports/types.d.mts +4 -0
  24. package/dist/exports/types.mjs +3 -0
  25. package/dist/exports/utils/guards.d.mts +5 -0
  26. package/dist/exports/utils/guards.mjs +4 -0
  27. package/dist/guards-0ycyntYX.mjs +132 -0
  28. package/dist/guards-0ycyntYX.mjs.map +1 -0
  29. package/dist/guards-l1R3q8pA.d.mts +87 -0
  30. package/dist/guards-l1R3q8pA.d.mts.map +1 -0
  31. package/dist/index.d.mts +12 -0
  32. package/dist/index.mjs +12 -0
  33. package/dist/{exports/operations-registry.d.ts → operations-registry-0GuSkOCC.d.mts} +7 -11
  34. package/dist/operations-registry-0GuSkOCC.d.mts.map +1 -0
  35. package/dist/operations-registry-DPZ5aElH.mjs +152 -0
  36. package/dist/operations-registry-DPZ5aElH.mjs.map +1 -0
  37. package/dist/param-ChBZwVcw.d.mts +8 -0
  38. package/dist/param-ChBZwVcw.d.mts.map +1 -0
  39. package/dist/param-DMU3OWfI.mjs +14 -0
  40. package/dist/param-DMU3OWfI.mjs.map +1 -0
  41. package/dist/plan-cjDF_yFX.d.mts +182 -0
  42. package/dist/plan-cjDF_yFX.d.mts.map +1 -0
  43. package/dist/query-lane-context-CgkPuKaR.d.mts +259 -0
  44. package/dist/query-lane-context-CgkPuKaR.d.mts.map +1 -0
  45. package/dist/schema-Bi5p4aAc.mjs +175 -0
  46. package/dist/schema-Bi5p4aAc.mjs.map +1 -0
  47. package/dist/schema-CEpzzWNY.d.mts +86 -0
  48. package/dist/schema-CEpzzWNY.d.mts.map +1 -0
  49. package/dist/types-Cdi4Whda.mjs +18 -0
  50. package/dist/types-Cdi4Whda.mjs.map +1 -0
  51. package/dist/types-DicmXfBm.d.mts +447 -0
  52. package/dist/types-DicmXfBm.d.mts.map +1 -0
  53. package/package.json +41 -57
  54. package/src/ast/adapter-types.ts +36 -0
  55. package/src/ast/codec-types.ts +444 -0
  56. package/src/ast/common.ts +36 -0
  57. package/src/ast/delete.ts +17 -0
  58. package/src/ast/driver-types.ts +42 -0
  59. package/src/ast/insert.ts +17 -0
  60. package/src/ast/join.ts +54 -0
  61. package/src/ast/order.ts +11 -0
  62. package/src/ast/predicate.ts +40 -0
  63. package/src/ast/select.ts +38 -0
  64. package/src/ast/sql-codecs.ts +67 -0
  65. package/src/ast/types.ts +187 -0
  66. package/src/ast/update.ts +19 -0
  67. package/src/ast/util.ts +9 -0
  68. package/src/exports/ast.ts +14 -0
  69. package/src/exports/errors.ts +1 -0
  70. package/src/exports/guards.ts +15 -0
  71. package/src/exports/operations-registry.ts +1 -0
  72. package/src/exports/param.ts +2 -0
  73. package/src/exports/plan.ts +1 -0
  74. package/src/exports/query-lane-context.ts +1 -0
  75. package/src/exports/schema.ts +6 -0
  76. package/src/exports/types.ts +1 -0
  77. package/src/exports/utils/guards.ts +1 -0
  78. package/src/index.ts +8 -0
  79. package/src/operations-registry.ts +276 -0
  80. package/src/param.ts +15 -0
  81. package/src/plan.ts +39 -0
  82. package/src/query-lane-context.ts +94 -0
  83. package/src/schema.ts +431 -0
  84. package/src/types.ts +799 -0
  85. package/src/utils/guards.ts +193 -0
  86. package/dist/chunk-2F7DSEOU.js +0 -8
  87. package/dist/chunk-2F7DSEOU.js.map +0 -1
  88. package/dist/chunk-36WJWNHT.js +0 -1
  89. package/dist/chunk-36WJWNHT.js.map +0 -1
  90. package/dist/chunk-5N34PNVZ.js +0 -62
  91. package/dist/chunk-5N34PNVZ.js.map +0 -1
  92. package/dist/chunk-7I3EMQID.js +0 -16
  93. package/dist/chunk-7I3EMQID.js.map +0 -1
  94. package/dist/chunk-CBTYMOX2.js +0 -152
  95. package/dist/chunk-CBTYMOX2.js.map +0 -1
  96. package/dist/chunk-G52ENULI.js +0 -1
  97. package/dist/chunk-G52ENULI.js.map +0 -1
  98. package/dist/chunk-KYSP7L5C.js +0 -16
  99. package/dist/chunk-KYSP7L5C.js.map +0 -1
  100. package/dist/chunk-M23L3JHG.js +0 -159
  101. package/dist/chunk-M23L3JHG.js.map +0 -1
  102. package/dist/chunk-MM74SVJ4.js +0 -13
  103. package/dist/chunk-MM74SVJ4.js.map +0 -1
  104. package/dist/chunk-U7AXAUJA.js +0 -1
  105. package/dist/chunk-U7AXAUJA.js.map +0 -1
  106. package/dist/chunk-WZBPVEZI.js +0 -320
  107. package/dist/chunk-WZBPVEZI.js.map +0 -1
  108. package/dist/exports/ast.d.ts +0 -119
  109. package/dist/exports/ast.js +0 -46
  110. package/dist/exports/ast.js.map +0 -1
  111. package/dist/exports/errors.js +0 -9
  112. package/dist/exports/errors.js.map +0 -1
  113. package/dist/exports/guards.d.ts +0 -63
  114. package/dist/exports/guards.js +0 -21
  115. package/dist/exports/guards.js.map +0 -1
  116. package/dist/exports/operations-registry.js +0 -9
  117. package/dist/exports/operations-registry.js.map +0 -1
  118. package/dist/exports/param.d.ts +0 -14
  119. package/dist/exports/param.js +0 -7
  120. package/dist/exports/param.js.map +0 -1
  121. package/dist/exports/plan.d.ts +0 -5
  122. package/dist/exports/plan.js +0 -7
  123. package/dist/exports/plan.js.map +0 -1
  124. package/dist/exports/query-lane-context.d.ts +0 -4
  125. package/dist/exports/query-lane-context.js +0 -2
  126. package/dist/exports/query-lane-context.js.map +0 -1
  127. package/dist/exports/schema.d.ts +0 -69
  128. package/dist/exports/schema.js +0 -14
  129. package/dist/exports/schema.js.map +0 -1
  130. package/dist/exports/types.d.ts +0 -335
  131. package/dist/exports/types.js +0 -10
  132. package/dist/exports/types.js.map +0 -1
  133. package/dist/index.d.ts +0 -13
  134. package/dist/index.js +0 -81
  135. package/dist/index.js.map +0 -1
  136. package/dist/plan-D0OG5qzy.d.ts +0 -142
  137. package/dist/query-lane-context-BhOMmb_K.d.ts +0 -158
  138. /package/{dist/exports/errors.d.ts → src/errors.ts} +0 -0
@@ -0,0 +1,94 @@
1
+ import type { OperationRegistry } from '@prisma-next/operations';
2
+ import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
3
+ import type { CodecRegistry } from './ast/codec-types';
4
+
5
+ /**
6
+ * Registry of initialized type helpers from storage.types.
7
+ * Each key is a type name from storage.types, and the value is:
8
+ * - The result of the codec's init hook (if provided), or
9
+ * - The full StorageTypeInstance metadata (codecId, nativeType, typeParams) if no init hook
10
+ */
11
+ export type TypeHelperRegistry = Record<string, unknown>;
12
+
13
+ // =============================================================================
14
+ // JSON Schema Validation Types
15
+ // =============================================================================
16
+
17
+ /**
18
+ * A single validation error from JSON Schema validation.
19
+ */
20
+ export interface JsonSchemaValidationError {
21
+ readonly path: string;
22
+ readonly message: string;
23
+ readonly keyword: string;
24
+ }
25
+
26
+ /**
27
+ * Result of a JSON Schema validation.
28
+ */
29
+ export type JsonSchemaValidationResult =
30
+ | { readonly valid: true }
31
+ | { readonly valid: false; readonly errors: ReadonlyArray<JsonSchemaValidationError> };
32
+
33
+ /**
34
+ * A compiled JSON Schema validate function.
35
+ * Returns a structured result indicating whether the value conforms to the schema.
36
+ */
37
+ export type JsonSchemaValidateFn = (value: unknown) => JsonSchemaValidationResult;
38
+
39
+ /**
40
+ * Registry of compiled JSON Schema validators for columns with typed JSON/JSONB.
41
+ *
42
+ * Built during context creation by scanning the contract for columns whose codec
43
+ * descriptor provides an `init` hook that returns a `{ validate }` helper.
44
+ * Keys are `"table.column"` (e.g., `"user.metadata"`).
45
+ */
46
+ export interface JsonSchemaValidatorRegistry {
47
+ /** Get the compiled validator for a column. Key format: "table.column". */
48
+ get(key: string): JsonSchemaValidateFn | undefined;
49
+ /** Number of registered validators. */
50
+ readonly size: number;
51
+ }
52
+
53
+ export type MutationDefaultsOp = 'create' | 'update';
54
+
55
+ export type AppliedMutationDefault = {
56
+ readonly column: string;
57
+ readonly value: unknown;
58
+ };
59
+
60
+ export type MutationDefaultsOptions = {
61
+ readonly op: MutationDefaultsOp;
62
+ readonly table: string;
63
+ readonly values: Record<string, unknown>;
64
+ };
65
+
66
+ /**
67
+ * Minimal context interface for SQL query lanes.
68
+ *
69
+ * Lanes only need contract, operations, and codecs to build typed ASTs and attach
70
+ * operation builders. This interface explicitly excludes runtime concerns like
71
+ * adapters, connection management, and transaction state.
72
+ */
73
+ export interface ExecutionContext<
74
+ TContract extends SqlContract<SqlStorage> = SqlContract<SqlStorage>,
75
+ > {
76
+ readonly contract: TContract;
77
+ readonly operations: OperationRegistry;
78
+ readonly codecs: CodecRegistry;
79
+ /**
80
+ * Type helper registry for parameterized types.
81
+ * Schema builders expose these helpers via schema.types.
82
+ */
83
+ readonly types: TypeHelperRegistry;
84
+ /**
85
+ * Compiled JSON Schema validators for typed JSON/JSONB columns.
86
+ * Present only when the contract declares columns with JSON Schema typeParams.
87
+ */
88
+ readonly jsonSchemaValidators?: JsonSchemaValidatorRegistry;
89
+ /**
90
+ * Applies execution-time mutation defaults for the given table.
91
+ * Returns the applied defaults (caller-provided values always win).
92
+ */
93
+ applyMutationDefaults(options: MutationDefaultsOptions): ReadonlyArray<AppliedMutationDefault>;
94
+ }
package/src/schema.ts ADDED
@@ -0,0 +1,431 @@
1
+ import type { OperationRegistry } from '@prisma-next/operations';
2
+ import { planInvalid } from '@prisma-next/plan';
3
+ import type {
4
+ ExtractCodecTypes,
5
+ ExtractOperationTypes,
6
+ SqlContract,
7
+ SqlStorage,
8
+ StorageColumn,
9
+ } from '@prisma-next/sql-contract/types';
10
+ import type { BinaryOp, ColumnRef, ExpressionSource, TableRef } from './ast/types';
11
+ import { attachOperationsToColumnBuilder } from './operations-registry';
12
+ import type { ExecutionContext } from './query-lane-context';
13
+ import type {
14
+ BinaryBuilder,
15
+ CodecTypes as CodecTypesType,
16
+ ColumnBuilder,
17
+ ComputeColumnJsType,
18
+ NullCheckBuilder,
19
+ OperationTypeSignature,
20
+ OperationTypes,
21
+ OrderBuilder,
22
+ ParamPlaceholder,
23
+ } from './types';
24
+
25
+ type TableColumns<Table extends { columns: Record<string, StorageColumn> }> = Table['columns'];
26
+
27
+ type ColumnBuilders<
28
+ Contract extends SqlContract<SqlStorage>,
29
+ TableName extends string,
30
+ Columns extends Record<string, StorageColumn>,
31
+ CodecTypes extends CodecTypesType,
32
+ Operations extends OperationTypes,
33
+ > = {
34
+ readonly [K in keyof Columns]: ColumnBuilder<
35
+ K & string,
36
+ Columns[K],
37
+ ComputeColumnJsType<Contract, TableName, K & string, Columns[K], CodecTypes>,
38
+ Operations
39
+ >;
40
+ };
41
+
42
+ export class ColumnBuilderImpl<
43
+ ColumnName extends string,
44
+ ColumnMeta extends StorageColumn,
45
+ JsType = unknown,
46
+ > implements ExpressionSource
47
+ {
48
+ readonly kind = 'column' as const;
49
+
50
+ constructor(
51
+ readonly table: string,
52
+ readonly column: ColumnName,
53
+ private readonly storageColumn: ColumnMeta,
54
+ ) {}
55
+
56
+ get columnMeta(): ColumnMeta {
57
+ return this.storageColumn;
58
+ }
59
+
60
+ // Type-level helper property (not used at runtime)
61
+ get __jsType(): JsType {
62
+ return undefined as unknown as JsType;
63
+ }
64
+
65
+ /**
66
+ * Converts this column builder to a ColumnRef expression.
67
+ * This is the canonical way to get an AST node from a builder.
68
+ */
69
+ toExpr(): ColumnRef {
70
+ return Object.freeze({
71
+ kind: 'col' as const,
72
+ table: this.table,
73
+ column: this.column,
74
+ });
75
+ }
76
+
77
+ private createBinaryBuilder(
78
+ op: BinaryOp,
79
+ value: ParamPlaceholder | ExpressionSource,
80
+ ): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
81
+ if (value == null) {
82
+ throw planInvalid(
83
+ 'Parameter placeholder or expression source required for column comparison',
84
+ );
85
+ }
86
+ // Check for ExpressionSource first (has toExpr method)
87
+ if ('toExpr' in value && typeof value.toExpr === 'function') {
88
+ return Object.freeze({
89
+ kind: 'binary' as const,
90
+ op,
91
+ left: this.toExpr(),
92
+ right: value,
93
+ }) as BinaryBuilder<ColumnName, ColumnMeta, JsType>;
94
+ }
95
+ // Must be a ParamPlaceholder
96
+ if ('kind' in value && value.kind === 'param-placeholder') {
97
+ return Object.freeze({
98
+ kind: 'binary' as const,
99
+ op,
100
+ left: this.toExpr(),
101
+ right: value,
102
+ }) as BinaryBuilder<ColumnName, ColumnMeta, JsType>;
103
+ }
104
+ throw planInvalid('Parameter placeholder or expression source required for column comparison');
105
+ }
106
+
107
+ eq(value: ParamPlaceholder | ExpressionSource): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
108
+ return this.createBinaryBuilder('eq', value);
109
+ }
110
+
111
+ neq(value: ParamPlaceholder | ExpressionSource): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
112
+ return this.createBinaryBuilder('neq', value);
113
+ }
114
+
115
+ gt(value: ParamPlaceholder | ExpressionSource): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
116
+ return this.createBinaryBuilder('gt', value);
117
+ }
118
+
119
+ lt(value: ParamPlaceholder | ExpressionSource): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
120
+ return this.createBinaryBuilder('lt', value);
121
+ }
122
+
123
+ gte(value: ParamPlaceholder | ExpressionSource): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
124
+ return this.createBinaryBuilder('gte', value);
125
+ }
126
+
127
+ lte(value: ParamPlaceholder | ExpressionSource): BinaryBuilder<ColumnName, ColumnMeta, JsType> {
128
+ return this.createBinaryBuilder('lte', value);
129
+ }
130
+
131
+ asc(): OrderBuilder<ColumnName, ColumnMeta, JsType> {
132
+ return Object.freeze({
133
+ kind: 'order' as const,
134
+ expr: this.toExpr(),
135
+ dir: 'asc' as const,
136
+ }) as OrderBuilder<ColumnName, ColumnMeta, JsType>;
137
+ }
138
+
139
+ desc(): OrderBuilder<ColumnName, ColumnMeta, JsType> {
140
+ return Object.freeze({
141
+ kind: 'order' as const,
142
+ expr: this.toExpr(),
143
+ dir: 'desc' as const,
144
+ }) as OrderBuilder<ColumnName, ColumnMeta, JsType>;
145
+ }
146
+
147
+ /**
148
+ * Creates an IS NULL check for this column.
149
+ * Available on all columns at runtime, but typed to only be visible on nullable columns.
150
+ */
151
+ isNull(): NullCheckBuilder<ColumnName, ColumnMeta, JsType> {
152
+ return Object.freeze({
153
+ kind: 'nullCheck' as const,
154
+ expr: this.toExpr(),
155
+ isNull: true,
156
+ }) as NullCheckBuilder<ColumnName, ColumnMeta, JsType>;
157
+ }
158
+
159
+ /**
160
+ * Creates an IS NOT NULL check for this column.
161
+ * Available on all columns at runtime, but typed to only be visible on nullable columns.
162
+ */
163
+ isNotNull(): NullCheckBuilder<ColumnName, ColumnMeta, JsType> {
164
+ return Object.freeze({
165
+ kind: 'nullCheck' as const,
166
+ expr: this.toExpr(),
167
+ isNull: false,
168
+ }) as NullCheckBuilder<ColumnName, ColumnMeta, JsType>;
169
+ }
170
+ }
171
+
172
+ export class TableBuilderImpl<
173
+ Contract extends SqlContract<SqlStorage>,
174
+ TableName extends string,
175
+ Columns extends Record<string, StorageColumn>,
176
+ CodecTypes extends CodecTypesType,
177
+ Operations extends OperationTypes,
178
+ > implements TableRef
179
+ {
180
+ readonly kind = 'table' as const;
181
+ readonly columns: ColumnBuilders<Contract, TableName, Columns, CodecTypes, Operations>;
182
+ private readonly _name: TableName;
183
+
184
+ constructor(
185
+ name: TableName,
186
+ columns: ColumnBuilders<Contract, TableName, Columns, CodecTypes, Operations>,
187
+ ) {
188
+ this._name = name;
189
+ this.columns = columns;
190
+ }
191
+
192
+ get name(): string {
193
+ return this._name;
194
+ }
195
+ }
196
+
197
+ function buildColumns<
198
+ Contract extends SqlContract<SqlStorage>,
199
+ TableName extends keyof Contract['storage']['tables'] & string,
200
+ CodecTypes extends CodecTypesType,
201
+ Operations extends OperationTypes,
202
+ >(
203
+ tableName: TableName,
204
+ storage: SqlStorage,
205
+ _contract: Contract,
206
+ operationRegistry?: OperationRegistry,
207
+ contractCapabilities?: Record<string, Record<string, boolean>>,
208
+ ): ColumnBuilders<
209
+ Contract,
210
+ TableName,
211
+ Contract['storage']['tables'][TableName]['columns'],
212
+ CodecTypes,
213
+ Operations
214
+ > {
215
+ const table = storage.tables[tableName];
216
+
217
+ if (!table) {
218
+ throw planInvalid(`Unknown table ${tableName}`);
219
+ }
220
+
221
+ type Columns = Contract['storage']['tables'][TableName]['columns'];
222
+ const tableColumns = table.columns as Columns;
223
+
224
+ const result = {} as {
225
+ [K in keyof Columns]: ColumnBuilder<
226
+ K & string,
227
+ Columns[K],
228
+ ComputeColumnJsType<Contract, TableName, K & string, Columns[K], CodecTypes>,
229
+ Operations
230
+ >;
231
+ };
232
+
233
+ const assignColumn = <ColumnKey extends keyof Columns & string>(
234
+ columnName: ColumnKey,
235
+ columnDef: Columns[ColumnKey],
236
+ ) => {
237
+ type JsType = ComputeColumnJsType<
238
+ Contract,
239
+ TableName,
240
+ ColumnKey,
241
+ Columns[ColumnKey],
242
+ CodecTypes
243
+ >;
244
+
245
+ const columnBuilder = new ColumnBuilderImpl<ColumnKey, Columns[ColumnKey], JsType>(
246
+ tableName,
247
+ columnName,
248
+ columnDef,
249
+ );
250
+
251
+ const builderWithOps = attachOperationsToColumnBuilder<
252
+ ColumnKey,
253
+ Columns[ColumnKey],
254
+ JsType,
255
+ Operations
256
+ >(
257
+ columnBuilder as unknown as ColumnBuilder<
258
+ ColumnKey,
259
+ Columns[ColumnKey],
260
+ JsType,
261
+ Record<string, never>
262
+ >,
263
+ columnDef,
264
+ operationRegistry,
265
+ contractCapabilities,
266
+ );
267
+
268
+ (result as Record<string, unknown>)[columnName] = builderWithOps;
269
+ };
270
+
271
+ for (const columnName of Object.keys(tableColumns) as Array<keyof Columns & string>) {
272
+ const columnDef = tableColumns[columnName];
273
+ if (!columnDef) continue;
274
+ assignColumn(columnName, columnDef);
275
+ }
276
+
277
+ return result as ColumnBuilders<Contract, TableName, Columns, CodecTypes, Operations>;
278
+ }
279
+
280
+ /**
281
+ * Creates a Proxy that enables accessing table columns directly on the table object,
282
+ * in addition to the standard `table.columns.columnName` syntax.
283
+ *
284
+ * This allows both access patterns:
285
+ * - `tables.user.columns.id` (standard access)
286
+ * - `tables.user.id` (convenience access via proxy)
287
+ *
288
+ * The proxy intercepts property access and routes column name lookups to
289
+ * `table.columns[prop]`, while preserving direct access to table properties
290
+ * like `name`, `kind`, and `columns`.
291
+ */
292
+ function createTableProxy<
293
+ Contract extends SqlContract<SqlStorage>,
294
+ TableName extends string,
295
+ Columns extends Record<string, StorageColumn>,
296
+ CodecTypes extends CodecTypesType,
297
+ Operations extends OperationTypes,
298
+ >(
299
+ table: TableBuilderImpl<Contract, TableName, Columns, CodecTypes, Operations>,
300
+ ): TableBuilderImpl<Contract, TableName, Columns, CodecTypes, Operations> {
301
+ return new Proxy(table, {
302
+ get(target, prop) {
303
+ if (prop === 'name' || prop === 'kind' || prop === 'columns') {
304
+ return Reflect.get(target, prop);
305
+ }
306
+ if (typeof prop === 'string' && prop in target.columns) {
307
+ return target.columns[prop as keyof typeof target.columns];
308
+ }
309
+ return undefined;
310
+ },
311
+ });
312
+ }
313
+
314
+ type ExtractSchemaTables<
315
+ Contract extends SqlContract<SqlStorage>,
316
+ CodecTypes extends CodecTypesType,
317
+ Operations extends OperationTypes,
318
+ > = {
319
+ readonly [TableName in keyof Contract['storage']['tables']]: TableBuilderImpl<
320
+ Contract,
321
+ TableName & string,
322
+ TableColumns<Contract['storage']['tables'][TableName]>,
323
+ CodecTypes,
324
+ Operations
325
+ > &
326
+ TableRef;
327
+ };
328
+
329
+ /**
330
+ * Extracts the types registry shape from a contract.
331
+ * Each key is a type name from storage.types, and the value preserves the
332
+ * literal type from the contract (including codecId, nativeType, and typeParams).
333
+ * Returns an empty object type {} when storage.types is undefined.
334
+ */
335
+ type ExtractSchemaTypes<Contract extends SqlContract<SqlStorage>> =
336
+ Contract['storage']['types'] extends infer Types
337
+ ? Types extends Record<string, unknown>
338
+ ? { readonly [TypeName in keyof Types]: Types[TypeName] }
339
+ : Record<string, never>
340
+ : Record<string, never>;
341
+
342
+ export type SchemaHandle<
343
+ Contract extends SqlContract<SqlStorage> = SqlContract<SqlStorage>,
344
+ CodecTypes extends CodecTypesType = CodecTypesType,
345
+ Operations extends OperationTypes = Record<string, never>,
346
+ > = {
347
+ readonly tables: ExtractSchemaTables<Contract, CodecTypes, Operations>;
348
+ /**
349
+ * Initialized type helpers from storage.types.
350
+ * Each entry corresponds to a named type instance in the contract's storage.types.
351
+ */
352
+ readonly types: ExtractSchemaTypes<Contract>;
353
+ };
354
+
355
+ type SchemaReturnType<Contract extends SqlContract<SqlStorage>> = SchemaHandle<
356
+ Contract,
357
+ ExtractCodecTypes<Contract>,
358
+ ToOperationTypes<ExtractOperationTypes<Contract>>
359
+ >;
360
+
361
+ type NormalizeOperationTypes<T> = {
362
+ [TypeId in keyof T]: {
363
+ [Method in keyof T[TypeId]]: T[TypeId][Method] extends OperationTypeSignature
364
+ ? T[TypeId][Method]
365
+ : OperationTypeSignature;
366
+ };
367
+ };
368
+
369
+ type ToOperationTypes<T> = T extends OperationTypes ? T : NormalizeOperationTypes<T>;
370
+
371
+ /**
372
+ * Creates a schema handle for building SQL queries.
373
+ *
374
+ * @param context - Query lane context containing contract, codec and operation registries
375
+ * @returns A schema handle with typed table builders and type helpers
376
+ *
377
+ * @example
378
+ * ```typescript
379
+ * const schemaHandle = schema<Contract>(context);
380
+ * const userTable = schemaHandle.tables.user;
381
+ * const vectorType = schemaHandle.types.Vector1536;
382
+ * ```
383
+ */
384
+ export function schema<Contract extends SqlContract<SqlStorage>>(
385
+ context: ExecutionContext<Contract>,
386
+ ): SchemaReturnType<Contract> {
387
+ const contract = context.contract;
388
+ const storage = contract.storage;
389
+ type CodecTypes = ExtractCodecTypes<Contract>;
390
+ type Operations = ToOperationTypes<ExtractOperationTypes<Contract>>;
391
+ const tables = {} as ExtractSchemaTables<Contract, CodecTypes, Operations>;
392
+ const contractCapabilities = contract.capabilities;
393
+
394
+ const operationRegistry = context.operations;
395
+
396
+ for (const tableName of Object.keys(storage.tables) as Array<
397
+ keyof Contract['storage']['tables'] & string
398
+ >) {
399
+ const columns = buildColumns<Contract, typeof tableName, CodecTypes, Operations>(
400
+ tableName,
401
+ storage,
402
+ contract,
403
+ operationRegistry,
404
+ contractCapabilities,
405
+ );
406
+ const table = new TableBuilderImpl<
407
+ Contract,
408
+ typeof tableName & string,
409
+ Contract['storage']['tables'][typeof tableName]['columns'],
410
+ CodecTypes,
411
+ Operations
412
+ >(tableName, columns);
413
+ const proxiedTable = createTableProxy<
414
+ Contract,
415
+ typeof tableName & string,
416
+ Contract['storage']['tables'][typeof tableName]['columns'],
417
+ CodecTypes,
418
+ Operations
419
+ >(table);
420
+ (tables as Record<string, unknown>)[tableName] = Object.freeze(
421
+ proxiedTable,
422
+ ) as ExtractSchemaTables<Contract, CodecTypes, Operations>[typeof tableName];
423
+ }
424
+
425
+ // Get type helpers from context (populated by runtime context creation)
426
+ const types = context.types as ExtractSchemaTypes<Contract>;
427
+
428
+ return Object.freeze({ tables, types }) as SchemaReturnType<Contract>;
429
+ }
430
+
431
+ export type { ColumnBuilderImpl as Column, TableBuilderImpl as Table };