@prisma-next/family-sql 0.14.0-dev.49 → 0.14.0-dev.50
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/{control-adapter-DHYFuOBy.d.mts → control-adapter-BVOj4gXI.d.mts} +34 -5
- package/dist/control-adapter-BVOj4gXI.d.mts.map +1 -0
- package/dist/control-adapter.d.mts +1 -1
- package/dist/control.d.mts +65 -50
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +308 -56
- package/dist/control.mjs.map +1 -1
- package/dist/diff.d.mts +141 -30
- package/dist/diff.d.mts.map +1 -1
- package/dist/diff.mjs +155 -1384
- package/dist/diff.mjs.map +1 -1
- package/dist/ir.d.mts +5 -5
- 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 +1 -1
- 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-DwUSJ4PO.mjs → sql-contract-serializer-C75cfMSS.mjs} +2 -2
- package/dist/{sql-contract-serializer-DwUSJ4PO.mjs.map → sql-contract-serializer-C75cfMSS.mjs.map} +1 -1
- package/dist/{types-DBxrwN1d.d.mts → types-BW7pzb2j.d.mts} +24 -15
- package/dist/types-BW7pzb2j.d.mts.map +1 -0
- package/package.json +21 -21
- package/src/core/control-instance.ts +99 -80
- package/src/core/control-target-descriptor.ts +29 -14
- package/src/core/diff/diff-tree-normalization.ts +211 -0
- package/src/core/diff/schema-verify.ts +324 -0
- package/src/core/diff/sql-schema-diff.ts +40 -1466
- package/src/core/diff/verifier-disposition.ts +14 -42
- package/src/core/ir/sql-schema-verifier-base.ts +5 -5
- package/src/core/migrations/contract-to-schema-ir.ts +63 -20
- package/src/core/migrations/native-type-expander.ts +28 -0
- package/src/core/migrations/schema-differ.ts +30 -29
- package/src/core/migrations/types.ts +23 -14
- package/src/exports/control.ts +6 -0
- package/src/exports/diff.ts +28 -8
- package/dist/contract-to-schema-ir-S-evq8E6.mjs +0 -264
- package/dist/contract-to-schema-ir-S-evq8E6.mjs.map +0 -1
- package/dist/control-adapter-DHYFuOBy.d.mts.map +0 -1
- package/dist/sql-schema-diff-6z36dZt6.d.mts +0 -105
- package/dist/sql-schema-diff-6z36dZt6.d.mts.map +0 -1
- package/dist/types-DBxrwN1d.d.mts.map +0 -1
- package/src/core/diff/control-verify-emit.ts +0 -46
- package/src/core/diff/verify-helpers.ts +0 -832
package/dist/control.mjs
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { i as sqlFamilyPslBlockDescriptors, n as sqlFamilyAuthoringFieldPresets, r as sqlFamilyEntityTypes, t as sqlFamilyAuthoringTypes } from "./authoring-type-constructors-CXd-8ydc.mjs";
|
|
2
|
-
import { t as
|
|
2
|
+
import { n as classifyDiffSubjectGranularity, o as verifySqlSchemaByDiff, s as extractCodecControlHooks, t as classifyDiffEntityKind } from "./schema-verify-W3r631Jh.mjs";
|
|
3
|
+
import { t as SqlContractSerializer } from "./sql-contract-serializer-C75cfMSS.mjs";
|
|
3
4
|
import { t as collectSupportedCodecTypeIds } from "./verify-C-G0obRm.mjs";
|
|
4
|
-
import { a as extractCodecControlHooks, i as resolveValueSetValues, n as contractToSchemaIR, r as detectDestructiveChanges, t as contractNamespaceToSchemaIR } from "./contract-to-schema-ir-S-evq8E6.mjs";
|
|
5
5
|
import { n as temporalAuthoringPresets, r as timestampNowControlDescriptor } from "./timestamp-now-generator-CloimujU.mjs";
|
|
6
6
|
import { sqlEmission } from "@prisma-next/sql-contract-emitter";
|
|
7
7
|
import { blindCast } from "@prisma-next/utils/casts";
|
|
8
|
-
import {
|
|
9
|
-
import { APP_SPACE_ID, SchemaTreeNode, VERIFY_CODE_HASH_MISMATCH, VERIFY_CODE_MARKER_MISSING, VERIFY_CODE_TARGET_MISMATCH, assembleAuthoringContributions, dispositionForCategory } from "@prisma-next/framework-components/control";
|
|
8
|
+
import { APP_SPACE_ID, SchemaTreeNode, VERIFY_CODE_HASH_MISMATCH, VERIFY_CODE_MARKER_MISSING, VERIFY_CODE_TARGET_MISMATCH, assembleAuthoringContributions } from "@prisma-next/framework-components/control";
|
|
10
9
|
import { isPlainRecord } from "@prisma-next/framework-components/ir";
|
|
11
10
|
import { assertDescriptorSelfConsistency } from "@prisma-next/migration-tools/spaces";
|
|
12
11
|
import { sqlContractCanonicalizationHooks } from "@prisma-next/sql-contract/canonicalization-hooks";
|
|
13
12
|
import { defaultIndexName } from "@prisma-next/sql-schema-ir/naming";
|
|
14
13
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
15
|
-
import {
|
|
14
|
+
import { effectiveControlPolicy } from "@prisma-next/contract/types";
|
|
15
|
+
import { StorageTable, isStorageTypeInstance } from "@prisma-next/sql-contract/types";
|
|
16
|
+
import { SqlSchemaIR, SqlTableIR } from "@prisma-next/sql-schema-ir/types";
|
|
16
17
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
17
18
|
//#region src/core/operation-preview.ts
|
|
18
19
|
function isDdlStatement(sqlStatement) {
|
|
@@ -210,12 +211,13 @@ function createSqlFamilyInstance(stack) {
|
|
|
210
211
|
const getControlAdapter = () => controlAdapter ??= adapter.create(stack);
|
|
211
212
|
const targetSerializer = target.contractSerializer;
|
|
212
213
|
const targetInferPslContract = blindCast(target).inferPslContract;
|
|
214
|
+
const diffSchema = blindCast(target).diffSchema;
|
|
215
|
+
const targetGranularityOf = blindCast(target).classifySubjectGranularity;
|
|
216
|
+
const targetEntityKindOf = blindCast(target).classifyEntityKind;
|
|
213
217
|
const describedContracts = extensions.flatMap((extension) => extension.contractSpace ? [{
|
|
214
218
|
spaceId: extension.id,
|
|
215
219
|
contract: extension.contractSpace.contractJson
|
|
216
220
|
}] : []);
|
|
217
|
-
const verifyDatabaseSchema = blindCast(target).verifyDatabaseSchema;
|
|
218
|
-
if (!verifyDatabaseSchema) throw new Error(`SQL target "${target.targetId}" is missing the required verifyDatabaseSchema descriptor operation`);
|
|
219
221
|
const deserializeWithTargetSerializer = (contractOrJson) => {
|
|
220
222
|
const serializer = targetSerializer ?? new SqlContractSerializer();
|
|
221
223
|
const json = targetSerializer !== void 0 && !isPlainRecord(contractOrJson) ? targetSerializer.serializeContract(blindCast(contractOrJson)) : contractOrJson;
|
|
@@ -322,40 +324,40 @@ function createSqlFamilyInstance(stack) {
|
|
|
322
324
|
},
|
|
323
325
|
verifySchema(options) {
|
|
324
326
|
const contract = deserializeWithTargetSerializer(options.contract);
|
|
325
|
-
|
|
327
|
+
if (!diffSchema) throw new Error(`SQL target "${target.targetId}" is missing the required diffSchema descriptor operation`);
|
|
328
|
+
if (!targetGranularityOf) throw new Error(`SQL target "${target.targetId}" is missing the required classifySubjectGranularity descriptor operation`);
|
|
329
|
+
return verifySqlSchemaByDiff({
|
|
326
330
|
contract,
|
|
327
331
|
schema: options.schema,
|
|
328
332
|
strict: options.strict,
|
|
329
|
-
|
|
330
|
-
|
|
333
|
+
frameworkComponents: options.frameworkComponents,
|
|
334
|
+
diffSchema,
|
|
335
|
+
granularityOf: targetGranularityOf
|
|
331
336
|
});
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
};
|
|
337
|
+
},
|
|
338
|
+
/**
|
|
339
|
+
* Classifies a diff issue's subject granularity on demand, by resolving
|
|
340
|
+
* its node's `nodeKind` through the target's classifier — the
|
|
341
|
+
* {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable}
|
|
342
|
+
* capability. Framework consumers spanning contract spaces (the
|
|
343
|
+
* migration aggregate's unclaimed-elements sweep) detect and call this
|
|
344
|
+
* instead of reaching into the concrete schema-IR node, which they
|
|
345
|
+
* cannot read; nothing is stamped on the issue or the node.
|
|
346
|
+
*/
|
|
347
|
+
classifySubjectGranularity(issue) {
|
|
348
|
+
if (!targetGranularityOf) throw new Error(`SQL target "${target.targetId}" is missing the required classifySubjectGranularity descriptor operation`);
|
|
349
|
+
return classifyDiffSubjectGranularity(issue, targetGranularityOf);
|
|
350
|
+
},
|
|
351
|
+
/**
|
|
352
|
+
* Classifies a diff issue's subject storage `entityKind` on demand, by
|
|
353
|
+
* resolving its node's `nodeKind` through the target's classifier —
|
|
354
|
+
* the sibling of `classifySubjectGranularity` above, and part of the
|
|
355
|
+
* same {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable}
|
|
356
|
+
* capability.
|
|
357
|
+
*/
|
|
358
|
+
classifyEntityKind(issue) {
|
|
359
|
+
if (!targetEntityKindOf) throw new Error(`SQL target "${target.targetId}" is missing the required classifyEntityKind descriptor operation`);
|
|
360
|
+
return classifyDiffEntityKind(issue, targetEntityKindOf);
|
|
359
361
|
},
|
|
360
362
|
async sign(options) {
|
|
361
363
|
const { driver, contract: contractInput, contractPath, configPath } = options;
|
|
@@ -553,24 +555,6 @@ function createSqlFamilyInstance(stack) {
|
|
|
553
555
|
}
|
|
554
556
|
};
|
|
555
557
|
}
|
|
556
|
-
/**
|
|
557
|
-
* Filters the structural schema-diff issues (from `diffDatabaseSchema`) through
|
|
558
|
-
* the contract's `defaultControlPolicy`. Issues whose outcome maps to a suppressed
|
|
559
|
-
* category under the effective policy are removed. This mirrors the control-policy
|
|
560
|
-
* filtering applied by `verifySqlSchema` for table/column-level findings.
|
|
561
|
-
*
|
|
562
|
-
* Outcome → category mapping:
|
|
563
|
-
* - `'extra'` → `'extraAuxiliary'` (an extra auxiliary entity, e.g. an RLS policy)
|
|
564
|
-
* - `'missing'` → `'declaredMissing'`
|
|
565
|
-
* - `'mismatch'` → `'declaredIncompatible'`
|
|
566
|
-
*/
|
|
567
|
-
function filterSchemaDiffIssues(issues, defaultControlPolicy) {
|
|
568
|
-
if (issues.length === 0) return issues;
|
|
569
|
-
const policy = effectiveControlPolicy(void 0, defaultControlPolicy);
|
|
570
|
-
return issues.filter((issue) => {
|
|
571
|
-
return dispositionForCategory(policy, issue.outcome === "extra" ? "extraAuxiliary" : issue.outcome === "missing" ? "declaredMissing" : "declaredIncompatible") !== "suppress";
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
558
|
//#endregion
|
|
575
559
|
//#region src/core/control-descriptor.ts
|
|
576
560
|
var SqlFamilyDescriptor = class {
|
|
@@ -590,6 +574,255 @@ var SqlFamilyDescriptor = class {
|
|
|
590
574
|
}
|
|
591
575
|
};
|
|
592
576
|
//#endregion
|
|
577
|
+
//#region src/core/migrations/contract-to-schema-ir.ts
|
|
578
|
+
function convertColumn(name, column, storageTypes, expandNativeType, renderDefault) {
|
|
579
|
+
const resolved = resolveColumnTypeMetadata(column, storageTypes);
|
|
580
|
+
const baseNativeType = expandNativeType ? expandNativeType({
|
|
581
|
+
nativeType: resolved.nativeType,
|
|
582
|
+
codecId: resolved.codecId,
|
|
583
|
+
...ifDefined("typeParams", resolved.typeParams)
|
|
584
|
+
}) : resolved.nativeType;
|
|
585
|
+
const nativeType = column.many ? `${baseNativeType}[]` : baseNativeType;
|
|
586
|
+
return {
|
|
587
|
+
name,
|
|
588
|
+
nativeType,
|
|
589
|
+
nullable: column.nullable,
|
|
590
|
+
...ifDefined("default", column.default != null && renderDefault ? renderDefault(column.default, column) : void 0),
|
|
591
|
+
resolvedNativeType: nativeType,
|
|
592
|
+
...ifDefined("resolvedDefault", column.default ?? void 0),
|
|
593
|
+
codecRef: buildColumnCodecRef(resolved, column.many),
|
|
594
|
+
codecBaseNativeType: resolved.nativeType,
|
|
595
|
+
...column.typeRef !== void 0 ? { codecNamedType: true } : {}
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Builds the column's `CodecRef` from its resolved (post-`typeRef`) codec
|
|
600
|
+
* identity — the same construction the query AST and the migration DDL
|
|
601
|
+
* renderer already use (TML-2456, TML-2918).
|
|
602
|
+
*/
|
|
603
|
+
function buildColumnCodecRef(resolved, many) {
|
|
604
|
+
return {
|
|
605
|
+
codecId: resolved.codecId,
|
|
606
|
+
...ifDefined("typeParams", resolved.typeParams !== void 0 ? blindCast(resolved.typeParams) : void 0),
|
|
607
|
+
...ifDefined("many", many)
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
function resolveColumnTypeMetadata(column, storageTypes) {
|
|
611
|
+
if (!column.typeRef) return column;
|
|
612
|
+
const referenced = storageTypes[column.typeRef];
|
|
613
|
+
if (!referenced) throw new Error(`Column references storage type "${column.typeRef}" but it is not defined in storage.types.`);
|
|
614
|
+
if (isStorageTypeInstance(referenced)) return {
|
|
615
|
+
codecId: referenced.codecId,
|
|
616
|
+
nativeType: referenced.nativeType,
|
|
617
|
+
typeParams: referenced.typeParams
|
|
618
|
+
};
|
|
619
|
+
throw new Error(`Storage type "${column.typeRef}" has an unknown polymorphic kind; expected a codec-typed StorageTypeInstance.`);
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Resolves a `ValueSetRef` to its permitted values from the contract storage.
|
|
623
|
+
*
|
|
624
|
+
* Throws when the referenced namespace or value-set is absent — this indicates
|
|
625
|
+
* the contract was built incorrectly (the check and the value-set must be
|
|
626
|
+
* co-emitted by the lowering step). Used by `convertCheck` (schema-IR
|
|
627
|
+
* projection), `verifyCheckConstraints` (verification), and
|
|
628
|
+
* `checkConstraintPlanCallStrategy` (migration planning) so all three agree on
|
|
629
|
+
* the resolved values and the error behavior on a missing reference.
|
|
630
|
+
*/
|
|
631
|
+
function allStrings(values) {
|
|
632
|
+
return values.every((value) => typeof value === "string");
|
|
633
|
+
}
|
|
634
|
+
function resolveValueSetValues(ref, storage, contextLabel) {
|
|
635
|
+
const ns = storage.namespaces[ref.namespaceId];
|
|
636
|
+
if (!ns) throw new Error(`resolveValueSetValues: namespace "${ref.namespaceId}" not found in storage (${contextLabel})`);
|
|
637
|
+
const valueSet = ns.entries.valueSet?.[ref.entityName];
|
|
638
|
+
if (!valueSet) throw new Error(`resolveValueSetValues: value-set "${ref.entityName}" not found in namespace "${ref.namespaceId}" (${contextLabel})`);
|
|
639
|
+
const values = valueSet.values;
|
|
640
|
+
if (!allStrings(values)) throw new Error(`resolveValueSetValues: value-set "${ref.entityName}" in namespace "${ref.namespaceId}" has a non-string value; numeric-enum CHECK constraints are not yet supported (${contextLabel})`);
|
|
641
|
+
return values;
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Projects a `CheckConstraint` IR into an `SqlCheckConstraintIRInput` by
|
|
645
|
+
* resolving the permitted values from the storage value-set it references.
|
|
646
|
+
*
|
|
647
|
+
* The `CheckConstraint.valueSet` ref points to
|
|
648
|
+
* `storage.namespaces[namespaceId].entries.valueSet[name]`. The resolved
|
|
649
|
+
* values are lifted directly from `StorageValueSet.values` so verification
|
|
650
|
+
* compares value sets, not SQL predicate strings.
|
|
651
|
+
*
|
|
652
|
+
* Throws if the referenced namespace or value-set is absent — this
|
|
653
|
+
* indicates the contract was built incorrectly (the check and the
|
|
654
|
+
* value-set must be co-emitted by the lowering step).
|
|
655
|
+
*/
|
|
656
|
+
function convertCheck(check, storage) {
|
|
657
|
+
const permittedValues = resolveValueSetValues(check.valueSet, storage, `check "${check.name}"`);
|
|
658
|
+
return {
|
|
659
|
+
name: check.name,
|
|
660
|
+
column: check.column,
|
|
661
|
+
permittedValues
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
function convertUnique(unique) {
|
|
665
|
+
return {
|
|
666
|
+
columns: unique.columns,
|
|
667
|
+
...ifDefined("name", unique.name)
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
function convertIndex(index) {
|
|
671
|
+
return {
|
|
672
|
+
columns: index.columns,
|
|
673
|
+
unique: false,
|
|
674
|
+
...ifDefined("name", index.name),
|
|
675
|
+
...ifDefined("type", index.type),
|
|
676
|
+
...ifDefined("options", index.options)
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
function convertForeignKey(fk) {
|
|
680
|
+
return {
|
|
681
|
+
columns: fk.source.columns,
|
|
682
|
+
referencedTable: fk.target.tableName,
|
|
683
|
+
referencedSchema: fk.target.namespaceId,
|
|
684
|
+
referencedColumns: fk.target.columns,
|
|
685
|
+
...ifDefined("name", fk.name),
|
|
686
|
+
...ifDefined("onDelete", fk.onDelete),
|
|
687
|
+
...ifDefined("onUpdate", fk.onUpdate)
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
function convertTable(name, table, storageTypes, expandNativeType, renderDefault, storage) {
|
|
691
|
+
const columns = {};
|
|
692
|
+
for (const [colName, colDef] of Object.entries(table.columns)) columns[colName] = convertColumn(colName, colDef, storageTypes, expandNativeType, renderDefault);
|
|
693
|
+
const satisfiedIndexColumns = new Set([
|
|
694
|
+
...table.indexes.map((idx) => idx.columns.join(",")),
|
|
695
|
+
...table.uniques.map((unique) => unique.columns.join(",")),
|
|
696
|
+
...table.primaryKey ? [table.primaryKey.columns.join(",")] : []
|
|
697
|
+
]);
|
|
698
|
+
const fkBackingIndexes = [];
|
|
699
|
+
for (const fk of table.foreignKeys) {
|
|
700
|
+
if (fk.index === false) continue;
|
|
701
|
+
const key = fk.source.columns.join(",");
|
|
702
|
+
if (satisfiedIndexColumns.has(key)) continue;
|
|
703
|
+
fkBackingIndexes.push({
|
|
704
|
+
columns: fk.source.columns,
|
|
705
|
+
unique: false,
|
|
706
|
+
name: defaultIndexName(name, fk.source.columns)
|
|
707
|
+
});
|
|
708
|
+
satisfiedIndexColumns.add(key);
|
|
709
|
+
}
|
|
710
|
+
const checks = table.checks && table.checks.length > 0 ? table.checks.map((c) => convertCheck(c, storage)) : void 0;
|
|
711
|
+
return new SqlTableIR({
|
|
712
|
+
name,
|
|
713
|
+
columns,
|
|
714
|
+
...ifDefined("primaryKey", table.primaryKey),
|
|
715
|
+
foreignKeys: table.foreignKeys.filter((fk) => fk.constraint !== false).map(convertForeignKey),
|
|
716
|
+
uniques: table.uniques.map(convertUnique),
|
|
717
|
+
indexes: [...table.indexes.map(convertIndex), ...fkBackingIndexes],
|
|
718
|
+
...ifDefined("checks", checks)
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Detects destructive changes between two contract storages.
|
|
723
|
+
*
|
|
724
|
+
* The additive-only planner silently ignores removals (tables, columns).
|
|
725
|
+
* This function detects those removals so callers can report them as conflicts
|
|
726
|
+
* rather than silently producing an empty plan.
|
|
727
|
+
*
|
|
728
|
+
* Returns an empty array if no destructive changes are found.
|
|
729
|
+
*/
|
|
730
|
+
function detectDestructiveChanges(from, to) {
|
|
731
|
+
if (!from) return [];
|
|
732
|
+
const hasOwn = (value, key) => Object.hasOwn(value, key);
|
|
733
|
+
const conflicts = [];
|
|
734
|
+
const namespaceIds = [...new Set([...Object.keys(from.namespaces), ...Object.keys(to.namespaces)])].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
735
|
+
for (const namespaceId of namespaceIds) {
|
|
736
|
+
const fromNs = from.namespaces[namespaceId];
|
|
737
|
+
const toNs = to.namespaces[namespaceId];
|
|
738
|
+
const fromTables = fromNs?.entries.table;
|
|
739
|
+
if (!fromTables) continue;
|
|
740
|
+
for (const tableName of Object.keys(fromTables)) {
|
|
741
|
+
const toTableRaw = toNs?.entries.table?.[tableName];
|
|
742
|
+
if (!StorageTable.is(toTableRaw)) {
|
|
743
|
+
conflicts.push({
|
|
744
|
+
kind: "tableRemoved",
|
|
745
|
+
summary: `Table "${tableName}" was removed`
|
|
746
|
+
});
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
const toTable = toTableRaw;
|
|
750
|
+
const fromTableRaw = fromTables[tableName];
|
|
751
|
+
if (!StorageTable.is(fromTableRaw)) continue;
|
|
752
|
+
const fromTable = fromTableRaw;
|
|
753
|
+
for (const columnName of Object.keys(fromTable.columns)) if (!hasOwn(toTable.columns, columnName)) conflicts.push({
|
|
754
|
+
kind: "columnRemoved",
|
|
755
|
+
summary: `Column "${tableName}"."${columnName}" was removed`
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return conflicts;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Converts a `Contract` to `SqlSchemaIR`.
|
|
763
|
+
*
|
|
764
|
+
* Reads `contract.storage` for tables and `contract.storage.types` for type
|
|
765
|
+
* annotations. Storage-type annotations are written under
|
|
766
|
+
* `options.annotationNamespace`.
|
|
767
|
+
*
|
|
768
|
+
* Drops codec metadata (`codecId`, `typeRef`) since the schema IR only represents
|
|
769
|
+
* structural information. When `expandNativeType` is provided, parameterized types
|
|
770
|
+
* are expanded (e.g. `character` + `{ length: 36 }` → `character(36)`) so the
|
|
771
|
+
* resulting IR compares correctly against the "to" contract during planning.
|
|
772
|
+
*
|
|
773
|
+
* Returns an empty schema IR when `contract` is `null` (new project).
|
|
774
|
+
*/
|
|
775
|
+
/**
|
|
776
|
+
* Converts the tables of a single namespace into a `SqlSchemaIR`, keyed by
|
|
777
|
+
* table name within that namespace. Unlike {@link contractToSchemaIR}, which
|
|
778
|
+
* flattens every namespace's tables into one bare-keyed record (and throws on a
|
|
779
|
+
* cross-namespace name collision), this scopes the table iteration to one
|
|
780
|
+
* namespace so the same table name can exist in two schemas.
|
|
781
|
+
*
|
|
782
|
+
* The full `storage` is still passed to `convertTable`, so value-set / enum /
|
|
783
|
+
* type resolution that legitimately spans namespaces is unaffected. Foreign
|
|
784
|
+
* keys are built purely from the FK descriptor (`fk.target`), so cross-namespace
|
|
785
|
+
* FKs survive per-namespace conversion. The `annotations` block (storage-type
|
|
786
|
+
* derived) is omitted here — the per-namespace tree consumer reads only the
|
|
787
|
+
* per-table fields.
|
|
788
|
+
*/
|
|
789
|
+
function contractNamespaceToSchemaIR(storage, namespaceId, options) {
|
|
790
|
+
if (options.annotationNamespace.length === 0) throw new Error("annotationNamespace must be a non-empty string");
|
|
791
|
+
const namespace = storage.namespaces[namespaceId];
|
|
792
|
+
if (!namespace) return new SqlSchemaIR({ tables: {} });
|
|
793
|
+
const storageTypes = { ...storage.types ?? {} };
|
|
794
|
+
const tables = {};
|
|
795
|
+
for (const [tableName, tableDefRaw] of Object.entries(namespace.entries.table ?? {})) {
|
|
796
|
+
StorageTable.assert(tableDefRaw, `namespaces.${namespaceId}.entries.table.${tableName}`);
|
|
797
|
+
tables[tableName] = convertTable(tableName, tableDefRaw, storageTypes, options.expandNativeType, options.renderDefault, storage);
|
|
798
|
+
}
|
|
799
|
+
return new SqlSchemaIR({ tables });
|
|
800
|
+
}
|
|
801
|
+
function contractToSchemaIR(contract, options) {
|
|
802
|
+
if (options.annotationNamespace.length === 0) throw new Error("annotationNamespace must be a non-empty string");
|
|
803
|
+
if (!contract) return new SqlSchemaIR({ tables: {} });
|
|
804
|
+
const storage = contract.storage;
|
|
805
|
+
const storageTypes = { ...storage.types ?? {} };
|
|
806
|
+
const tables = {};
|
|
807
|
+
for (const ns of Object.values(storage.namespaces)) for (const [tableName, tableDefRaw] of Object.entries(ns.entries.table ?? {})) {
|
|
808
|
+
StorageTable.assert(tableDefRaw, `namespaces.${ns.id}.entries.table.${tableName}`);
|
|
809
|
+
const tableDef = tableDefRaw;
|
|
810
|
+
if (tables[tableName] !== void 0) throw new Error(`contractToSchemaIR: duplicate SQL table name "${tableName}" across namespaces (ambiguous for flat SqlSchemaIR.tables).`);
|
|
811
|
+
tables[tableName] = convertTable(tableName, tableDef, storageTypes, options.expandNativeType, options.renderDefault, storage);
|
|
812
|
+
}
|
|
813
|
+
return new SqlSchemaIR({
|
|
814
|
+
tables,
|
|
815
|
+
...ifDefined("annotations", deriveAnnotations(storage, options.annotationNamespace, options.resolveEnumNamespaceSchema))
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
function deriveAnnotations(storage, annotationNamespace, _resolveEnumNamespaceSchema) {
|
|
819
|
+
const storageTypes = {};
|
|
820
|
+
for (const typeInstance of Object.values(storage.types ?? {})) if (isStorageTypeInstance(typeInstance)) storageTypes[typeInstance.nativeType] = typeInstance;
|
|
821
|
+
const envelope = { ...Object.keys(storageTypes).length > 0 ? { storageTypes } : {} };
|
|
822
|
+
if (Object.keys(envelope).length === 0) return void 0;
|
|
823
|
+
return { [annotationNamespace]: envelope };
|
|
824
|
+
}
|
|
825
|
+
//#endregion
|
|
593
826
|
//#region src/core/migrations/control-policy.ts
|
|
594
827
|
/**
|
|
595
828
|
* The control policy that governs a single call. The `external` default is an
|
|
@@ -884,6 +1117,25 @@ function unionSorted(a, b) {
|
|
|
884
1117
|
return [...set].sort((x, y) => x < y ? -1 : x > y ? 1 : 0);
|
|
885
1118
|
}
|
|
886
1119
|
//#endregion
|
|
1120
|
+
//#region src/core/migrations/native-type-expander.ts
|
|
1121
|
+
/**
|
|
1122
|
+
* Builds the codec-hook-composed `expandNativeType` callback the contract→IR
|
|
1123
|
+
* derivation uses to expand parameterized native types (e.g. `character` +
|
|
1124
|
+
* `{ length: 36 }` → `character(36)`). Returns `undefined` when no framework
|
|
1125
|
+
* components are supplied, so callers can omit the option entirely.
|
|
1126
|
+
*/
|
|
1127
|
+
function buildNativeTypeExpander(frameworkComponents) {
|
|
1128
|
+
if (!frameworkComponents) return;
|
|
1129
|
+
const codecHooks = extractCodecControlHooks(frameworkComponents);
|
|
1130
|
+
return (input) => {
|
|
1131
|
+
if (!input.typeParams) return input.nativeType;
|
|
1132
|
+
if (!input.codecId) return input.nativeType;
|
|
1133
|
+
const hooks = codecHooks.get(input.codecId);
|
|
1134
|
+
if (!hooks?.expandNativeType) return input.nativeType;
|
|
1135
|
+
return hooks.expandNativeType(input);
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
//#endregion
|
|
887
1139
|
//#region src/core/migrations/plan-helpers.ts
|
|
888
1140
|
const readOnlyEmptyObject = Object.freeze({});
|
|
889
1141
|
function cloneRecord(value) {
|
|
@@ -995,6 +1247,6 @@ const INIT_ADDITIVE_POLICY = Object.freeze({ allowedOperationClasses: Object.fre
|
|
|
995
1247
|
//#region src/exports/control.ts
|
|
996
1248
|
var control_default = new SqlFamilyDescriptor();
|
|
997
1249
|
//#endregion
|
|
998
|
-
export { INIT_ADDITIVE_POLICY, assembleAuthoringContributions, contractNamespaceToSchemaIR, contractToSchemaIR, controlPolicyForCall, createMigrationPlan, control_default as default, detectDestructiveChanges, extractCodecControlHooks, partitionCallsByControlPolicy, partitionIssuesByControlPolicy, planFieldEventOperations, plannerFailure, plannerSuccess, resolveValueSetValues, runnerFailure, runnerSuccess, temporalAuthoringPresets, timestampNowControlDescriptor };
|
|
1250
|
+
export { INIT_ADDITIVE_POLICY, assembleAuthoringContributions, buildNativeTypeExpander, contractNamespaceToSchemaIR, contractToSchemaIR, controlPolicyForCall, createMigrationPlan, control_default as default, detectDestructiveChanges, extractCodecControlHooks, partitionCallsByControlPolicy, partitionIssuesByControlPolicy, planFieldEventOperations, plannerFailure, plannerSuccess, resolveValueSetValues, runnerFailure, runnerSuccess, temporalAuthoringPresets, timestampNowControlDescriptor };
|
|
999
1251
|
|
|
1000
1252
|
//# sourceMappingURL=control.mjs.map
|