@prisma-next/family-sql 0.14.0-dev.6 → 0.14.0-dev.61
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/dist/{authoring-type-constructors-CjFfO6LM.mjs → authoring-type-constructors-CXd-8ydc.mjs} +7 -30
- package/dist/authoring-type-constructors-CXd-8ydc.mjs.map +1 -0
- package/dist/{control-adapter-Cmw9LvEP.d.mts → control-adapter-BeN8TDZ5.d.mts} +29 -8
- package/dist/control-adapter-BeN8TDZ5.d.mts.map +1 -0
- package/dist/control-adapter.d.mts +1 -1
- package/dist/control.d.mts +238 -6
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +347 -942
- package/dist/control.mjs.map +1 -1
- package/dist/diff.d.mts +102 -0
- package/dist/diff.d.mts.map +1 -0
- package/dist/diff.mjs +14 -0
- package/dist/diff.mjs.map +1 -0
- package/dist/ir.d.mts +26 -9
- package/dist/ir.d.mts.map +1 -1
- package/dist/ir.mjs +2 -2
- package/dist/ir.mjs.map +1 -1
- package/dist/migration.d.mts +11 -2
- package/dist/migration.d.mts.map +1 -1
- package/dist/migration.mjs +7 -0
- package/dist/migration.mjs.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/psl-infer.d.mts +108 -0
- package/dist/psl-infer.d.mts.map +1 -0
- package/dist/psl-infer.mjs +353 -0
- package/dist/psl-infer.mjs.map +1 -0
- package/dist/schema-differ-DnoopSXm.d.mts +45 -0
- package/dist/schema-differ-DnoopSXm.d.mts.map +1 -0
- package/dist/schema-verify-W3r631Jh.mjs +226 -0
- package/dist/schema-verify-W3r631Jh.mjs.map +1 -0
- package/dist/{sql-contract-serializer-BR2vC7Z-.mjs → sql-contract-serializer-C75cfMSS.mjs} +46 -20
- package/dist/sql-contract-serializer-C75cfMSS.mjs.map +1 -0
- package/dist/{types-kgstZ_Zd.d.mts → types-COTsneBU.d.mts} +34 -139
- package/dist/types-COTsneBU.d.mts.map +1 -0
- package/package.json +23 -23
- package/src/core/authoring-entity-types.ts +12 -37
- package/src/core/control-adapter.ts +10 -5
- package/src/core/control-instance.ts +210 -42
- package/src/core/control-target-descriptor.ts +98 -0
- package/src/core/diff/schema-verify.ts +324 -0
- package/src/core/diff/sql-schema-diff.ts +41 -0
- package/src/core/diff/verifier-disposition.ts +30 -0
- package/src/core/ir/sql-contract-serializer-base.ts +70 -56
- package/src/core/ir/sql-contract-serializer.ts +5 -7
- package/src/core/ir/sql-schema-verifier-base.ts +5 -5
- package/src/core/migrations/contract-to-schema-ir.ts +126 -32
- package/src/core/migrations/field-event-planner.ts +2 -2
- package/src/core/migrations/native-type-expander.ts +28 -0
- package/src/core/migrations/schema-differ.ts +41 -0
- package/src/core/migrations/types.ts +31 -34
- package/src/core/psl-contract-infer/name-transforms.ts +15 -0
- package/src/core/psl-contract-infer/printer-config.ts +12 -6
- package/src/core/psl-contract-infer/relation-inference.ts +9 -1
- package/src/core/sql-migration.ts +12 -1
- package/src/exports/control.ts +11 -1
- package/src/exports/diff.ts +25 -0
- package/src/exports/psl-infer.ts +40 -0
- package/dist/authoring-type-constructors-CjFfO6LM.mjs.map +0 -1
- package/dist/control-adapter-Cmw9LvEP.d.mts.map +0 -1
- package/dist/schema-verify.d.mts +0 -39
- package/dist/schema-verify.d.mts.map +0 -1
- package/dist/schema-verify.mjs +0 -2
- package/dist/sql-contract-serializer-BR2vC7Z-.mjs.map +0 -1
- package/dist/test-utils.d.mts +0 -2
- package/dist/test-utils.mjs +0 -2
- package/dist/types-kgstZ_Zd.d.mts.map +0 -1
- package/dist/verify-sql-schema-thU-jKpf.d.mts +0 -66
- package/dist/verify-sql-schema-thU-jKpf.d.mts.map +0 -1
- package/dist/verify-sql-schema-xT4udQLQ.mjs +0 -1501
- package/dist/verify-sql-schema-xT4udQLQ.mjs.map +0 -1
- package/src/core/psl-contract-infer/postgres-default-mapping.ts +0 -16
- package/src/core/psl-contract-infer/postgres-type-map.ts +0 -157
- package/src/core/psl-contract-infer/sql-schema-ir-to-psl-ast.ts +0 -795
- package/src/core/schema-verify/control-verify-emit.ts +0 -46
- package/src/core/schema-verify/verifier-disposition.ts +0 -58
- package/src/core/schema-verify/verify-helpers.ts +0 -820
- package/src/core/schema-verify/verify-sql-schema.ts +0 -1311
- package/src/exports/schema-verify.ts +0 -18
- package/src/exports/test-utils.ts +0 -9
|
@@ -11,10 +11,12 @@ import type {
|
|
|
11
11
|
ControlFamilyInstance,
|
|
12
12
|
ControlStack,
|
|
13
13
|
CoreSchemaView,
|
|
14
|
+
DiffSubjectGranularity,
|
|
14
15
|
MigrationPlanOperation,
|
|
15
16
|
OperationPreview,
|
|
16
17
|
OperationPreviewCapable,
|
|
17
18
|
PslContractInferCapable,
|
|
19
|
+
SchemaDiffIssue,
|
|
18
20
|
SchemaViewCapable,
|
|
19
21
|
SignDatabaseResult,
|
|
20
22
|
VerifyDatabaseResult,
|
|
@@ -28,6 +30,7 @@ import {
|
|
|
28
30
|
VERIFY_CODE_TARGET_MISMATCH,
|
|
29
31
|
} from '@prisma-next/framework-components/control';
|
|
30
32
|
import type { TypesImportSpec } from '@prisma-next/framework-components/emission';
|
|
33
|
+
import { isPlainRecord } from '@prisma-next/framework-components/ir';
|
|
31
34
|
import type { PslDocumentAst } from '@prisma-next/framework-components/psl-ast';
|
|
32
35
|
import { assertDescriptorSelfConsistency } from '@prisma-next/migration-tools/spaces';
|
|
33
36
|
import { sqlContractCanonicalizationHooks } from '@prisma-next/sql-contract/canonicalization-hooks';
|
|
@@ -39,17 +42,26 @@ import type {
|
|
|
39
42
|
SqlExecuteRequest,
|
|
40
43
|
} from '@prisma-next/sql-relational-core/ast';
|
|
41
44
|
import { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';
|
|
42
|
-
import type {
|
|
45
|
+
import type { SqlSchemaIRNode, SqlTableIR } from '@prisma-next/sql-schema-ir/types';
|
|
46
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
43
47
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
44
48
|
import type { SqlControlAdapter } from './control-adapter';
|
|
49
|
+
import type {
|
|
50
|
+
SqlControlTargetDescriptor,
|
|
51
|
+
SqlDescribedContractSpace,
|
|
52
|
+
} from './control-target-descriptor';
|
|
53
|
+
import {
|
|
54
|
+
classifyDiffEntityKind,
|
|
55
|
+
classifyDiffSubjectGranularity,
|
|
56
|
+
verifySqlSchemaByDiff,
|
|
57
|
+
} from './diff/schema-verify';
|
|
45
58
|
import { SqlContractSerializer } from './ir/sql-contract-serializer';
|
|
59
|
+
import type { SqlSchemaDiffFn } from './migrations/schema-differ';
|
|
46
60
|
import type {
|
|
47
61
|
SqlControlAdapterDescriptor,
|
|
48
62
|
SqlControlExtensionDescriptor,
|
|
49
63
|
} from './migrations/types';
|
|
50
64
|
import { sqlOperationsToPreview } from './operation-preview';
|
|
51
|
-
import { sqlSchemaIrToPslAst } from './psl-contract-infer/sql-schema-ir-to-psl-ast';
|
|
52
|
-
import { verifySqlSchema } from './schema-verify/verify-sql-schema';
|
|
53
65
|
import { collectSupportedCodecTypeIds } from './verify';
|
|
54
66
|
|
|
55
67
|
function extractCodecTypeIdsFromContract(contract: unknown): readonly string[] {
|
|
@@ -186,9 +198,9 @@ interface SqlFamilyInstanceState {
|
|
|
186
198
|
}
|
|
187
199
|
|
|
188
200
|
export interface SqlControlFamilyInstance
|
|
189
|
-
extends ControlFamilyInstance<'sql',
|
|
190
|
-
SchemaViewCapable<
|
|
191
|
-
PslContractInferCapable<
|
|
201
|
+
extends ControlFamilyInstance<'sql', SqlSchemaIRNode>,
|
|
202
|
+
SchemaViewCapable<SqlSchemaIRNode>,
|
|
203
|
+
PslContractInferCapable<SqlSchemaIRNode>,
|
|
192
204
|
OperationPreviewCapable,
|
|
193
205
|
SqlFamilyInstanceState {
|
|
194
206
|
/**
|
|
@@ -210,22 +222,41 @@ export interface SqlControlFamilyInstance
|
|
|
210
222
|
}): Promise<VerifyDatabaseResult>;
|
|
211
223
|
|
|
212
224
|
/**
|
|
213
|
-
* Verify a contract against an already-introspected schema
|
|
225
|
+
* Verify a contract against an already-introspected schema.
|
|
214
226
|
*
|
|
215
227
|
* Callers that need to verify against the live database compose
|
|
216
228
|
* `introspect({ driver })` + `verifySchema({ contract, schema, ... })`.
|
|
217
|
-
* The aggregate verifier
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* as `extras`.
|
|
229
|
+
* The aggregate verifier hands in the full introspected schema and scopes
|
|
230
|
+
* the returned result to each member's contract space afterwards — so
|
|
231
|
+
* sibling-space tables never survive as `extras`.
|
|
221
232
|
*/
|
|
222
233
|
verifySchema(options: {
|
|
223
234
|
readonly contract: unknown;
|
|
224
|
-
readonly schema:
|
|
235
|
+
readonly schema: SqlSchemaIRNode;
|
|
225
236
|
readonly strict: boolean;
|
|
226
237
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
227
238
|
}): VerifyDatabaseSchemaResult;
|
|
228
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Classifies a diff issue's subject granularity on demand, resolved from
|
|
242
|
+
* its node's `nodeKind` via the target's classifier. Satisfies the
|
|
243
|
+
* {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable}
|
|
244
|
+
* capability that framework consumers spanning contract spaces (the
|
|
245
|
+
* migration aggregate's unclaimed-elements sweep) detect via
|
|
246
|
+
* `hasSchemaSubjectClassifier` and call instead of reading family/target
|
|
247
|
+
* node vocabulary. Nothing is stamped on the issue or the node.
|
|
248
|
+
*/
|
|
249
|
+
classifySubjectGranularity(issue: SchemaDiffIssue): DiffSubjectGranularity | undefined;
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Classifies a diff issue's subject storage `entityKind` on demand,
|
|
253
|
+
* resolved from its node's `nodeKind` via the target's classifier —
|
|
254
|
+
* sibling of `classifySubjectGranularity` above, part of the same
|
|
255
|
+
* {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable}
|
|
256
|
+
* capability. Nothing is stamped on the issue or the node.
|
|
257
|
+
*/
|
|
258
|
+
classifyEntityKind(issue: SchemaDiffIssue): string | undefined;
|
|
259
|
+
|
|
229
260
|
sign(options: {
|
|
230
261
|
readonly driver: SqlControlDriverInstance<string>;
|
|
231
262
|
readonly contract: unknown;
|
|
@@ -236,9 +267,9 @@ export interface SqlControlFamilyInstance
|
|
|
236
267
|
introspect(options: {
|
|
237
268
|
readonly driver: SqlControlDriverInstance<string>;
|
|
238
269
|
readonly contract?: unknown;
|
|
239
|
-
}): Promise<
|
|
270
|
+
}): Promise<SqlSchemaIRNode>;
|
|
240
271
|
|
|
241
|
-
inferPslContract(schemaIR:
|
|
272
|
+
inferPslContract(schemaIR: SqlSchemaIRNode): PslDocumentAst;
|
|
242
273
|
|
|
243
274
|
lowerAst(
|
|
244
275
|
ast: AnyQueryAst | DdlNode,
|
|
@@ -294,8 +325,6 @@ export interface SqlControlFamilyInstance
|
|
|
294
325
|
|
|
295
326
|
bootstrapControlTableQueries(): readonly DdlNode[];
|
|
296
327
|
|
|
297
|
-
bootstrapSignMarkerQueries(): readonly DdlNode[];
|
|
298
|
-
|
|
299
328
|
toOperationPreview(operations: readonly MigrationPlanOperation[]): OperationPreview;
|
|
300
329
|
}
|
|
301
330
|
|
|
@@ -523,12 +552,66 @@ export function createSqlFamilyInstance<TTargetId extends string>(
|
|
|
523
552
|
|
|
524
553
|
const targetSerializer = (
|
|
525
554
|
target as unknown as {
|
|
526
|
-
contractSerializer?: {
|
|
555
|
+
contractSerializer?: {
|
|
556
|
+
deserializeContract(json: unknown): Contract<SqlStorage>;
|
|
557
|
+
serializeContract(contract: Contract<SqlStorage>): unknown;
|
|
558
|
+
};
|
|
527
559
|
}
|
|
528
560
|
).contractSerializer;
|
|
529
|
-
|
|
561
|
+
// Database→PSL inference is target logic (it owns the dialect type/default
|
|
562
|
+
// maps and walks its own schema tree), so it is read off the descriptor like
|
|
563
|
+
// `contractSerializer`. Absent for targets without `contract infer` (Mongo).
|
|
564
|
+
const targetInferPslContract = blindCast<
|
|
565
|
+
SqlControlTargetDescriptor<TTargetId, unknown>,
|
|
566
|
+
'reading the optional target-descriptor inferPslContract hook'
|
|
567
|
+
>(target).inferPslContract;
|
|
568
|
+
// The full-tree node diff the verify VERDICT derives from. Read lazily so
|
|
569
|
+
// construction-only stub descriptors (schema-view tests) keep working; the
|
|
570
|
+
// throw happens at verify time.
|
|
571
|
+
const diffSchema = blindCast<
|
|
572
|
+
{ readonly diffSchema?: SqlSchemaDiffFn },
|
|
573
|
+
'reading the target-descriptor diffSchema hook'
|
|
574
|
+
>(target).diffSchema;
|
|
575
|
+
// The target's nodeKind → granularity classifier, resolved on demand by the
|
|
576
|
+
// verdict and by the instance's `classifySubjectGranularity` method below —
|
|
577
|
+
// never stamped onto an issue. Read lazily for the same
|
|
578
|
+
// construction-only-stub reason as `diffSchema`; the throw happens at call
|
|
579
|
+
// time.
|
|
580
|
+
const targetGranularityOf = blindCast<
|
|
581
|
+
{ readonly classifySubjectGranularity?: (nodeKind: string) => DiffSubjectGranularity },
|
|
582
|
+
'reading the target-descriptor classifySubjectGranularity hook'
|
|
583
|
+
>(target).classifySubjectGranularity;
|
|
584
|
+
// The target's nodeKind → storage entityKind classifier — sibling of
|
|
585
|
+
// `targetGranularityOf` above, read the same lazy way for the same
|
|
586
|
+
// construction-only-stub reason.
|
|
587
|
+
const targetEntityKindOf = blindCast<
|
|
588
|
+
{ readonly classifyEntityKind?: (nodeKind: string) => string | undefined },
|
|
589
|
+
'reading the target-descriptor classifyEntityKind hook'
|
|
590
|
+
>(target).classifyEntityKind;
|
|
591
|
+
// `contract infer` needs each extension pack's already-assembled contract,
|
|
592
|
+
// carried as-is (no merging — that is the contract-spaces machinery's
|
|
593
|
+
// concern), paired with the `spaceId` its descriptor was registered under
|
|
594
|
+
// (neither the contract JSON nor `ContractSpace` self-declares it), so the
|
|
595
|
+
// target hook can omit elements those contracts describe and qualify a
|
|
596
|
+
// cross-space relation with the owning pack's space id.
|
|
597
|
+
const describedContracts: readonly SqlDescribedContractSpace[] = extensions.flatMap(
|
|
598
|
+
(extension) =>
|
|
599
|
+
extension.contractSpace
|
|
600
|
+
? [{ spaceId: extension.id, contract: extension.contractSpace.contractJson }]
|
|
601
|
+
: [],
|
|
602
|
+
);
|
|
603
|
+
const deserializeWithTargetSerializer = (contractOrJson: unknown): Contract<SqlStorage> => {
|
|
530
604
|
const serializer = targetSerializer ?? new SqlContractSerializer();
|
|
531
|
-
|
|
605
|
+
const json =
|
|
606
|
+
targetSerializer !== undefined && !isPlainRecord(contractOrJson)
|
|
607
|
+
? targetSerializer.serializeContract(
|
|
608
|
+
blindCast<
|
|
609
|
+
Contract<SqlStorage>,
|
|
610
|
+
'isPlainRecord returned false, so contractOrJson is a class instance, not raw JSON'
|
|
611
|
+
>(contractOrJson),
|
|
612
|
+
)
|
|
613
|
+
: contractOrJson;
|
|
614
|
+
return serializer.deserializeContract(json) as Contract<SqlStorage>;
|
|
532
615
|
};
|
|
533
616
|
|
|
534
617
|
return {
|
|
@@ -669,22 +752,67 @@ export function createSqlFamilyInstance<TTargetId extends string>(
|
|
|
669
752
|
|
|
670
753
|
verifySchema(options: {
|
|
671
754
|
readonly contract: unknown;
|
|
672
|
-
readonly schema:
|
|
755
|
+
readonly schema: SqlSchemaIRNode;
|
|
673
756
|
readonly strict: boolean;
|
|
674
757
|
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
|
|
675
758
|
}): VerifyDatabaseSchemaResult {
|
|
676
759
|
const contract = deserializeWithTargetSerializer(options.contract) as Contract<SqlStorage>;
|
|
677
|
-
|
|
678
|
-
|
|
760
|
+
if (!diffSchema) {
|
|
761
|
+
throw new Error(
|
|
762
|
+
`SQL target "${target.targetId}" is missing the required diffSchema descriptor operation`,
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
if (!targetGranularityOf) {
|
|
766
|
+
throw new Error(
|
|
767
|
+
`SQL target "${target.targetId}" is missing the required classifySubjectGranularity descriptor operation`,
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
// THE VERDICT: the target's full-tree node diff, graded by the
|
|
771
|
+
// family's post-diff filters (strict gating + control-policy
|
|
772
|
+
// disposition), plus the codec verifyType hook findings. The result
|
|
773
|
+
// is issue-based — `ok` holds exactly when both issue lists are empty.
|
|
774
|
+
return verifySqlSchemaByDiff({
|
|
679
775
|
contract,
|
|
680
776
|
schema: options.schema,
|
|
681
777
|
strict: options.strict,
|
|
682
|
-
typeMetadataRegistry,
|
|
683
778
|
frameworkComponents: options.frameworkComponents,
|
|
684
|
-
|
|
685
|
-
|
|
779
|
+
diffSchema,
|
|
780
|
+
granularityOf: targetGranularityOf,
|
|
686
781
|
});
|
|
687
782
|
},
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Classifies a diff issue's subject granularity on demand, by resolving
|
|
786
|
+
* its node's `nodeKind` through the target's classifier — the
|
|
787
|
+
* {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable}
|
|
788
|
+
* capability. Framework consumers spanning contract spaces (the
|
|
789
|
+
* migration aggregate's unclaimed-elements sweep) detect and call this
|
|
790
|
+
* instead of reaching into the concrete schema-IR node, which they
|
|
791
|
+
* cannot read; nothing is stamped on the issue or the node.
|
|
792
|
+
*/
|
|
793
|
+
classifySubjectGranularity(issue: SchemaDiffIssue): DiffSubjectGranularity | undefined {
|
|
794
|
+
if (!targetGranularityOf) {
|
|
795
|
+
throw new Error(
|
|
796
|
+
`SQL target "${target.targetId}" is missing the required classifySubjectGranularity descriptor operation`,
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
return classifyDiffSubjectGranularity(issue, targetGranularityOf);
|
|
800
|
+
},
|
|
801
|
+
/**
|
|
802
|
+
* Classifies a diff issue's subject storage `entityKind` on demand, by
|
|
803
|
+
* resolving its node's `nodeKind` through the target's classifier —
|
|
804
|
+
* the sibling of `classifySubjectGranularity` above, and part of the
|
|
805
|
+
* same {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable}
|
|
806
|
+
* capability.
|
|
807
|
+
*/
|
|
808
|
+
classifyEntityKind(issue: SchemaDiffIssue): string | undefined {
|
|
809
|
+
if (!targetEntityKindOf) {
|
|
810
|
+
throw new Error(
|
|
811
|
+
`SQL target "${target.targetId}" is missing the required classifyEntityKind descriptor operation`,
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
return classifyDiffEntityKind(issue, targetEntityKindOf);
|
|
815
|
+
},
|
|
688
816
|
async sign(options: {
|
|
689
817
|
readonly driver: SqlControlDriverInstance<string>;
|
|
690
818
|
readonly contract: unknown;
|
|
@@ -848,12 +976,17 @@ export function createSqlFamilyInstance<TTargetId extends string>(
|
|
|
848
976
|
async introspect(options: {
|
|
849
977
|
readonly driver: SqlControlDriverInstance<string>;
|
|
850
978
|
readonly contract?: unknown;
|
|
851
|
-
}): Promise<
|
|
979
|
+
}): Promise<SqlSchemaIRNode> {
|
|
852
980
|
return getControlAdapter().introspect(options.driver, options.contract);
|
|
853
981
|
},
|
|
854
982
|
|
|
855
|
-
inferPslContract(schemaIR:
|
|
856
|
-
|
|
983
|
+
inferPslContract(schemaIR: SqlSchemaIRNode): PslDocumentAst {
|
|
984
|
+
if (!targetInferPslContract) {
|
|
985
|
+
throw new Error(
|
|
986
|
+
`Target "${target.targetId}" does not support contract infer (no inferPslContract on its descriptor).`,
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
return targetInferPslContract(schemaIR, describedContracts);
|
|
857
990
|
},
|
|
858
991
|
|
|
859
992
|
lowerAst(
|
|
@@ -867,17 +1000,52 @@ export function createSqlFamilyInstance<TTargetId extends string>(
|
|
|
867
1000
|
return getControlAdapter().bootstrapControlTableQueries();
|
|
868
1001
|
},
|
|
869
1002
|
|
|
870
|
-
bootstrapSignMarkerQueries(): readonly DdlNode[] {
|
|
871
|
-
return getControlAdapter().bootstrapSignMarkerQueries();
|
|
872
|
-
},
|
|
873
|
-
|
|
874
1003
|
toOperationPreview(operations: readonly MigrationPlanOperation[]): OperationPreview {
|
|
875
1004
|
return sqlOperationsToPreview(operations);
|
|
876
1005
|
},
|
|
877
1006
|
|
|
878
|
-
toSchemaView(schema:
|
|
879
|
-
|
|
880
|
-
|
|
1007
|
+
toSchemaView(schema: SqlSchemaIRNode): CoreSchemaView {
|
|
1008
|
+
// Walk the schema-IR tree's own structure (root → namespaces → tables)
|
|
1009
|
+
// into one flat list of table nodes. A root that exposes a `namespaces`
|
|
1010
|
+
// record (Postgres) contributes each namespace's tables; a flat root
|
|
1011
|
+
// (SQLite) is its own single namespace. The single-schema common case
|
|
1012
|
+
// renders the same table-level view as today — no synthetic namespace
|
|
1013
|
+
// level.
|
|
1014
|
+
const root = blindCast<
|
|
1015
|
+
{
|
|
1016
|
+
readonly namespaces?: Readonly<
|
|
1017
|
+
Record<string, { readonly tables: Record<string, SqlTableIR> }>
|
|
1018
|
+
>;
|
|
1019
|
+
readonly tables?: Record<string, SqlTableIR>;
|
|
1020
|
+
},
|
|
1021
|
+
'structural read of the schema-IR tree own namespaces/tables records'
|
|
1022
|
+
>(schema);
|
|
1023
|
+
// A multi-namespace root qualifies every table's display name with its
|
|
1024
|
+
// namespace key, so same-named tables in different namespaces (e.g.
|
|
1025
|
+
// public.thing and auth.thing) render distinct ids and labels. The
|
|
1026
|
+
// single-namespace case (and a flat SQLite root) keeps today's bare
|
|
1027
|
+
// names. Synthesized constraint/index fallback names keep the bare table
|
|
1028
|
+
// name — that is the name the database derives them from.
|
|
1029
|
+
const namespaceEntries: ReadonlyArray<[string | undefined, Record<string, SqlTableIR>]> =
|
|
1030
|
+
root.namespaces !== undefined
|
|
1031
|
+
? Object.entries(root.namespaces).map(
|
|
1032
|
+
([namespaceKey, namespace]): [string, Record<string, SqlTableIR>] => [
|
|
1033
|
+
namespaceKey,
|
|
1034
|
+
namespace.tables,
|
|
1035
|
+
],
|
|
1036
|
+
)
|
|
1037
|
+
: [[undefined, root.tables ?? {}]];
|
|
1038
|
+
const qualify = namespaceEntries.length > 1;
|
|
1039
|
+
const tableEntries: ReadonlyArray<[string, string, SqlTableIR]> = namespaceEntries.flatMap(
|
|
1040
|
+
([namespaceKey, tables]) =>
|
|
1041
|
+
Object.entries(tables).map(([tableName, table]): [string, string, SqlTableIR] => [
|
|
1042
|
+
qualify && namespaceKey !== undefined ? `${namespaceKey}.${tableName}` : tableName,
|
|
1043
|
+
tableName,
|
|
1044
|
+
table,
|
|
1045
|
+
]),
|
|
1046
|
+
);
|
|
1047
|
+
const tableNodes: readonly SchemaTreeNode[] = tableEntries.map(
|
|
1048
|
+
([displayName, tableName, table]: [string, string, SqlTableIR]) => {
|
|
881
1049
|
const children: SchemaTreeNode[] = [];
|
|
882
1050
|
|
|
883
1051
|
const columnNodes: SchemaTreeNode[] = [];
|
|
@@ -888,7 +1056,7 @@ export function createSqlFamilyInstance<TTargetId extends string>(
|
|
|
888
1056
|
columnNodes.push(
|
|
889
1057
|
new SchemaTreeNode({
|
|
890
1058
|
kind: 'field',
|
|
891
|
-
id: `column-${
|
|
1059
|
+
id: `column-${displayName}-${columnName}`,
|
|
892
1060
|
label,
|
|
893
1061
|
meta: {
|
|
894
1062
|
nativeType: column.nativeType,
|
|
@@ -903,7 +1071,7 @@ export function createSqlFamilyInstance<TTargetId extends string>(
|
|
|
903
1071
|
children.push(
|
|
904
1072
|
new SchemaTreeNode({
|
|
905
1073
|
kind: 'collection',
|
|
906
|
-
id: `columns-${
|
|
1074
|
+
id: `columns-${displayName}`,
|
|
907
1075
|
label: 'columns',
|
|
908
1076
|
children: columnNodes,
|
|
909
1077
|
}),
|
|
@@ -915,7 +1083,7 @@ export function createSqlFamilyInstance<TTargetId extends string>(
|
|
|
915
1083
|
children.push(
|
|
916
1084
|
new SchemaTreeNode({
|
|
917
1085
|
kind: 'index',
|
|
918
|
-
id: `primary-key-${
|
|
1086
|
+
id: `primary-key-${displayName}`,
|
|
919
1087
|
label: `primary key: ${pkColumns}`,
|
|
920
1088
|
meta: {
|
|
921
1089
|
columns: table.primaryKey.columns,
|
|
@@ -931,7 +1099,7 @@ export function createSqlFamilyInstance<TTargetId extends string>(
|
|
|
931
1099
|
children.push(
|
|
932
1100
|
new SchemaTreeNode({
|
|
933
1101
|
kind: 'index',
|
|
934
|
-
id: `unique-${
|
|
1102
|
+
id: `unique-${displayName}-${name}`,
|
|
935
1103
|
label,
|
|
936
1104
|
meta: {
|
|
937
1105
|
columns: unique.columns,
|
|
@@ -947,7 +1115,7 @@ export function createSqlFamilyInstance<TTargetId extends string>(
|
|
|
947
1115
|
children.push(
|
|
948
1116
|
new SchemaTreeNode({
|
|
949
1117
|
kind: 'index',
|
|
950
|
-
id: `index-${
|
|
1118
|
+
id: `index-${displayName}-${name}`,
|
|
951
1119
|
label,
|
|
952
1120
|
meta: {
|
|
953
1121
|
columns: index.columns,
|
|
@@ -975,8 +1143,8 @@ export function createSqlFamilyInstance<TTargetId extends string>(
|
|
|
975
1143
|
|
|
976
1144
|
return new SchemaTreeNode({
|
|
977
1145
|
kind: 'entity',
|
|
978
|
-
id: `table-${
|
|
979
|
-
label: `table ${
|
|
1146
|
+
id: `table-${displayName}`,
|
|
1147
|
+
label: `table ${displayName}`,
|
|
980
1148
|
...(Object.keys(tableMeta).length > 0 ? { meta: tableMeta } : {}),
|
|
981
1149
|
...(children.length > 0 ? { children } : {}),
|
|
982
1150
|
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { Contract } from '@prisma-next/contract/types';
|
|
2
|
+
import type {
|
|
3
|
+
ContractSerializer,
|
|
4
|
+
DiffSubjectGranularity,
|
|
5
|
+
MigratableTargetDescriptor,
|
|
6
|
+
SchemaVerifier,
|
|
7
|
+
} from '@prisma-next/framework-components/control';
|
|
8
|
+
import type { PslDocumentAst } from '@prisma-next/framework-components/psl-ast';
|
|
9
|
+
import type { SqlStorage } from '@prisma-next/sql-contract/types';
|
|
10
|
+
import type { SqlOperationDescriptors } from '@prisma-next/sql-operations';
|
|
11
|
+
import type { SqlSchemaIR, SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
|
|
12
|
+
import type { SqlControlAdapter } from './control-adapter';
|
|
13
|
+
import type { SqlControlFamilyInstance } from './control-instance';
|
|
14
|
+
import type { SqlSchemaDiffFn } from './migrations/schema-differ';
|
|
15
|
+
import type { SqlMigrationPlanner, SqlMigrationRunner } from './migrations/types';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* One stack extension pack's already-assembled contract, paired with the
|
|
19
|
+
* `spaceId` its extension descriptor was registered under. `contract infer`
|
|
20
|
+
* needs both: the contract to know which elements the pack describes and to
|
|
21
|
+
* resolve the domain model a cross-space foreign key targets, the `spaceId`
|
|
22
|
+
* to qualify the emitted relation type (`<spaceId>:<namespace>.<Model>`).
|
|
23
|
+
* Neither the contract JSON nor the framework's `ContractSpace` wrapper
|
|
24
|
+
* self-declares its owning space id — it is only known from the extension
|
|
25
|
+
* descriptor that carries the `ContractSpace`.
|
|
26
|
+
*/
|
|
27
|
+
export interface SqlDescribedContractSpace {
|
|
28
|
+
readonly spaceId: string;
|
|
29
|
+
readonly contract: Contract<SqlStorage>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SqlControlTargetDescriptor<
|
|
33
|
+
TTargetId extends string,
|
|
34
|
+
TTargetDetails,
|
|
35
|
+
TContract extends Contract<SqlStorage> = Contract<SqlStorage>,
|
|
36
|
+
> extends MigratableTargetDescriptor<'sql', TTargetId, SqlControlFamilyInstance> {
|
|
37
|
+
readonly queryOperations?: () => SqlOperationDescriptors;
|
|
38
|
+
/**
|
|
39
|
+
* JSON ⇄ class boundary for the SQL target's contract. The descriptor
|
|
40
|
+
* composes a concrete `SqlContractSerializerBase` subclass; the rest
|
|
41
|
+
* of the control stack reaches `descriptor.contractSerializer` rather
|
|
42
|
+
* than importing a per-target deserialization function.
|
|
43
|
+
*/
|
|
44
|
+
readonly contractSerializer: ContractSerializer<TContract>;
|
|
45
|
+
/**
|
|
46
|
+
* Per-target schema verifier walking the contract against
|
|
47
|
+
* `SqlSchemaIR`. The descriptor composes a concrete
|
|
48
|
+
* `SqlSchemaVerifierBase` subclass; the family-shared walk lives on
|
|
49
|
+
* the base, the target-specific dispatch on the subclass.
|
|
50
|
+
*/
|
|
51
|
+
readonly schemaVerifier: SchemaVerifier<TContract, SqlSchemaIR>;
|
|
52
|
+
/**
|
|
53
|
+
* Database→PSL inference for `contract infer`. Target logic (owns the dialect
|
|
54
|
+
* maps), so it lives on the descriptor. Optional: targets without `contract
|
|
55
|
+
* infer` (Mongo) omit it, and the family instance throws when it is absent.
|
|
56
|
+
* `describedContracts` carries the stack's extension packs' already-assembled
|
|
57
|
+
* contracts (each paired with its `spaceId`) so the inferrer can omit elements
|
|
58
|
+
* they already describe, and can qualify a cross-space relation with the
|
|
59
|
+
* owning pack's space id.
|
|
60
|
+
*/
|
|
61
|
+
readonly inferPslContract?: (
|
|
62
|
+
schema: SqlSchemaIRNode,
|
|
63
|
+
describedContracts?: readonly SqlDescribedContractSpace[],
|
|
64
|
+
) => PslDocumentAst;
|
|
65
|
+
/**
|
|
66
|
+
* The full-tree node diff the family verify verdict derives from —
|
|
67
|
+
* expected-tree derivation, pre-diff normalization, the generic differ,
|
|
68
|
+
* and ownership scoping, all target-side. The family applies strict
|
|
69
|
+
* gating + control-policy disposition over the returned issues; verify
|
|
70
|
+
* rejects when a surviving issue is a failure.
|
|
71
|
+
*/
|
|
72
|
+
readonly diffSchema: SqlSchemaDiffFn;
|
|
73
|
+
/**
|
|
74
|
+
* Classifies a diff-tree node's `nodeKind` into its framework-neutral
|
|
75
|
+
* {@link DiffSubjectGranularity} — the target owns the full node vocabulary
|
|
76
|
+
* that appears in its diff tree (its own kinds plus the relational kinds it
|
|
77
|
+
* delegates to), so it is the one place that can resolve this. The family
|
|
78
|
+
* verdict calls it inline per issue (never stamping the result); the
|
|
79
|
+
* framework aggregate's unclaimed-elements sweep reaches the same
|
|
80
|
+
* classifier via the family instance's `classifySubjectGranularity`
|
|
81
|
+
* capability.
|
|
82
|
+
*/
|
|
83
|
+
readonly classifySubjectGranularity: (nodeKind: string) => DiffSubjectGranularity;
|
|
84
|
+
/**
|
|
85
|
+
* Classifies a diff-tree node's `nodeKind` into its storage `entityKind` —
|
|
86
|
+
* the same vocabulary the contract storage's `entries` dictionary keys use
|
|
87
|
+
* (e.g. `'table'`). Sibling of `classifySubjectGranularity`, resolved the
|
|
88
|
+
* same way: the target owns the full node vocabulary, so it is the one
|
|
89
|
+
* place that can resolve this. `undefined` for a node kind with no
|
|
90
|
+
* storage entity of its own (a column, an index, …). The framework
|
|
91
|
+
* aggregate's unclaimed-elements sweep reaches this via the family
|
|
92
|
+
* instance's `classifyEntityKind` capability, so it never hardcodes a
|
|
93
|
+
* family entity kind.
|
|
94
|
+
*/
|
|
95
|
+
readonly classifyEntityKind: (nodeKind: string) => string | undefined;
|
|
96
|
+
createPlanner(adapter: SqlControlAdapter<TTargetId>): SqlMigrationPlanner<TTargetDetails>;
|
|
97
|
+
createRunner(family: SqlControlFamilyInstance): SqlMigrationRunner<TTargetDetails>;
|
|
98
|
+
}
|