@prisma-next/family-sql 0.3.0-dev.6 → 0.3.0-dev.63

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