@prisma-next/family-sql 0.3.0-dev.12 → 0.3.0-dev.123

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 (101) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +34 -7
  3. package/dist/assembly-Dzumaba1.mjs +159 -0
  4. package/dist/assembly-Dzumaba1.mjs.map +1 -0
  5. package/dist/control-adapter.d.mts +60 -0
  6. package/dist/control-adapter.d.mts.map +1 -0
  7. package/dist/control-adapter.mjs +1 -0
  8. package/dist/control-instance-BKuHINR7.d.mts +411 -0
  9. package/dist/control-instance-BKuHINR7.d.mts.map +1 -0
  10. package/dist/control.d.mts +128 -0
  11. package/dist/control.d.mts.map +1 -0
  12. package/dist/control.mjs +683 -0
  13. package/dist/control.mjs.map +1 -0
  14. package/dist/runtime.d.mts +27 -0
  15. package/dist/runtime.d.mts.map +1 -0
  16. package/dist/runtime.mjs +38 -0
  17. package/dist/runtime.mjs.map +1 -0
  18. package/dist/schema-verify.d.mts +48 -0
  19. package/dist/schema-verify.d.mts.map +1 -0
  20. package/dist/schema-verify.mjs +4 -0
  21. package/dist/test-utils.d.mts +2 -0
  22. package/dist/test-utils.mjs +3 -0
  23. package/dist/verify-BfMETJcM.mjs +108 -0
  24. package/dist/verify-BfMETJcM.mjs.map +1 -0
  25. package/dist/verify-sql-schema-C3Pit9o4.mjs +1085 -0
  26. package/dist/verify-sql-schema-C3Pit9o4.mjs.map +1 -0
  27. package/dist/verify-sql-schema-DhHnkpPa.d.mts +67 -0
  28. package/dist/verify-sql-schema-DhHnkpPa.d.mts.map +1 -0
  29. package/dist/verify.d.mts +31 -0
  30. package/dist/verify.d.mts.map +1 -0
  31. package/dist/verify.mjs +3 -0
  32. package/package.json +35 -46
  33. package/src/core/assembly.ts +265 -59
  34. package/src/core/control-adapter.ts +15 -0
  35. package/src/core/{descriptor.ts → control-descriptor.ts} +15 -11
  36. package/src/core/{instance.ts → control-instance.ts} +106 -248
  37. package/src/core/migrations/contract-to-schema-ir.ts +265 -0
  38. package/src/core/migrations/types.ts +193 -168
  39. package/src/core/runtime-descriptor.ts +19 -41
  40. package/src/core/runtime-instance.ts +11 -133
  41. package/src/core/schema-verify/verify-helpers.ts +201 -105
  42. package/src/core/schema-verify/verify-sql-schema.ts +918 -413
  43. package/src/core/verify.ts +4 -13
  44. package/src/exports/control.ts +29 -6
  45. package/src/exports/runtime.ts +2 -6
  46. package/src/exports/schema-verify.ts +10 -2
  47. package/src/exports/test-utils.ts +1 -1
  48. package/dist/chunk-BHEGVBY7.js +0 -772
  49. package/dist/chunk-BHEGVBY7.js.map +0 -1
  50. package/dist/chunk-SQ2VWYDV.js +0 -589
  51. package/dist/chunk-SQ2VWYDV.js.map +0 -1
  52. package/dist/chunk-SU7LN2UH.js +0 -96
  53. package/dist/chunk-SU7LN2UH.js.map +0 -1
  54. package/dist/core/assembly.d.ts +0 -25
  55. package/dist/core/assembly.d.ts.map +0 -1
  56. package/dist/core/control-adapter.d.ts +0 -42
  57. package/dist/core/control-adapter.d.ts.map +0 -1
  58. package/dist/core/descriptor.d.ts +0 -24
  59. package/dist/core/descriptor.d.ts.map +0 -1
  60. package/dist/core/instance.d.ts +0 -140
  61. package/dist/core/instance.d.ts.map +0 -1
  62. package/dist/core/migrations/plan-helpers.d.ts +0 -20
  63. package/dist/core/migrations/plan-helpers.d.ts.map +0 -1
  64. package/dist/core/migrations/policies.d.ts +0 -6
  65. package/dist/core/migrations/policies.d.ts.map +0 -1
  66. package/dist/core/migrations/types.d.ts +0 -280
  67. package/dist/core/migrations/types.d.ts.map +0 -1
  68. package/dist/core/runtime-descriptor.d.ts +0 -19
  69. package/dist/core/runtime-descriptor.d.ts.map +0 -1
  70. package/dist/core/runtime-instance.d.ts +0 -54
  71. package/dist/core/runtime-instance.d.ts.map +0 -1
  72. package/dist/core/schema-verify/verify-helpers.d.ts +0 -50
  73. package/dist/core/schema-verify/verify-helpers.d.ts.map +0 -1
  74. package/dist/core/schema-verify/verify-sql-schema.d.ts +0 -45
  75. package/dist/core/schema-verify/verify-sql-schema.d.ts.map +0 -1
  76. package/dist/core/verify.d.ts +0 -39
  77. package/dist/core/verify.d.ts.map +0 -1
  78. package/dist/exports/control-adapter.d.ts +0 -2
  79. package/dist/exports/control-adapter.d.ts.map +0 -1
  80. package/dist/exports/control-adapter.js +0 -1
  81. package/dist/exports/control-adapter.js.map +0 -1
  82. package/dist/exports/control.d.ts +0 -13
  83. package/dist/exports/control.d.ts.map +0 -1
  84. package/dist/exports/control.js +0 -149
  85. package/dist/exports/control.js.map +0 -1
  86. package/dist/exports/runtime.d.ts +0 -8
  87. package/dist/exports/runtime.d.ts.map +0 -1
  88. package/dist/exports/runtime.js +0 -64
  89. package/dist/exports/runtime.js.map +0 -1
  90. package/dist/exports/schema-verify.d.ts +0 -11
  91. package/dist/exports/schema-verify.d.ts.map +0 -1
  92. package/dist/exports/schema-verify.js +0 -11
  93. package/dist/exports/schema-verify.js.map +0 -1
  94. package/dist/exports/test-utils.d.ts +0 -7
  95. package/dist/exports/test-utils.d.ts.map +0 -1
  96. package/dist/exports/test-utils.js +0 -17
  97. package/dist/exports/test-utils.js.map +0 -1
  98. package/dist/exports/verify.d.ts +0 -2
  99. package/dist/exports/verify.d.ts.map +0 -1
  100. package/dist/exports/verify.js +0 -11
  101. package/dist/exports/verify.js.map +0 -1
