@prisma-next/family-sql 0.3.0-dev.33 → 0.3.0-dev.36

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