@prisma-next/family-sql 0.3.0-dev.34 → 0.3.0-dev.37
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.
- package/README.md +11 -6
- package/dist/assembly-BVS641kd.mjs +106 -0
- package/dist/assembly-BVS641kd.mjs.map +1 -0
- package/dist/control-adapter.d.mts +60 -0
- package/dist/control-adapter.d.mts.map +1 -0
- package/dist/control-adapter.mjs +1 -0
- package/dist/control-instance-Cvmn5zpn.d.mts +292 -0
- package/dist/control-instance-Cvmn5zpn.d.mts.map +1 -0
- package/dist/control.d.mts +64 -0
- package/dist/control.d.mts.map +1 -0
- package/dist/control.mjs +534 -0
- package/dist/control.mjs.map +1 -0
- package/dist/runtime.d.mts +27 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +38 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/schema-verify.d.mts +48 -0
- package/dist/schema-verify.d.mts.map +1 -0
- package/dist/schema-verify.mjs +4 -0
- package/dist/test-utils.d.mts +2 -0
- package/dist/test-utils.mjs +3 -0
- package/dist/verify-BfMETJcM.mjs +108 -0
- package/dist/verify-BfMETJcM.mjs.map +1 -0
- package/dist/verify-sql-schema-B4T5MEuz.mjs +934 -0
- package/dist/verify-sql-schema-B4T5MEuz.mjs.map +1 -0
- package/dist/verify-sql-schema-CvQoGm2Q.d.mts +67 -0
- package/dist/verify-sql-schema-CvQoGm2Q.d.mts.map +1 -0
- package/dist/verify.d.mts +31 -0
- package/dist/verify.d.mts.map +1 -0
- package/dist/verify.mjs +3 -0
- package/package.json +33 -44
- package/src/core/assembly.ts +120 -96
- package/src/core/control-adapter.ts +15 -0
- package/src/core/{descriptor.ts → control-descriptor.ts} +15 -11
- package/src/core/{instance.ts → control-instance.ts} +72 -231
- package/src/core/migrations/types.ts +62 -163
- package/src/core/runtime-descriptor.ts +19 -41
- package/src/core/runtime-instance.ts +11 -133
- package/src/core/schema-verify/verify-sql-schema.ts +820 -397
- package/src/core/verify.ts +4 -13
- package/src/exports/control.ts +9 -6
- package/src/exports/runtime.ts +2 -6
- package/src/exports/schema-verify.ts +4 -1
- package/src/exports/test-utils.ts +0 -1
- package/dist/chunk-EHYNXF4K.js +0 -627
- package/dist/chunk-EHYNXF4K.js.map +0 -1
- package/dist/chunk-SU7LN2UH.js +0 -96
- package/dist/chunk-SU7LN2UH.js.map +0 -1
- package/dist/chunk-XH2Y5NTD.js +0 -715
- package/dist/chunk-XH2Y5NTD.js.map +0 -1
- package/dist/core/assembly.d.ts +0 -43
- package/dist/core/assembly.d.ts.map +0 -1
- package/dist/core/control-adapter.d.ts +0 -42
- package/dist/core/control-adapter.d.ts.map +0 -1
- package/dist/core/descriptor.d.ts +0 -28
- package/dist/core/descriptor.d.ts.map +0 -1
- package/dist/core/instance.d.ts +0 -140
- package/dist/core/instance.d.ts.map +0 -1
- package/dist/core/migrations/plan-helpers.d.ts +0 -20
- package/dist/core/migrations/plan-helpers.d.ts.map +0 -1
- package/dist/core/migrations/policies.d.ts +0 -6
- package/dist/core/migrations/policies.d.ts.map +0 -1
- package/dist/core/migrations/types.d.ts +0 -280
- package/dist/core/migrations/types.d.ts.map +0 -1
- package/dist/core/runtime-descriptor.d.ts +0 -19
- package/dist/core/runtime-descriptor.d.ts.map +0 -1
- package/dist/core/runtime-instance.d.ts +0 -54
- package/dist/core/runtime-instance.d.ts.map +0 -1
- package/dist/core/schema-verify/verify-helpers.d.ts +0 -96
- package/dist/core/schema-verify/verify-helpers.d.ts.map +0 -1
- package/dist/core/schema-verify/verify-sql-schema.d.ts +0 -45
- package/dist/core/schema-verify/verify-sql-schema.d.ts.map +0 -1
- package/dist/core/verify.d.ts +0 -39
- package/dist/core/verify.d.ts.map +0 -1
- package/dist/exports/control-adapter.d.ts +0 -2
- package/dist/exports/control-adapter.d.ts.map +0 -1
- package/dist/exports/control-adapter.js +0 -1
- package/dist/exports/control-adapter.js.map +0 -1
- package/dist/exports/control.d.ts +0 -13
- package/dist/exports/control.d.ts.map +0 -1
- package/dist/exports/control.js +0 -149
- package/dist/exports/control.js.map +0 -1
- package/dist/exports/runtime.d.ts +0 -8
- package/dist/exports/runtime.d.ts.map +0 -1
- package/dist/exports/runtime.js +0 -64
- package/dist/exports/runtime.js.map +0 -1
- package/dist/exports/schema-verify.d.ts +0 -11
- package/dist/exports/schema-verify.d.ts.map +0 -1
- package/dist/exports/schema-verify.js +0 -15
- package/dist/exports/schema-verify.js.map +0 -1
- package/dist/exports/test-utils.d.ts +0 -7
- package/dist/exports/test-utils.d.ts.map +0 -1
- package/dist/exports/test-utils.js +0 -17
- package/dist/exports/test-utils.js.map +0 -1
- package/dist/exports/verify.d.ts +0 -2
- package/dist/exports/verify.d.ts.map +0 -1
- package/dist/exports/verify.js +0 -11
- package/dist/exports/verify.js.map +0 -1
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import type {
|
|
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
|
-
|
|
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: {
|
|
151
|
-
|
|
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?: {
|
|
188
|
-
|
|
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:
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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:
|
|
358
|
-
readonly adapter:
|
|
359
|
-
readonly extensionPacks: readonly
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
!('
|
|
347
|
+
!('storageHash' in contractIR) ||
|
|
471
348
|
!('target' in contractIR) ||
|
|
472
|
-
typeof contractIR.
|
|
349
|
+
typeof contractIR.storageHash !== 'string' ||
|
|
473
350
|
typeof contractIR.target !== 'string'
|
|
474
351
|
) {
|
|
475
|
-
throw new Error('Contract is missing required fields:
|
|
352
|
+
throw new Error('Contract is missing required fields: storageHash or target');
|
|
476
353
|
}
|
|
477
354
|
|
|
478
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
:
|
|
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: {
|
|
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
|
-
|
|
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
|
-
|
|
670
|
-
const existingCoreHash = existingMarker.coreHash;
|
|
526
|
+
const existingStorageHash = existingMarker.storageHash;
|
|
671
527
|
const existingProfileHash = existingMarker.profileHash;
|
|
672
528
|
|
|
673
|
-
|
|
674
|
-
const coreHashMatches = existingCoreHash === contractCoreHash;
|
|
529
|
+
const storageHashMatches = existingStorageHash === contractStorageHash;
|
|
675
530
|
const profileHashMatches = existingProfileHash === contractProfileHash;
|
|
676
531
|
|
|
677
|
-
if (!
|
|
678
|
-
// Hashes differ - update marker and capture previous hashes for output
|
|
532
|
+
if (!storageHashMatches || !profileHashMatches) {
|
|
679
533
|
previousHashes = {
|
|
680
|
-
|
|
534
|
+
storageHash: existingStorageHash,
|
|
681
535
|
profileHash: existingProfileHash,
|
|
682
536
|
};
|
|
683
537
|
const write = writeContractMarker({
|
|
684
|
-
|
|
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?.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
744
|
+
storageHash: result.storageHash,
|
|
745
|
+
...(result.executionHash ? { executionHash: result.executionHash } : {}),
|
|
905
746
|
profileHash: result.profileHash,
|
|
906
747
|
};
|
|
907
748
|
},
|