@@ -1,15 +1,14 @@
1
- import type { TargetBoundComponentDescriptor } from '@prisma-next/contract/framework-components';
1
+ import type {
2
+ TargetBoundComponentDescriptor,
3
+ TargetDescriptor,
4
+ } from '@prisma-next/contract/framework-components';
2
5
  import type { ContractIR } from '@prisma-next/contract/ir';
3
- import type { OperationManifest } from '@prisma-next/contract/pack-manifest-types';
4
6
  import type { ContractMarkerRecord, TypesImportSpec } from '@prisma-next/contract/types';
5
7
  import { emit } from '@prisma-next/core-control-plane/emission';
6
8
  import type { CoreSchemaView, SchemaTreeNode } from '@prisma-next/core-control-plane/schema-view';
7
9
  import type {
8
- ControlAdapterDescriptor,
9
10
  ControlDriverInstance,
10
- ControlExtensionDescriptor,
11
11
  ControlFamilyInstance,
12
- ControlTargetDescriptor,
13
12
  EmitContractResult,
14
13
  OperationContext,
15
14
  SignDatabaseResult,
@@ -18,14 +17,14 @@ import type {
18
17
  } from '@prisma-next/core-control-plane/types';
19
18
  import type { OperationRegistry } from '@prisma-next/operations';
20
19
  import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';
20
+ import { validateContract } from '@prisma-next/sql-contract/validate';
21
21
  import { sqlTargetFamilyHook } from '@prisma-next/sql-contract-emitter';
22
- import { validateContract } from '@prisma-next/sql-contract-ts/contract';
23
- import type { SqlOperationSignature } from '@prisma-next/sql-operations';
24
22
  import {
25
23
  ensureSchemaStatement,
26
24
  ensureTableStatement,
27
25
  writeContractMarker,
28
26
  } from '@prisma-next/sql-runtime';
27
+ import { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';
29
28
  import type { SqlSchemaIR, SqlTableIR } from '@prisma-next/sql-schema-ir/types';
30
29
  import { ifDefined } from '@prisma-next/utils/defined';
31
30
  import {
@@ -33,59 +32,19 @@ import {
33
32
  extractCodecTypeImports,
34
33
  extractExtensionIds,
35
34
  extractOperationTypeImports,
35
+ extractParameterizedRenderers,
36
+ extractParameterizedTypeImports,
37
+ extractQueryOperationTypeImports,
38
+ type SqlControlDescriptorWithContributions,
36
39
  } from './assembly';
37
40
  import type { SqlControlAdapter } from './control-adapter';
41
+ import type {
42
+ SqlControlAdapterDescriptor,
43
+ SqlControlExtensionDescriptor,
44
+ } from './migrations/types';
38
45
  import { verifySqlSchema } from './schema-verify/verify-sql-schema';
39
46
  import { collectSupportedCodecTypeIds, readMarker } from './verify';
40
47
 
41
- /**
42
- * Converts an OperationManifest (descriptor declarative data) to a SqlOperationSignature.
43
- * This is SQL-family-specific conversion logic used by instance creation and test utilities.
44
- */
45
- export function convertOperationManifest(manifest: OperationManifest): SqlOperationSignature {
46
- return {
47
- forTypeId: manifest.for,
48
- method: manifest.method,
49
- args: manifest.args.map((arg: OperationManifest['args'][number]) => {
50
- if (arg.kind === 'typeId') {
51
- if (!arg.type) {
52
- throw new Error('typeId arg must have type property');
53
- }
54
- return { kind: 'typeId' as const, type: arg.type };
55
- }
56
- if (arg.kind === 'param') {
57
- return { kind: 'param' as const };
58
- }
59
- if (arg.kind === 'literal') {
60
- return { kind: 'literal' as const };
61
- }
62
- throw new Error(`Invalid arg kind: ${(arg as { kind: unknown }).kind}`);
63
- }),
64
- returns: (() => {
65
- if (manifest.returns.kind === 'typeId') {
66
- return { kind: 'typeId' as const, type: manifest.returns.type };
67
- }
68
- if (manifest.returns.kind === 'builtin') {
69
- return {
70
- kind: 'builtin' as const,
71
- type: manifest.returns.type as 'number' | 'boolean' | 'string',
72
- };
73
- }
74
- throw new Error(`Invalid return kind: ${(manifest.returns as { kind: unknown }).kind}`);
75
- })(),
76
- lowering: {
77
- targetFamily: 'sql',
78
- strategy: manifest.lowering.strategy,
79
- template: manifest.lowering.template,
80
- },
81
- ...(manifest.capabilities ? { capabilities: manifest.capabilities } : {}),
82
- };
83
- }
84
-
85
- /**
86
- * Extracts codec type IDs used in contract storage tables.
87
- * Uses type guards to safely access SQL-specific structure without importing SQL types.
88
- */
89
48
  function extractCodecTypeIdsFromContract(contract: unknown): readonly string[] {
90
49
  const typeIds = new Set<string>();
91
50
 
@@ -127,14 +86,11 @@ function extractCodecTypeIdsFromContract(contract: unknown): readonly string[] {
127
86
  return Array.from(typeIds).sort();
128
87
  }
129
88
 
130
- /**
131
- * Creates a VerifyDatabaseResult object with common structure.
132
- */
133
89
  function createVerifyResult(options: {
134
90
  ok: boolean;
135
91
  code?: string;
136
92
  summary: string;
137
- contractCoreHash: string;
93
+ contractStorageHash: string;
138
94
  contractProfileHash?: string;
139
95
  marker?: ContractMarkerRecord;
140
96
  expectedTargetId: string;
@@ -145,8 +101,8 @@ function createVerifyResult(options: {
145
101
  contractPath: string;
146
102
  totalTime: number;
147
103
  }): VerifyDatabaseResult {
148
- const contract: { coreHash: string; profileHash?: string } = {
149
- coreHash: options.contractCoreHash,
104
+ const contract: { storageHash: string; profileHash?: string } = {
105
+ storageHash: options.contractStorageHash,
150
106
  };
151
107
  if (options.contractProfileHash) {
152
108
  contract.profileHash = options.contractProfileHash;
@@ -182,8 +138,8 @@ function createVerifyResult(options: {
182
138
  }
183
139
 
184
140
  if (options.marker) {
185
- (result as { marker?: { coreHash: string; profileHash: string } }).marker = {
186
- coreHash: options.marker.coreHash,
141
+ (result as { marker?: { storageHash: string; profileHash: string } }).marker = {
142
+ storageHash: options.marker.storageHash,
187
143
  profileHash: options.marker.profileHash,
188
144
  };
189
145
  }
@@ -200,10 +156,6 @@ function createVerifyResult(options: {
200
156
  return result;
201
157
  }
202
158
 
203
- /**
204
- * Type metadata for SQL storage types.
205
- * Maps contract storage type IDs to native database types.
206
- */
207
159
  interface SqlTypeMetadata {
208
160
  readonly typeId: string;
209
161
  readonly familyId: 'sql';
@@ -211,15 +163,8 @@ interface SqlTypeMetadata {
211
163
  readonly nativeType?: string;
212
164
  }
213
165
 
214
- /**
215
- * Registry mapping type IDs to their metadata.
216
- * Keyed by contract storage type ID (e.g., 'pg/int4@1').
217
- */
218
166
  type SqlTypeMetadataRegistry = Map<string, SqlTypeMetadata>;
219
167
 
220
- /**
221
- * State fields for SQL family instance that hold assembly data.
222
- */
223
168
  interface SqlFamilyInstanceState {
224
169
  readonly operationRegistry: OperationRegistry;
225
170
  readonly codecTypeImports: ReadonlyArray<TypesImportSpec>;
@@ -228,9 +173,6 @@ interface SqlFamilyInstanceState {
228
173
  readonly typeMetadataRegistry: SqlTypeMetadataRegistry;
229
174
  }
230
175
 
231
- /**
232
- * Options for schema verification.
233
- */
234
176
  export interface SchemaVerifyOptions {
235
177
  readonly driver: ControlDriverInstance<'sql', string>;
236
178
  readonly contractIR: unknown;
@@ -243,23 +185,11 @@ export interface SchemaVerifyOptions {
243
185
  readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
244
186
  }
245
187
 
246
- /**
247
- * SQL control family instance interface.
248
- * Extends ControlFamilyInstance with SQL-specific domain actions.
249
- */
250
188
  export interface SqlControlFamilyInstance
251
189
  extends ControlFamilyInstance<'sql'>,
252
190
  SqlFamilyInstanceState {
253
- /**
254
- * Validates a contract JSON and returns a validated ContractIR (without mappings).
255
- * Mappings are runtime-only and should not be part of ContractIR.
256
- */
257
191
  validateContractIR(contractJson: unknown): ContractIR;
258
192
 
259
- /**
260
- * Verifies the database marker against the contract.
261
- * Compares target, coreHash, and profileHash.
262
- */
263
193
  verify(options: {
264
194
  readonly driver: ControlDriverInstance<'sql', string>;
265
195
  readonly contractIR: unknown;
@@ -268,17 +198,8 @@ export interface SqlControlFamilyInstance
268
198
  readonly configPath?: string;
269
199
  }): Promise<VerifyDatabaseResult>;
270
200
 
271
- /**
272
- * Verifies the database schema against the contract.
273
- * Compares contract requirements against live database schema.
274
- */
275
201
  schemaVerify(options: SchemaVerifyOptions): Promise<VerifyDatabaseSchemaResult>;
276
202
 
277
- /**
278
- * Signs the database with the contract marker.
279
- * Writes or updates the contract marker if schema verification passes.
280
- * This operation is idempotent - if the marker already matches, no changes are made.
281
- */
282
203
  sign(options: {
283
204
  readonly driver: ControlDriverInstance<'sql', string>;
284
205
  readonly contractIR: unknown;
@@ -286,51 +207,25 @@ export interface SqlControlFamilyInstance
286
207
  readonly configPath?: string;
287
208
  }): Promise<SignDatabaseResult>;
288
209
 
289
- /**
290
- * Introspects the database schema and returns a family-specific schema IR.
291
- *
292
- * This is a read-only operation that returns a snapshot of the live database schema.
293
- * The method is family-owned and delegates to target/adapter-specific introspectors
294
- * to perform the actual schema introspection.
295
- *
296
- * @param options - Introspection options
297
- * @param options.driver - Control plane driver for database connection
298
- * @param options.contractIR - Optional contract IR for contract-guided introspection.
299
- * When provided, families may use it for filtering, optimization, or validation
300
- * during introspection. The contract IR does not change the meaning of "what exists"
301
- * in the database - it only guides how introspection is performed.
302
- * @returns Promise resolving to the family-specific Schema IR (e.g., `SqlSchemaIR` for SQL).
303
- * The IR represents the complete schema snapshot at the time of introspection.
304
- */
305
210
  introspect(options: {
306
211
  readonly driver: ControlDriverInstance<'sql', string>;
307
212
  readonly contractIR?: unknown;
308
213
  }): Promise<SqlSchemaIR>;
309
214
 
310
- /**
311
- * Projects a SQL Schema IR into a core schema view for CLI visualization.
312
- * Converts SqlSchemaIR (tables, columns, indexes, extensions) into a tree structure.
313
- */
314
215
  toSchemaView(schema: SqlSchemaIR): CoreSchemaView;
315
216
 
316
- /**
317
- * Emits contract JSON and DTS as strings.
318
- * Uses the instance's preassembled state (operation registry, type imports, extension IDs).
319
- * Handles stripping mappings and validation internally.
320
- */
321
217
  emitContract(options: { readonly contractIR: ContractIR | unknown }): Promise<EmitContractResult>;
322
218
  }
323
219
 
324
- /**
325
- * SQL family instance type.
326
- * Maintains backward compatibility with FamilyInstance while implementing SqlControlFamilyInstance.
327
- */
328
220
  export type SqlFamilyInstance = SqlControlFamilyInstance;
329
221
 
330
222
  interface CreateSqlFamilyInstanceOptions<TTargetId extends string> {
331
- readonly target: ControlTargetDescriptor<'sql', TTargetId>;
332
- readonly adapter: ControlAdapterDescriptor<'sql', TTargetId>;
333
- readonly extensionPacks: readonly ControlExtensionDescriptor<'sql', TTargetId>[];
223
+ readonly target: TargetDescriptor<'sql', TTargetId> &
224
+ SqlControlDescriptorWithContributions &
225
+ DescriptorWithStorageTypes;
226
+ readonly adapter: SqlControlAdapterDescriptor<TTargetId> & DescriptorWithStorageTypes;
227
+ readonly extensionPacks: readonly (SqlControlExtensionDescriptor<TTargetId> &
228
+ DescriptorWithStorageTypes)[];
334
229
  }
335
230
 
336
231
  function isSqlControlAdapter<TTargetId extends string>(
@@ -344,28 +239,32 @@ function isSqlControlAdapter<TTargetId extends string>(
344
239
  );
345
240
  }
346
241
 
347
- /**
348
- * Builds a SQL type metadata registry from extension pack manifests.
349
- * Collects type metadata from target, adapter, and extension pack manifests.
350
- *
351
- * @param options - Descriptors for target, adapter, and extensions
352
- * @returns Registry mapping type IDs to their metadata, filtered by targetId
353
- */
242
+ interface DescriptorWithStorageTypes {
243
+ readonly targetId?: string | undefined;
244
+ readonly types?:
245
+ | {
246
+ readonly storage?:
247
+ | ReadonlyArray<{
248
+ readonly typeId: string;
249
+ readonly familyId: string;
250
+ readonly targetId: string;
251
+ readonly nativeType?: string | undefined;
252
+ }>
253
+ | undefined;
254
+ }
255
+ | undefined;
256
+ }
257
+
354
258
  function buildSqlTypeMetadataRegistry(options: {
355
- readonly target: ControlTargetDescriptor<'sql', string>;
356
- readonly adapter: ControlAdapterDescriptor<'sql', string>;
357
- readonly extensionPacks: readonly ControlExtensionDescriptor<'sql', string>[];
259
+ readonly target: DescriptorWithStorageTypes;
260
+ readonly adapter: DescriptorWithStorageTypes & { readonly targetId: string };
261
+ readonly extensionPacks: readonly DescriptorWithStorageTypes[];
358
262
  }): SqlTypeMetadataRegistry {
359
263
  const { target, adapter, extensionPacks: extensions } = options;
360
264
  const registry = new Map<string, SqlTypeMetadata>();
361
-
362
- // Get targetId from adapter (they should match)
363
265
  const targetId = adapter.targetId;
364
-
365
- // Collect descriptors to iterate over
366
266
  const descriptors = [target, adapter, ...extensions];
367
267
 
368
- // Iterate over each descriptor's types
369
268
  for (const descriptor of descriptors) {
370
269
  const types = descriptor.types;
371
270
  const storageTypes = types?.storage;
@@ -374,11 +273,8 @@ function buildSqlTypeMetadataRegistry(options: {
374
273
  continue;
375
274
  }
376
275
 
377
- // Filter for SQL family and matching targetId
378
276
  for (const storageType of storageTypes) {
379
277
  if (storageType.familyId === 'sql' && storageType.targetId === targetId) {
380
- // Use existing entry if present, otherwise create new one
381
- // Later entries (extensions) can override earlier ones (adapter/target)
382
278
  registry.set(storageType.typeId, {
383
279
  typeId: storageType.typeId,
384
280
  familyId: 'sql',
@@ -392,36 +288,28 @@ function buildSqlTypeMetadataRegistry(options: {
392
288
  return registry;
393
289
  }
394
290
 
395
- /**
396
- * Creates a SQL family instance for control-plane operations.
397
- */
398
291
  export function createSqlFamilyInstance<TTargetId extends string>(
399
292
  options: CreateSqlFamilyInstanceOptions<TTargetId>,
400
293
  ): SqlFamilyInstance {
401
294
  const { target, adapter, extensionPacks: extensions = [] } = options;
402
295
 
403
- // Build descriptors array for assembly
404
- // Assembly functions only use manifest and id, so we can pass Control*Descriptor types directly
405
- const descriptors = [target, adapter, ...extensions];
296
+ const descriptors: SqlControlDescriptorWithContributions[] = [target, adapter, ...extensions];
406
297
 
407
- // Assemble operation registry, type imports, and extension IDs
408
- const operationRegistry = assembleOperationRegistry(descriptors, convertOperationManifest);
298
+ const operationRegistry = assembleOperationRegistry(descriptors);
409
299
  const codecTypeImports = extractCodecTypeImports(descriptors);
410
300
  const operationTypeImports = extractOperationTypeImports(descriptors);
301
+ const queryOperationTypeImports = extractQueryOperationTypeImports(descriptors);
411
302
  const extensionIds = extractExtensionIds(adapter, target, extensions);
303
+ const parameterizedRenderers = extractParameterizedRenderers(descriptors);
304
+ const parameterizedTypeImports = extractParameterizedTypeImports(descriptors);
412
305
 
413
- // Build type metadata registry from manifests
414
306
  const typeMetadataRegistry = buildSqlTypeMetadataRegistry({
415
307
  target,
416
308
  adapter,
417
309
  extensionPacks: extensions,
418
310
  });
419
311
 
420
- /**
421
- * Strips mappings from a contract (mappings are runtime-only).
422
- */
423
312
  function stripMappings(contract: unknown): unknown {
424
- // Type guard to check if contract has mappings
425
313
  if (typeof contract === 'object' && contract !== null && 'mappings' in contract) {
426
314
  const { mappings: _mappings, ...contractIR } = contract as {
427
315
  mappings?: unknown;
@@ -432,6 +320,13 @@ export function createSqlFamilyInstance<TTargetId extends string>(
432
320
  return contract;
433
321
  }
434
322
 
323
+ function normalizeProviderContractIR(contract: unknown): ContractIR {
324
+ const contractWithoutMappings = stripMappings(contract);
325
+ const validated = validateContract<SqlContract<SqlStorage>>(contractWithoutMappings);
326
+ const { mappings: _mappings, ...contractIR } = validated;
327
+ return contractIR as ContractIR;
328
+ }
329
+
435
330
  return {
436
331
  familyId: 'sql',
437
332
  operationRegistry,
@@ -441,12 +336,7 @@ export function createSqlFamilyInstance<TTargetId extends string>(
441
336
  typeMetadataRegistry,
442
337
 
443
338
  validateContractIR(contractJson: unknown): ContractIR {
444
- // Validate the contract (this normalizes and validates structure/logic)
445
- const validated = validateContract<SqlContract<SqlStorage>>(contractJson);
446
- // Strip mappings before returning ContractIR (mappings are runtime-only)
447
- // The validated contract has all required ContractIR properties
448
- const { mappings: _mappings, ...contractIR } = validated;
449
- return contractIR as ContractIR;
339
+ return normalizeProviderContractIR(contractJson);
450
340
  },
451
341
 
452
342
  async verify(verifyOptions: {
@@ -459,40 +349,30 @@ export function createSqlFamilyInstance<TTargetId extends string>(
459
349
  const { driver, contractIR, expectedTargetId, contractPath, configPath } = verifyOptions;
460
350
  const startTime = Date.now();
461
351
 
462
- // Type guard to ensure contract has required properties
463
352
  if (
464
353
  typeof contractIR !== 'object' ||
465
354
  contractIR === null ||
466
- !('coreHash' in contractIR) ||
355
+ !('storageHash' in contractIR) ||
467
356
  !('target' in contractIR) ||
468
- typeof contractIR.coreHash !== 'string' ||
357
+ typeof contractIR.storageHash !== 'string' ||
469
358
  typeof contractIR.target !== 'string'
470
359
  ) {
471
- throw new Error('Contract is missing required fields: coreHash or target');
360
+ throw new Error('Contract is missing required fields: storageHash or target');
472
361
  }
473
362
 
474
- // Extract contract hashes and target
475
- const contractCoreHash = contractIR.coreHash;
363
+ const contractStorageHash = contractIR.storageHash;
476
364
  const contractProfileHash =
477
365
  'profileHash' in contractIR && typeof contractIR.profileHash === 'string'
478
366
  ? contractIR.profileHash
479
367
  : undefined;
480
368
  const contractTarget = contractIR.target;
481
369
 
482
- // Read marker from database
483
370
  const marker = await readMarker(driver);
484
371
 
485
- // Compute codec coverage (optional)
486
372
  let missingCodecs: readonly string[] | undefined;
487
373
  let codecCoverageSkipped = false;
488
- const supportedTypeIds = collectSupportedCodecTypeIds<'sql', string>([
489
- adapter,
490
- target,
491
- ...extensions,
492
- ]);
374
+ const supportedTypeIds = collectSupportedCodecTypeIds([adapter, target, ...extensions]);
493
375
  if (supportedTypeIds.length === 0) {
494
- // Helper is present but returns empty (MVP behavior)
495
- // Coverage check is skipped - missingCodecs remains undefined
496
376
  codecCoverageSkipped = true;
497
377
  } else {
498
378
  const supportedSet = new Set(supportedTypeIds);
@@ -503,14 +383,13 @@ export function createSqlFamilyInstance<TTargetId extends string>(
503
383
  }
504
384
  }
505
385
 
506
- // Check marker presence
507
386
  if (!marker) {
508
387
  const totalTime = Date.now() - startTime;
509
388
  return createVerifyResult({
510
389
  ok: false,
511
- code: 'PN-RTM-3001',
390
+ code: 'PN-RUN-3001',
512
391
  summary: 'Marker missing',
513
- contractCoreHash,
392
+ contractStorageHash,
514
393
  expectedTargetId,
515
394
  contractPath,
516
395
  totalTime,
@@ -521,14 +400,13 @@ export function createSqlFamilyInstance<TTargetId extends string>(
521
400
  });
522
401
  }
523
402
 
524
- // Compare target
525
403
  if (contractTarget !== expectedTargetId) {
526
404
  const totalTime = Date.now() - startTime;
527
405
  return createVerifyResult({
528
406
  ok: false,
529
- code: 'PN-RTM-3003',
407
+ code: 'PN-RUN-3003',
530
408
  summary: 'Target mismatch',
531
- contractCoreHash,
409
+ contractStorageHash,
532
410
  marker,
533
411
  expectedTargetId,
534
412
  actualTargetId: contractTarget,
@@ -541,14 +419,13 @@ export function createSqlFamilyInstance<TTargetId extends string>(
541
419
  });
542
420
  }
543
421
 
544
- // Compare hashes
545
- if (marker.coreHash !== contractCoreHash) {
422
+ if (marker.storageHash !== contractStorageHash) {
546
423
  const totalTime = Date.now() - startTime;
547
424
  return createVerifyResult({
548
425
  ok: false,
549
- code: 'PN-RTM-3002',
426
+ code: 'PN-RUN-3002',
550
427
  summary: 'Hash mismatch',
551
- contractCoreHash,
428
+ contractStorageHash,
552
429
  marker,
553
430
  expectedTargetId,
554
431
  contractPath,
@@ -560,14 +437,13 @@ export function createSqlFamilyInstance<TTargetId extends string>(
560
437
  });
561
438
  }
562
439
 
563
- // Compare profile hash if present
564
440
  if (contractProfileHash && marker.profileHash !== contractProfileHash) {
565
441
  const totalTime = Date.now() - startTime;
566
442
  return createVerifyResult({
567
443
  ok: false,
568
- code: 'PN-RTM-3002',
444
+ code: 'PN-RUN-3002',
569
445
  summary: 'Hash mismatch',
570
- contractCoreHash,
446
+ contractStorageHash,
571
447
  contractProfileHash,
572
448
  marker,
573
449
  expectedTargetId,
@@ -579,12 +455,11 @@ export function createSqlFamilyInstance<TTargetId extends string>(
579
455
  });
580
456
  }
581
457
 
582
- // Success - all checks passed
583
458
  const totalTime = Date.now() - startTime;
584
459
  return createVerifyResult({
585
460
  ok: true,
586
461
  summary: 'Database matches contract',
587
- contractCoreHash,
462
+ contractStorageHash,
588
463
  marker,
589
464
  expectedTargetId,
590
465
  contractPath,
@@ -599,17 +474,14 @@ export function createSqlFamilyInstance<TTargetId extends string>(
599
474
  async schemaVerify(options: SchemaVerifyOptions): Promise<VerifyDatabaseSchemaResult> {
600
475
  const { driver, contractIR, strict, context, frameworkComponents } = options;
601
476
 
602
- // Validate contractIR as SqlContract<SqlStorage>
603
477
  const contract = validateContract<SqlContract<SqlStorage>>(contractIR);
604
478
 
605
- // Introspect live schema (DB I/O)
606
479
  const controlAdapter = adapter.create();
607
480
  if (!isSqlControlAdapter(controlAdapter)) {
608
481
  throw new Error('Adapter does not implement SqlControlAdapter.introspect()');
609
482
  }
610
483
  const schemaIR = await controlAdapter.introspect(driver, contractIR);
611
484
 
612
- // Pure verification (no I/O) - delegates to extracted pure function
613
485
  return verifySqlSchema({
614
486
  contract,
615
487
  schema: schemaIR,
@@ -617,6 +489,9 @@ export function createSqlFamilyInstance<TTargetId extends string>(
617
489
  ...ifDefined('context', context),
618
490
  typeMetadataRegistry,
619
491
  frameworkComponents,
492
+ // Wire up target-specific normalizers if available
493
+ ...ifDefined('normalizeDefault', controlAdapter.normalizeDefault),
494
+ ...ifDefined('normalizeNativeType', controlAdapter.normalizeNativeType),
620
495
  });
621
496
  },
622
497
  async sign(options: {
@@ -628,33 +503,27 @@ export function createSqlFamilyInstance<TTargetId extends string>(
628
503
  const { driver, contractIR, contractPath, configPath } = options;
629
504
  const startTime = Date.now();
630
505
 
631
- // Validate contractIR as SqlContract<SqlStorage>
632
506
  const contract = validateContract<SqlContract<SqlStorage>>(contractIR);
633
507
 
634
- // Extract contract hashes and target
635
- const contractCoreHash = contract.coreHash;
508
+ const contractStorageHash = contract.storageHash;
636
509
  const contractProfileHash =
637
510
  'profileHash' in contract && typeof contract.profileHash === 'string'
638
511
  ? contract.profileHash
639
- : contractCoreHash;
512
+ : contractStorageHash;
640
513
  const contractTarget = contract.target;
641
514
 
642
- // Ensure marker schema and table exist
643
515
  await driver.query(ensureSchemaStatement.sql, ensureSchemaStatement.params);
644
516
  await driver.query(ensureTableStatement.sql, ensureTableStatement.params);
645
517
 
646
- // Read existing marker
647
518
  const existingMarker = await readMarker(driver);
648
519
 
649
- // Determine if we need to write/update marker
650
520
  let markerCreated = false;
651
521
  let markerUpdated = false;
652
- let previousHashes: { coreHash?: string; profileHash?: string } | undefined;
522
+ let previousHashes: { storageHash?: string; profileHash?: string } | undefined;
653
523
 
654
524
  if (!existingMarker) {
655
- // No marker exists - insert new one
656
525
  const write = writeContractMarker({
657
- coreHash: contractCoreHash,
526
+ storageHash: contractStorageHash,
658
527
  profileHash: contractProfileHash,
659
528
  contractJson: contractIR,
660
529
  canonicalVersion: 1,
@@ -662,22 +531,19 @@ export function createSqlFamilyInstance<TTargetId extends string>(
662
531
  await driver.query(write.insert.sql, write.insert.params);
663
532
  markerCreated = true;
664
533
  } else {
665
- // Marker exists - check if hashes differ
666
- const existingCoreHash = existingMarker.coreHash;
534
+ const existingStorageHash = existingMarker.storageHash;
667
535
  const existingProfileHash = existingMarker.profileHash;
668
536
 
669
- // Compare hashes (use strict equality to ensure exact match)
670
- const coreHashMatches = existingCoreHash === contractCoreHash;
537
+ const storageHashMatches = existingStorageHash === contractStorageHash;
671
538
  const profileHashMatches = existingProfileHash === contractProfileHash;
672
539
 
673
- if (!coreHashMatches || !profileHashMatches) {
674
- // Hashes differ - update marker and capture previous hashes for output
540
+ if (!storageHashMatches || !profileHashMatches) {
675
541
  previousHashes = {
676
- coreHash: existingCoreHash,
542
+ storageHash: existingStorageHash,
677
543
  profileHash: existingProfileHash,
678
544
  };
679
545
  const write = writeContractMarker({
680
- coreHash: contractCoreHash,
546
+ storageHash: contractStorageHash,
681
547
  profileHash: contractProfileHash,
682
548
  contractJson: contractIR,
683
549
  canonicalVersion: existingMarker.canonicalVersion ?? 1,
@@ -685,15 +551,13 @@ export function createSqlFamilyInstance<TTargetId extends string>(
685
551
  await driver.query(write.update.sql, write.update.params);
686
552
  markerUpdated = true;
687
553
  }
688
- // If hashes match, no-op (idempotent) - previousHashes remains undefined
689
554
  }
690
555
 
691
- // Build summary message
692
556
  let summary: string;
693
557
  if (markerCreated) {
694
558
  summary = 'Database signed (marker created)';
695
559
  } else if (markerUpdated) {
696
- summary = `Database signed (marker updated from ${previousHashes?.coreHash ?? 'unknown'})`;
560
+ summary = `Database signed (marker updated from ${previousHashes?.storageHash ?? 'unknown'})`;
697
561
  } else {
698
562
  summary = 'Database already signed with this contract';
699
563
  }
@@ -704,7 +568,7 @@ export function createSqlFamilyInstance<TTargetId extends string>(
704
568
  ok: true,
705
569
  summary,
706
570
  contract: {
707
- coreHash: contractCoreHash,
571
+ storageHash: contractStorageHash,
708
572
  profileHash: contractProfileHash,
709
573
  },
710
574
  target: {
@@ -746,18 +610,15 @@ export function createSqlFamilyInstance<TTargetId extends string>(
746
610
  toSchemaView(schema: SqlSchemaIR): CoreSchemaView {
747
611
  const rootLabel = 'contract';
748
612
 
749
- // Build table nodes
750
613
  const tableNodes: readonly SchemaTreeNode[] = Object.entries(schema.tables).map(
751
614
  ([tableName, table]: [string, SqlTableIR]) => {
752
615
  const children: SchemaTreeNode[] = [];
753
616
 
754
- // Add column nodes grouped under "columns"
755
617
  const columnNodes: SchemaTreeNode[] = [];
756
618
  for (const [columnName, column] of Object.entries(table.columns)) {
757
- const nullableText = column.nullable ? '(nullable)' : '(not nullable)';
758
- // Always display nativeType for introspection (database state)
759
619
  const typeDisplay = column.nativeType;
760
- const label = `${columnName}: ${typeDisplay} ${nullableText}`;
620
+ const nullability = column.nullable ? 'nullable' : 'not nullable';
621
+ const label = `${columnName}: ${typeDisplay} (${nullability})`;
761
622
  columnNodes.push({
762
623
  kind: 'field',
763
624
  id: `column-${tableName}-${columnName}`,
@@ -765,11 +626,11 @@ export function createSqlFamilyInstance<TTargetId extends string>(
765
626
  meta: {
766
627
  nativeType: column.nativeType,
767
628
  nullable: column.nullable,
629
+ ...ifDefined('default', column.default),
768
630
  },
769
631
  });
770
632
  }
771
633
 
772
- // Add "columns" grouping node if there are columns
773
634
  if (columnNodes.length > 0) {
774
635
  children.push({
775
636
  kind: 'collection',
@@ -779,7 +640,6 @@ export function createSqlFamilyInstance<TTargetId extends string>(
779
640
  });
780
641
  }
781
642
 
782
- // Add primary key node if present
783
643
  if (table.primaryKey) {
784
644
  const pkColumns = table.primaryKey.columns.join(', ');
785
645
  children.push({
@@ -793,7 +653,6 @@ export function createSqlFamilyInstance<TTargetId extends string>(
793
653
  });
794
654
  }
795
655
 
796
- // Add unique constraint nodes
797
656
  for (const unique of table.uniques) {
798
657
  const name = unique.name ?? `${tableName}_${unique.columns.join('_')}_unique`;
799
658
  const label = `unique ${name}`;
@@ -808,9 +667,8 @@ export function createSqlFamilyInstance<TTargetId extends string>(
808
667
  });
809
668
  }
810
669
 
811
- // Add index nodes
812
670
  for (const index of table.indexes) {
813
- const name = index.name ?? `${tableName}_${index.columns.join('_')}_idx`;
671
+ const name = index.name ?? defaultIndexName(tableName, index.columns);
814
672
  const label = index.unique ? `unique index ${name}` : `index ${name}`;
815
673
  children.push({
816
674
  kind: 'index',
@@ -823,7 +681,6 @@ export function createSqlFamilyInstance<TTargetId extends string>(
823
681
  });
824
682
  }
825
683
 
826
- // Build table meta
827
684
  const tableMeta: Record<string, unknown> = {};
828
685
  if (table.primaryKey) {
829
686
  tableMeta['primaryKey'] = table.primaryKey.columns;
@@ -851,15 +708,16 @@ export function createSqlFamilyInstance<TTargetId extends string>(
851
708
  },
852
709
  );
853
710
 
854
- // Add extension nodes (format: "extensionName extension is enabled")
855
- const extensionNodes: readonly SchemaTreeNode[] = schema.extensions.map((extName) => ({
856
- kind: 'extension',
857
- id: `extension-${extName}`,
858
- label: `${extName} extension is enabled`,
859
- }));
711
+ const dependencyNodes: readonly SchemaTreeNode[] = schema.dependencies.map((dep) => {
712
+ const shortName = dep.id.split('.').pop() ?? dep.id;
713
+ return {
714
+ kind: 'dependency',
715
+ id: `dependency-${dep.id}`,
716
+ label: `${shortName} dependency is installed`,
717
+ };
718
+ });
860
719
 
861
- // Combine all children
862
- const rootChildren = [...tableNodes, ...extensionNodes];
720
+ const rootChildren = [...tableNodes, ...dependencyNodes];
863
721
 
864
722
  const rootNode: SchemaTreeNode = {
865
723
  kind: 'root',
@@ -874,20 +732,19 @@ export function createSqlFamilyInstance<TTargetId extends string>(
874
732
  },
875
733
 
876
734
  async emitContract({ contractIR }): Promise<EmitContractResult> {
877
- // Strip mappings if present (mappings are runtime-only)
878
- const contractWithoutMappings = stripMappings(contractIR);
879
-
880
- // Validate and normalize the contract
881
- const validatedIR = this.validateContractIR(contractWithoutMappings);
735
+ const normalizedIR = normalizeProviderContractIR(contractIR);
882
736
 
883
737
  const result = await emit(
884
- validatedIR,
738
+ normalizedIR,
885
739
  {
886
740
  outputDir: '',
887
741
  operationRegistry,
888
742
  codecTypeImports,
889
743
  operationTypeImports,
744
+ queryOperationTypeImports,
890
745
  extensionIds,
746
+ parameterizedRenderers,
747
+ parameterizedTypeImports,
891
748
  },
892
749
  sqlTargetFamilyHook,
893
750
  );
@@ -895,7 +752,8 @@ export function createSqlFamilyInstance<TTargetId extends string>(
895
752
  return {
896
753
  contractJson: result.contractJson,
897
754
  contractDts: result.contractDts,
898
- coreHash: result.coreHash,
755
+ storageHash: result.storageHash,
756
+ ...(result.executionHash ? { executionHash: result.executionHash } : {}),
899
757
  profileHash: result.profileHash,
900
758
  };
901
759
  },