@prisma-next/sql-contract-psl 0.13.0 → 0.14.0-dev.10
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 -7
- package/dist/index.d.mts +10 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{interpreter-B_KtZusL.mjs → interpreter-CygvamTk.mjs} +529 -312
- package/dist/interpreter-CygvamTk.mjs.map +1 -0
- package/dist/provider.d.mts +1 -1
- package/dist/provider.d.mts.map +1 -1
- package/dist/provider.mjs +25 -8
- package/dist/provider.mjs.map +1 -1
- package/package.json +12 -12
- package/src/interpreter.ts +215 -426
- package/src/provider.ts +39 -8
- package/src/psl-attribute-parsing.ts +18 -14
- package/src/psl-authoring-arguments.ts +2 -2
- package/src/psl-column-resolution.ts +18 -16
- package/src/psl-field-resolution.ts +112 -28
- package/src/psl-named-type-resolution.ts +250 -0
- package/src/psl-relation-resolution.ts +250 -11
- package/dist/interpreter-B_KtZusL.mjs.map +0 -1
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { crossRef } from "@prisma-next/contract/types";
|
|
2
|
-
import { hasRegisteredFieldNamespace, instantiateAuthoringEntityType, instantiateAuthoringFieldPreset, instantiateAuthoringTypeConstructor, isAuthoringEntityTypeDescriptor, isAuthoringFieldPresetDescriptor, isAuthoringTypeConstructorDescriptor, validateAuthoringHelperArguments } from "@prisma-next/framework-components/authoring";
|
|
3
|
-
import {
|
|
2
|
+
import { hasRegisteredFieldNamespace, instantiateAuthoringEntityType, instantiateAuthoringFieldPreset, instantiateAuthoringTypeConstructor, isAuthoringEntityTypeDescriptor, isAuthoringFieldPresetDescriptor, isAuthoringPslBlockDescriptor, isAuthoringTypeConstructorDescriptor, validateAuthoringHelperArguments } from "@prisma-next/framework-components/authoring";
|
|
3
|
+
import { keywordPslSpan, nodePslSpan, parseQuotedStringLiteral } from "@prisma-next/psl-parser";
|
|
4
4
|
import { buildSqlContractFromDefinition } from "@prisma-next/sql-contract-ts/contract-builder";
|
|
5
|
+
import { assertDefined, invariant } from "@prisma-next/utils/assertions";
|
|
5
6
|
import { blindCast } from "@prisma-next/utils/casts";
|
|
6
7
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
7
8
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
8
|
-
import { getPositionalArgument, parseQuotedStringLiteral } from "@prisma-next/psl-parser";
|
|
9
|
-
import { assertDefined, invariant } from "@prisma-next/utils/assertions";
|
|
10
9
|
//#region src/psl-attribute-parsing.ts
|
|
10
|
+
function getPositionalArgument(attribute, index = 0) {
|
|
11
|
+
return attribute.args.filter((arg) => arg.kind === "positional")[index]?.value;
|
|
12
|
+
}
|
|
11
13
|
function lowerFirst(value) {
|
|
12
14
|
if (value.length === 0) return value;
|
|
13
15
|
return value[0]?.toLowerCase() + value.slice(1);
|
|
@@ -931,7 +933,7 @@ function resolvePslTypeConstructorDescriptor(input) {
|
|
|
931
933
|
*
|
|
932
934
|
* Symmetric with `instantiatePslTypeConstructor` but richer: a field preset can contribute `default`, `executionDefaults`, `id`, `unique`, and `nullable` in addition to the storage-type triple. PSL → typed-args coercion happens here (via `mapPslHelperArgs`) so that `instantiateAuthoringFieldPreset` itself stays typed-input-only and TS keeps its zero-runtime-validation cost.
|
|
933
935
|
*/
|
|
934
|
-
function
|
|
936
|
+
function instantiateFieldPreset(input) {
|
|
935
937
|
const helperPath = input.call.path.join(".");
|
|
936
938
|
const args = mapPslHelperArgs({
|
|
937
939
|
args: input.call.args,
|
|
@@ -970,10 +972,14 @@ function instantiatePslFieldPreset(input) {
|
|
|
970
972
|
}
|
|
971
973
|
}
|
|
972
974
|
function resolveFieldTypeDescriptor(input) {
|
|
975
|
+
if (input.field.malformedType) return {
|
|
976
|
+
ok: false,
|
|
977
|
+
alreadyReported: true
|
|
978
|
+
};
|
|
973
979
|
if (input.field.typeConstructor) {
|
|
974
980
|
const presetDescriptor = getAuthoringFieldPreset(input.authoringContributions, input.field.typeConstructor.path);
|
|
975
981
|
if (presetDescriptor) {
|
|
976
|
-
const instantiated =
|
|
982
|
+
const instantiated = instantiateFieldPreset({
|
|
977
983
|
call: input.field.typeConstructor,
|
|
978
984
|
descriptor: presetDescriptor,
|
|
979
985
|
diagnostics: input.diagnostics,
|
|
@@ -1071,7 +1077,7 @@ const NATIVE_TYPE_SPECS = {
|
|
|
1071
1077
|
"db.Uuid": {
|
|
1072
1078
|
args: "noArgs",
|
|
1073
1079
|
baseType: "String",
|
|
1074
|
-
codecId:
|
|
1080
|
+
codecId: "pg/uuid@1",
|
|
1075
1081
|
nativeType: "uuid"
|
|
1076
1082
|
},
|
|
1077
1083
|
"db.SmallInt": {
|
|
@@ -1301,13 +1307,53 @@ function lowerDefaultForField(input) {
|
|
|
1301
1307
|
return { executionDefaults: { onCreate: lowered.value.generated } };
|
|
1302
1308
|
}
|
|
1303
1309
|
function resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptors, scalarTypeDescriptors) {
|
|
1304
|
-
if (field.typeRef && namedTypeDescriptors.has(field.typeRef)) return namedTypeDescriptors.get(field.typeRef);
|
|
1305
1310
|
if (namedTypeDescriptors.has(field.typeName)) return namedTypeDescriptors.get(field.typeName);
|
|
1306
1311
|
if (enumTypeDescriptors.has(field.typeName)) return enumTypeDescriptors.get(field.typeName);
|
|
1307
1312
|
return scalarTypeDescriptors.get(field.typeName);
|
|
1308
1313
|
}
|
|
1309
1314
|
//#endregion
|
|
1310
1315
|
//#region src/psl-field-resolution.ts
|
|
1316
|
+
function lowerEnumDefaultForField(input) {
|
|
1317
|
+
const positionalEntries = input.defaultAttribute.args.filter((arg) => arg.kind === "positional");
|
|
1318
|
+
const hasNamedEntries = input.defaultAttribute.args.some((arg) => arg.kind === "named");
|
|
1319
|
+
const expressionEntry = positionalEntries[0];
|
|
1320
|
+
if (hasNamedEntries || positionalEntries.length !== 1 || expressionEntry === void 0) {
|
|
1321
|
+
input.diagnostics.push({
|
|
1322
|
+
code: "PSL_INVALID_DEFAULT_FUNCTION_ARGUMENT",
|
|
1323
|
+
message: `Field "${input.modelName}.${input.fieldName}" @default on an enum field expects exactly one positional enum member argument.`,
|
|
1324
|
+
sourceId: input.sourceId,
|
|
1325
|
+
span: input.defaultAttribute.span
|
|
1326
|
+
});
|
|
1327
|
+
return {};
|
|
1328
|
+
}
|
|
1329
|
+
const raw = expressionEntry.value.trim();
|
|
1330
|
+
const isQuotedString = /^(['"]).*\1$/.test(raw);
|
|
1331
|
+
const isFunctionCall = raw.includes("(") && raw.endsWith(")");
|
|
1332
|
+
if (isQuotedString || isFunctionCall) {
|
|
1333
|
+
input.diagnostics.push({
|
|
1334
|
+
code: "PSL_ENUM_DEFAULT_MUST_BE_MEMBER_NAME",
|
|
1335
|
+
message: `Field "${input.modelName}.${input.fieldName}" @default on an enum field must name a member (e.g. @default(Low)), not a raw value or function.`,
|
|
1336
|
+
sourceId: input.sourceId,
|
|
1337
|
+
span: input.defaultAttribute.span
|
|
1338
|
+
});
|
|
1339
|
+
return {};
|
|
1340
|
+
}
|
|
1341
|
+
const match = input.enumHandle.enumMembers.find((m) => m.name === raw);
|
|
1342
|
+
if (!match) {
|
|
1343
|
+
const validNames = input.enumHandle.enumMembers.map((m) => m.name).join(", ");
|
|
1344
|
+
input.diagnostics.push({
|
|
1345
|
+
code: "PSL_ENUM_UNKNOWN_DEFAULT_MEMBER",
|
|
1346
|
+
message: `Field "${input.modelName}.${input.fieldName}" @default(${raw}) does not name a member of ${input.enumHandle.enumName}. Valid members: ${validNames}.`,
|
|
1347
|
+
sourceId: input.sourceId,
|
|
1348
|
+
span: input.defaultAttribute.span
|
|
1349
|
+
});
|
|
1350
|
+
return {};
|
|
1351
|
+
}
|
|
1352
|
+
return { defaultValue: {
|
|
1353
|
+
kind: "literal",
|
|
1354
|
+
value: blindCast(match.value)
|
|
1355
|
+
} };
|
|
1356
|
+
}
|
|
1311
1357
|
const MODEL_COORDINATE_SEPARATOR = "\0";
|
|
1312
1358
|
function modelCoordinateKey(namespaceId, modelName) {
|
|
1313
1359
|
return `${namespaceId}${MODEL_COORDINATE_SEPARATOR}${modelName}`;
|
|
@@ -1381,9 +1427,9 @@ function extractFieldConstraintNames(input) {
|
|
|
1381
1427
|
};
|
|
1382
1428
|
}
|
|
1383
1429
|
function collectResolvedFields(input) {
|
|
1384
|
-
const { model, mapping, enumTypeDescriptors, namedTypeDescriptors, modelNames, compositeTypeNames, composedExtensions, authoringContributions, familyId, targetId, defaultFunctionRegistry, generatorDescriptorById, diagnostics, sourceId, scalarTypeDescriptors } = input;
|
|
1430
|
+
const { model, mapping, enumTypeDescriptors, namedTypeDescriptors, modelNames, compositeTypeNames, composedExtensions, authoringContributions, familyId, targetId, defaultFunctionRegistry, generatorDescriptorById, diagnostics, sourceId, scalarTypeDescriptors, enumHandles } = input;
|
|
1385
1431
|
const resolvedFields = [];
|
|
1386
|
-
for (const field of model.fields) {
|
|
1432
|
+
for (const field of Object.values(model.fields)) {
|
|
1387
1433
|
const isModelField = modelNames.has(field.typeName);
|
|
1388
1434
|
if (field.list && isModelField) continue;
|
|
1389
1435
|
validateFieldAttributes({
|
|
@@ -1474,7 +1520,15 @@ function collectResolvedFields(input) {
|
|
|
1474
1520
|
});
|
|
1475
1521
|
continue;
|
|
1476
1522
|
}
|
|
1477
|
-
const
|
|
1523
|
+
const enumHandle = enumHandles?.get(field.typeName);
|
|
1524
|
+
const loweredDefault = defaultAttribute ? enumHandle ? lowerEnumDefaultForField({
|
|
1525
|
+
modelName: model.name,
|
|
1526
|
+
fieldName: field.name,
|
|
1527
|
+
defaultAttribute,
|
|
1528
|
+
enumHandle,
|
|
1529
|
+
sourceId,
|
|
1530
|
+
diagnostics
|
|
1531
|
+
}) : lowerDefaultForField({
|
|
1478
1532
|
modelName: model.name,
|
|
1479
1533
|
fieldName: field.name,
|
|
1480
1534
|
defaultAttribute,
|
|
@@ -1495,7 +1549,8 @@ function collectResolvedFields(input) {
|
|
|
1495
1549
|
});
|
|
1496
1550
|
continue;
|
|
1497
1551
|
}
|
|
1498
|
-
|
|
1552
|
+
const fieldUsesNamedType = namedTypeDescriptors.has(field.typeName);
|
|
1553
|
+
if (loweredOnCreate && !fieldUsesNamedType) {
|
|
1499
1554
|
const generatedDescriptor = generatorDescriptorById.get(loweredOnCreate.id)?.resolveGeneratedColumnDescriptor?.({ generated: loweredOnCreate });
|
|
1500
1555
|
if (generatedDescriptor) descriptor = generatedDescriptor;
|
|
1501
1556
|
}
|
|
@@ -1531,6 +1586,7 @@ function collectResolvedFields(input) {
|
|
|
1531
1586
|
field,
|
|
1532
1587
|
columnName: mappedColumnName,
|
|
1533
1588
|
descriptor,
|
|
1589
|
+
nullable: presetContributions?.nullable ?? field.optional,
|
|
1534
1590
|
...ifDefined("defaultValue", fieldDefaultValue),
|
|
1535
1591
|
...ifDefined("executionDefaults", fieldExecutionDefaults),
|
|
1536
1592
|
isId: isIdField || Boolean(presetContributions?.id),
|
|
@@ -1556,7 +1612,7 @@ function buildModelMappings(modelEntries, defaultNamespaceId, diagnostics, sourc
|
|
|
1556
1612
|
span: model.span
|
|
1557
1613
|
});
|
|
1558
1614
|
const fieldColumns = /* @__PURE__ */ new Map();
|
|
1559
|
-
for (const field of model.fields) {
|
|
1615
|
+
for (const field of Object.values(model.fields)) {
|
|
1560
1616
|
const columnName = parseMapName({
|
|
1561
1617
|
attribute: getAttribute(field.attributes, "map"),
|
|
1562
1618
|
defaultValue: field.name,
|
|
@@ -1576,6 +1632,171 @@ function buildModelMappings(modelEntries, defaultNamespaceId, diagnostics, sourc
|
|
|
1576
1632
|
return result;
|
|
1577
1633
|
}
|
|
1578
1634
|
//#endregion
|
|
1635
|
+
//#region src/psl-named-type-resolution.ts
|
|
1636
|
+
function validateNamedTypeAttributes(input) {
|
|
1637
|
+
const [dbNativeTypeAttribute, ...extraDbNativeTypeAttributes] = input.allowDbNativeType ? input.declaration.attributes.filter((attribute) => attribute.name.startsWith("db.")) : [];
|
|
1638
|
+
let hasUnsupportedNamedTypeAttribute = false;
|
|
1639
|
+
for (const extra of extraDbNativeTypeAttributes) {
|
|
1640
|
+
input.diagnostics.push({
|
|
1641
|
+
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
1642
|
+
message: `Named type "${input.declaration.name}" can declare at most one @db.* attribute`,
|
|
1643
|
+
sourceId: input.sourceId,
|
|
1644
|
+
span: extra.span
|
|
1645
|
+
});
|
|
1646
|
+
hasUnsupportedNamedTypeAttribute = true;
|
|
1647
|
+
}
|
|
1648
|
+
for (const attribute of input.declaration.attributes) {
|
|
1649
|
+
if (input.allowDbNativeType && attribute.name.startsWith("db.")) continue;
|
|
1650
|
+
const uncomposedNamespace = checkUncomposedNamespace(attribute.name, input.composedExtensions, {
|
|
1651
|
+
familyId: input.familyId,
|
|
1652
|
+
targetId: input.targetId,
|
|
1653
|
+
authoringContributions: input.authoringContributions
|
|
1654
|
+
});
|
|
1655
|
+
if (uncomposedNamespace) {
|
|
1656
|
+
reportUncomposedNamespace({
|
|
1657
|
+
subjectLabel: `Attribute "@${attribute.name}"`,
|
|
1658
|
+
namespace: uncomposedNamespace,
|
|
1659
|
+
sourceId: input.sourceId,
|
|
1660
|
+
span: attribute.span,
|
|
1661
|
+
diagnostics: input.diagnostics
|
|
1662
|
+
});
|
|
1663
|
+
hasUnsupportedNamedTypeAttribute = true;
|
|
1664
|
+
continue;
|
|
1665
|
+
}
|
|
1666
|
+
input.diagnostics.push({
|
|
1667
|
+
code: "PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE",
|
|
1668
|
+
message: `Named type "${input.declaration.name}" uses unsupported attribute "${attribute.name}"`,
|
|
1669
|
+
sourceId: input.sourceId,
|
|
1670
|
+
span: attribute.span
|
|
1671
|
+
});
|
|
1672
|
+
hasUnsupportedNamedTypeAttribute = true;
|
|
1673
|
+
}
|
|
1674
|
+
return {
|
|
1675
|
+
dbNativeTypeAttribute,
|
|
1676
|
+
hasUnsupportedNamedTypeAttribute
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
function resolveNamedTypeDeclarations(input) {
|
|
1680
|
+
const storageTypeEntries = [];
|
|
1681
|
+
const namedTypeDescriptors = /* @__PURE__ */ new Map();
|
|
1682
|
+
for (const declaration of input.declarations) {
|
|
1683
|
+
if (declaration.isConstructor) {
|
|
1684
|
+
const typeConstructor = declaration.typeConstructor;
|
|
1685
|
+
if (typeConstructor === void 0) {
|
|
1686
|
+
input.diagnostics.push({
|
|
1687
|
+
code: "PSL_UNSUPPORTED_NAMED_TYPE_BASE",
|
|
1688
|
+
message: `Named type "${declaration.name}" must declare a base type or constructor`,
|
|
1689
|
+
sourceId: input.sourceId,
|
|
1690
|
+
span: declaration.span
|
|
1691
|
+
});
|
|
1692
|
+
continue;
|
|
1693
|
+
}
|
|
1694
|
+
const { hasUnsupportedNamedTypeAttribute } = validateNamedTypeAttributes({
|
|
1695
|
+
declaration,
|
|
1696
|
+
sourceId: input.sourceId,
|
|
1697
|
+
diagnostics: input.diagnostics,
|
|
1698
|
+
composedExtensions: input.composedExtensions,
|
|
1699
|
+
authoringContributions: input.authoringContributions,
|
|
1700
|
+
allowDbNativeType: false,
|
|
1701
|
+
familyId: input.familyId,
|
|
1702
|
+
targetId: input.targetId
|
|
1703
|
+
});
|
|
1704
|
+
if (hasUnsupportedNamedTypeAttribute) continue;
|
|
1705
|
+
const helperPath = typeConstructor.path.join(".");
|
|
1706
|
+
const descriptor = resolvePslTypeConstructorDescriptor({
|
|
1707
|
+
call: typeConstructor,
|
|
1708
|
+
authoringContributions: input.authoringContributions,
|
|
1709
|
+
composedExtensions: input.composedExtensions,
|
|
1710
|
+
familyId: input.familyId,
|
|
1711
|
+
targetId: input.targetId,
|
|
1712
|
+
diagnostics: input.diagnostics,
|
|
1713
|
+
sourceId: input.sourceId,
|
|
1714
|
+
unsupportedCode: "PSL_UNSUPPORTED_NAMED_TYPE_CONSTRUCTOR",
|
|
1715
|
+
unsupportedMessage: `Named type "${declaration.name}" references unsupported constructor "${helperPath}"`
|
|
1716
|
+
});
|
|
1717
|
+
if (!descriptor) continue;
|
|
1718
|
+
const storageType = instantiatePslTypeConstructor({
|
|
1719
|
+
call: typeConstructor,
|
|
1720
|
+
descriptor,
|
|
1721
|
+
diagnostics: input.diagnostics,
|
|
1722
|
+
sourceId: input.sourceId,
|
|
1723
|
+
entityLabel: `Named type "${declaration.name}"`
|
|
1724
|
+
});
|
|
1725
|
+
if (!storageType) continue;
|
|
1726
|
+
namedTypeDescriptors.set(declaration.name, toNamedTypeFieldDescriptor(declaration.name, storageType));
|
|
1727
|
+
storageTypeEntries.push([declaration.name, {
|
|
1728
|
+
kind: "codec-instance",
|
|
1729
|
+
codecId: storageType.codecId,
|
|
1730
|
+
nativeType: storageType.nativeType,
|
|
1731
|
+
typeParams: storageType.typeParams ?? {}
|
|
1732
|
+
}]);
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
1735
|
+
const baseType = declaration.baseType;
|
|
1736
|
+
if (baseType === void 0) {
|
|
1737
|
+
input.diagnostics.push({
|
|
1738
|
+
code: "PSL_UNSUPPORTED_NAMED_TYPE_BASE",
|
|
1739
|
+
message: `Named type "${declaration.name}" must declare a base type or constructor`,
|
|
1740
|
+
sourceId: input.sourceId,
|
|
1741
|
+
span: declaration.span
|
|
1742
|
+
});
|
|
1743
|
+
continue;
|
|
1744
|
+
}
|
|
1745
|
+
const baseDescriptor = input.enumTypeDescriptors.get(baseType) ?? input.scalarTypeDescriptors.get(baseType);
|
|
1746
|
+
if (!baseDescriptor) {
|
|
1747
|
+
input.diagnostics.push({
|
|
1748
|
+
code: "PSL_UNSUPPORTED_NAMED_TYPE_BASE",
|
|
1749
|
+
message: `Named type "${declaration.name}" references unsupported base type "${baseType}"`,
|
|
1750
|
+
sourceId: input.sourceId,
|
|
1751
|
+
span: declaration.span
|
|
1752
|
+
});
|
|
1753
|
+
continue;
|
|
1754
|
+
}
|
|
1755
|
+
const { dbNativeTypeAttribute, hasUnsupportedNamedTypeAttribute } = validateNamedTypeAttributes({
|
|
1756
|
+
declaration,
|
|
1757
|
+
sourceId: input.sourceId,
|
|
1758
|
+
diagnostics: input.diagnostics,
|
|
1759
|
+
composedExtensions: input.composedExtensions,
|
|
1760
|
+
authoringContributions: input.authoringContributions,
|
|
1761
|
+
allowDbNativeType: true,
|
|
1762
|
+
familyId: input.familyId,
|
|
1763
|
+
targetId: input.targetId
|
|
1764
|
+
});
|
|
1765
|
+
if (hasUnsupportedNamedTypeAttribute) continue;
|
|
1766
|
+
if (dbNativeTypeAttribute) {
|
|
1767
|
+
const descriptor = resolveDbNativeTypeAttribute({
|
|
1768
|
+
attribute: dbNativeTypeAttribute,
|
|
1769
|
+
baseType,
|
|
1770
|
+
baseDescriptor,
|
|
1771
|
+
diagnostics: input.diagnostics,
|
|
1772
|
+
sourceId: input.sourceId,
|
|
1773
|
+
entityLabel: `Named type "${declaration.name}"`
|
|
1774
|
+
});
|
|
1775
|
+
if (!descriptor) continue;
|
|
1776
|
+
namedTypeDescriptors.set(declaration.name, toNamedTypeFieldDescriptor(declaration.name, descriptor));
|
|
1777
|
+
storageTypeEntries.push([declaration.name, {
|
|
1778
|
+
kind: "codec-instance",
|
|
1779
|
+
codecId: descriptor.codecId,
|
|
1780
|
+
nativeType: descriptor.nativeType,
|
|
1781
|
+
typeParams: descriptor.typeParams ?? {}
|
|
1782
|
+
}]);
|
|
1783
|
+
continue;
|
|
1784
|
+
}
|
|
1785
|
+
const descriptor = toNamedTypeFieldDescriptor(declaration.name, baseDescriptor);
|
|
1786
|
+
namedTypeDescriptors.set(declaration.name, descriptor);
|
|
1787
|
+
storageTypeEntries.push([declaration.name, {
|
|
1788
|
+
kind: "codec-instance",
|
|
1789
|
+
codecId: baseDescriptor.codecId,
|
|
1790
|
+
nativeType: baseDescriptor.nativeType,
|
|
1791
|
+
typeParams: {}
|
|
1792
|
+
}]);
|
|
1793
|
+
}
|
|
1794
|
+
return {
|
|
1795
|
+
storageTypes: Object.fromEntries(storageTypeEntries),
|
|
1796
|
+
namedTypeDescriptors
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
//#endregion
|
|
1579
1800
|
//#region src/psl-relation-resolution.ts
|
|
1580
1801
|
const REFERENTIAL_ACTION_MAP = {
|
|
1581
1802
|
NoAction: "noAction",
|
|
@@ -1713,7 +1934,11 @@ function parseRelationAttribute(input) {
|
|
|
1713
1934
|
function indexFkRelations(input) {
|
|
1714
1935
|
const modelRelations = /* @__PURE__ */ new Map();
|
|
1715
1936
|
const fkRelationsByPair = /* @__PURE__ */ new Map();
|
|
1937
|
+
const fkRelationsByDeclaringModel = /* @__PURE__ */ new Map();
|
|
1716
1938
|
for (const relation of input.fkRelationMetadata) {
|
|
1939
|
+
const declaringFkRelations = fkRelationsByDeclaringModel.get(relation.declaringModelName);
|
|
1940
|
+
if (declaringFkRelations) declaringFkRelations.push(relation);
|
|
1941
|
+
else fkRelationsByDeclaringModel.set(relation.declaringModelName, [relation]);
|
|
1717
1942
|
const existing = modelRelations.get(relation.declaringModelName);
|
|
1718
1943
|
const current = existing ?? [];
|
|
1719
1944
|
if (!existing) modelRelations.set(relation.declaringModelName, current);
|
|
@@ -1740,15 +1965,177 @@ function indexFkRelations(input) {
|
|
|
1740
1965
|
}
|
|
1741
1966
|
return {
|
|
1742
1967
|
modelRelations,
|
|
1743
|
-
fkRelationsByPair
|
|
1968
|
+
fkRelationsByPair,
|
|
1969
|
+
fkRelationsByDeclaringModel
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
function idColumnsAreExactlyFkPair(idColumns, parentColumns, childColumns) {
|
|
1973
|
+
if (idColumns.length !== parentColumns.length + childColumns.length) return false;
|
|
1974
|
+
const fkColumns = new Set([...parentColumns, ...childColumns]);
|
|
1975
|
+
if (fkColumns.size !== parentColumns.length + childColumns.length) return false;
|
|
1976
|
+
return idColumns.every((column) => fkColumns.has(column));
|
|
1977
|
+
}
|
|
1978
|
+
/**
|
|
1979
|
+
* Reorders the child FK's junction columns into the target model's id-column
|
|
1980
|
+
* order. Returns undefined unless the FK references exactly the target's full
|
|
1981
|
+
* id, because downstream consumers pair `through.childColumns` positionally
|
|
1982
|
+
* against the target id columns — an FK referencing anything else (a non-id
|
|
1983
|
+
* unique, a partial id) would produce a silently wrong join.
|
|
1984
|
+
*/
|
|
1985
|
+
function childColumnsInTargetIdOrder(childFk, targetIdColumns) {
|
|
1986
|
+
if (childFk.referencedColumns.length !== targetIdColumns.length) return;
|
|
1987
|
+
const localByReferenced = /* @__PURE__ */ new Map();
|
|
1988
|
+
for (const [index, referencedColumn] of childFk.referencedColumns.entries()) {
|
|
1989
|
+
const localColumn = childFk.localColumns[index];
|
|
1990
|
+
if (localColumn === void 0) return;
|
|
1991
|
+
localByReferenced.set(referencedColumn, localColumn);
|
|
1992
|
+
}
|
|
1993
|
+
if (localByReferenced.size !== targetIdColumns.length) return;
|
|
1994
|
+
const ordered = [];
|
|
1995
|
+
for (const idColumn of targetIdColumns) {
|
|
1996
|
+
const localColumn = localByReferenced.get(idColumn);
|
|
1997
|
+
if (localColumn === void 0) return;
|
|
1998
|
+
ordered.push(localColumn);
|
|
1999
|
+
}
|
|
2000
|
+
return ordered;
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Finds explicit junction models that connect a bare backrelation list field
|
|
2004
|
+
* to its target model: a model whose composite id columns are exactly the FK
|
|
2005
|
+
* columns of one relation back to the candidate's model (the parent side) and
|
|
2006
|
+
* one relation to the candidate's target model (the child side). The child
|
|
2007
|
+
* FK must reference exactly the target model's id columns; its junction
|
|
2008
|
+
* columns are carried in target-id order on the pair. A relation name on the
|
|
2009
|
+
* list field pins the parent-side FK relation, which is how self-referential
|
|
2010
|
+
* many-to-many sides are disambiguated.
|
|
2011
|
+
*
|
|
2012
|
+
* Alongside the recognised pairs, returns junction-shaped near-misses (models
|
|
2013
|
+
* that link both sides but were declined) so the caller can emit a
|
|
2014
|
+
* junction-specific diagnostic instead of the generic orphaned-list message.
|
|
2015
|
+
*/
|
|
2016
|
+
function findJunctionFkPairs(input) {
|
|
2017
|
+
const targetIdColumns = input.modelIdColumns.get(input.candidate.targetModelName);
|
|
2018
|
+
if (!targetIdColumns || targetIdColumns.length === 0) return {
|
|
2019
|
+
pairs: [],
|
|
2020
|
+
nearMisses: []
|
|
2021
|
+
};
|
|
2022
|
+
const pairs = [];
|
|
2023
|
+
const nearMisses = [];
|
|
2024
|
+
for (const [junctionModelName, junctionFks] of input.fkRelationsByDeclaringModel) {
|
|
2025
|
+
const idColumns = input.modelIdColumns.get(junctionModelName);
|
|
2026
|
+
for (const parentFk of junctionFks) {
|
|
2027
|
+
if (parentFk.targetModelName !== input.candidate.modelName) continue;
|
|
2028
|
+
if (input.candidate.relationName !== void 0 && parentFk.relationName !== input.candidate.relationName) continue;
|
|
2029
|
+
for (const childFk of junctionFks) {
|
|
2030
|
+
if (childFk === parentFk || childFk.targetModelName !== input.candidate.targetModelName) continue;
|
|
2031
|
+
if (!idColumns || !idColumnsAreExactlyFkPair(idColumns, parentFk.localColumns, childFk.localColumns)) {
|
|
2032
|
+
nearMisses.push({
|
|
2033
|
+
junctionModelName,
|
|
2034
|
+
reason: "id-not-fk-covering"
|
|
2035
|
+
});
|
|
2036
|
+
continue;
|
|
2037
|
+
}
|
|
2038
|
+
const orderedChildColumns = childColumnsInTargetIdOrder(childFk, targetIdColumns);
|
|
2039
|
+
if (!orderedChildColumns) {
|
|
2040
|
+
nearMisses.push({
|
|
2041
|
+
junctionModelName,
|
|
2042
|
+
reason: "target-fk-not-id"
|
|
2043
|
+
});
|
|
2044
|
+
continue;
|
|
2045
|
+
}
|
|
2046
|
+
pairs.push({
|
|
2047
|
+
parentFk,
|
|
2048
|
+
childFk,
|
|
2049
|
+
childColumnsInTargetIdOrder: orderedChildColumns
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
return {
|
|
2055
|
+
pairs,
|
|
2056
|
+
nearMisses
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
function junctionNearMissDiagnostic(candidate, nearMiss, sourceId) {
|
|
2060
|
+
const listField = `${candidate.modelName}.${candidate.field.name}`;
|
|
2061
|
+
const data = {
|
|
2062
|
+
listField,
|
|
2063
|
+
junctionModel: nearMiss.junctionModelName,
|
|
2064
|
+
targetModel: candidate.targetModelName
|
|
2065
|
+
};
|
|
2066
|
+
if (nearMiss.reason === "target-fk-not-id") return {
|
|
2067
|
+
code: "PSL_JUNCTION_TARGET_FK_NOT_ID",
|
|
2068
|
+
message: `Backrelation list field "${listField}" found junction model "${nearMiss.junctionModelName}", but its foreign key to "${candidate.targetModelName}" does not reference "${candidate.targetModelName}"'s @id. The junction's target-side foreign key must reference "${candidate.targetModelName}"'s full @id columns for many-to-many recognition.`,
|
|
2069
|
+
sourceId,
|
|
2070
|
+
span: candidate.field.span,
|
|
2071
|
+
data
|
|
2072
|
+
};
|
|
2073
|
+
return {
|
|
2074
|
+
code: "PSL_JUNCTION_ID_NOT_FK_COVERING",
|
|
2075
|
+
message: `Backrelation list field "${listField}" found junction-shaped model "${nearMiss.junctionModelName}" linking "${candidate.modelName}" and "${candidate.targetModelName}", but its id does not cover exactly its foreign-key columns. Declare @@id([...]) on "${nearMiss.junctionModelName}" listing exactly the two foreign-key columns for many-to-many recognition.`,
|
|
2076
|
+
sourceId,
|
|
2077
|
+
span: candidate.field.span,
|
|
2078
|
+
data
|
|
1744
2079
|
};
|
|
1745
2080
|
}
|
|
2081
|
+
function manyToManyRelationNode(candidate, pair) {
|
|
2082
|
+
return {
|
|
2083
|
+
fieldName: candidate.field.name,
|
|
2084
|
+
toModel: pair.childFk.targetModelName,
|
|
2085
|
+
toTable: pair.childFk.targetTableName,
|
|
2086
|
+
...ifDefined("toNamespaceId", pair.childFk.targetNamespaceId),
|
|
2087
|
+
cardinality: "N:M",
|
|
2088
|
+
on: {
|
|
2089
|
+
parentTable: candidate.tableName,
|
|
2090
|
+
parentColumns: pair.parentFk.referencedColumns,
|
|
2091
|
+
childTable: pair.parentFk.declaringTableName,
|
|
2092
|
+
childColumns: pair.parentFk.localColumns
|
|
2093
|
+
},
|
|
2094
|
+
through: {
|
|
2095
|
+
table: pair.parentFk.declaringTableName,
|
|
2096
|
+
...ifDefined("namespaceId", pair.parentFk.declaringNamespaceId),
|
|
2097
|
+
parentColumns: pair.parentFk.localColumns,
|
|
2098
|
+
childColumns: pair.childColumnsInTargetIdOrder
|
|
2099
|
+
}
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
function relationsForModel(modelRelations, modelName) {
|
|
2103
|
+
const existing = modelRelations.get(modelName);
|
|
2104
|
+
if (existing) return existing;
|
|
2105
|
+
const created = [];
|
|
2106
|
+
modelRelations.set(modelName, created);
|
|
2107
|
+
return created;
|
|
2108
|
+
}
|
|
1746
2109
|
function applyBackrelationCandidates(input) {
|
|
1747
2110
|
for (const candidate of input.backrelationCandidates) {
|
|
1748
2111
|
const pairKey = fkRelationPairKey(candidate.targetModelName, candidate.modelName);
|
|
1749
2112
|
const pairMatches = input.fkRelationsByPair.get(pairKey) ?? [];
|
|
1750
2113
|
const matches = candidate.relationName ? pairMatches.filter((relation) => relation.relationName === candidate.relationName) : [...pairMatches];
|
|
1751
2114
|
if (matches.length === 0) {
|
|
2115
|
+
const { pairs: junctionPairs, nearMisses } = findJunctionFkPairs({
|
|
2116
|
+
candidate,
|
|
2117
|
+
fkRelationsByDeclaringModel: input.fkRelationsByDeclaringModel,
|
|
2118
|
+
modelIdColumns: input.modelIdColumns
|
|
2119
|
+
});
|
|
2120
|
+
const junctionPair = junctionPairs[0];
|
|
2121
|
+
if (junctionPairs.length === 1 && junctionPair) {
|
|
2122
|
+
relationsForModel(input.modelRelations, candidate.modelName).push(manyToManyRelationNode(candidate, junctionPair));
|
|
2123
|
+
continue;
|
|
2124
|
+
}
|
|
2125
|
+
if (junctionPairs.length > 1) {
|
|
2126
|
+
input.diagnostics.push({
|
|
2127
|
+
code: "PSL_AMBIGUOUS_BACKRELATION_LIST",
|
|
2128
|
+
message: `Backrelation list field "${candidate.modelName}.${candidate.field.name}" matches multiple junction FK pairs for a many-to-many relation. Add @relation(name: "...") (or @relation("...")) to the list field and the junction FK-side relation pointing back at "${candidate.modelName}" to disambiguate.`,
|
|
2129
|
+
sourceId: input.sourceId,
|
|
2130
|
+
span: candidate.field.span
|
|
2131
|
+
});
|
|
2132
|
+
continue;
|
|
2133
|
+
}
|
|
2134
|
+
const nearMiss = nearMisses[0];
|
|
2135
|
+
if (nearMiss) {
|
|
2136
|
+
input.diagnostics.push(junctionNearMissDiagnostic(candidate, nearMiss, input.sourceId));
|
|
2137
|
+
continue;
|
|
2138
|
+
}
|
|
1752
2139
|
input.diagnostics.push({
|
|
1753
2140
|
code: "PSL_ORPHANED_BACKRELATION_LIST",
|
|
1754
2141
|
message: `Backrelation list field "${candidate.modelName}.${candidate.field.name}" has no matching FK-side relation on model "${candidate.targetModelName}". Add @relation(fields: [...], references: [...]) on the FK-side relation or use an explicit join model for many-to-many.`,
|
|
@@ -1769,13 +2156,11 @@ function applyBackrelationCandidates(input) {
|
|
|
1769
2156
|
invariant(matches.length === 1, "Backrelation matching requires exactly one match");
|
|
1770
2157
|
const matched = matches[0];
|
|
1771
2158
|
assertDefined(matched, "Backrelation matching requires a defined relation match");
|
|
1772
|
-
|
|
1773
|
-
const current = existing ?? [];
|
|
1774
|
-
if (!existing) input.modelRelations.set(candidate.modelName, current);
|
|
1775
|
-
current.push({
|
|
2159
|
+
relationsForModel(input.modelRelations, candidate.modelName).push({
|
|
1776
2160
|
fieldName: candidate.field.name,
|
|
1777
2161
|
toModel: matched.declaringModelName,
|
|
1778
2162
|
toTable: matched.declaringTableName,
|
|
2163
|
+
...ifDefined("toNamespaceId", matched.declaringNamespaceId),
|
|
1779
2164
|
cardinality: "1:N",
|
|
1780
2165
|
on: {
|
|
1781
2166
|
parentTable: candidate.tableName,
|
|
@@ -1829,32 +2214,11 @@ function buildComposedExtensionPackRefs(target, extensionIds, extensionPackRefs
|
|
|
1829
2214
|
version: "0.0.1"
|
|
1830
2215
|
}]));
|
|
1831
2216
|
}
|
|
1832
|
-
function diagnosticDedupKey(diagnostic) {
|
|
1833
|
-
const span = diagnostic.span;
|
|
1834
|
-
const spanKey = span ? `${span.start.offset}:${span.end.offset}:${span.start.line}:${span.end.line}` : "";
|
|
1835
|
-
return `${diagnostic.code}\u0000${diagnostic.sourceId}\u0000${spanKey}\u0000${diagnostic.message}`;
|
|
1836
|
-
}
|
|
1837
|
-
function dedupeDiagnostics(diagnostics) {
|
|
1838
|
-
const seen = /* @__PURE__ */ new Map();
|
|
1839
|
-
for (const diagnostic of diagnostics) {
|
|
1840
|
-
const key = diagnosticDedupKey(diagnostic);
|
|
1841
|
-
if (!seen.has(key)) seen.set(key, diagnostic);
|
|
1842
|
-
}
|
|
1843
|
-
return [...seen.values()];
|
|
1844
|
-
}
|
|
1845
2217
|
function compareStrings(left, right) {
|
|
1846
2218
|
if (left < right) return -1;
|
|
1847
2219
|
if (left > right) return 1;
|
|
1848
2220
|
return 0;
|
|
1849
2221
|
}
|
|
1850
|
-
function mapParserDiagnostics(document) {
|
|
1851
|
-
return document.diagnostics.map((diagnostic) => ({
|
|
1852
|
-
code: diagnostic.code,
|
|
1853
|
-
message: diagnostic.message,
|
|
1854
|
-
sourceId: diagnostic.sourceId,
|
|
1855
|
-
span: diagnostic.span
|
|
1856
|
-
}));
|
|
1857
|
-
}
|
|
1858
2222
|
/**
|
|
1859
2223
|
* Name of the framework-parser synthesised bucket for top-level
|
|
1860
2224
|
* declarations. Re-declared here so the per-target dispatch does not
|
|
@@ -1916,240 +2280,69 @@ function resolveNamespaceIdForSqlTarget(input) {
|
|
|
1916
2280
|
}
|
|
1917
2281
|
function validateNamespaceBlocksForSqlTarget(input) {
|
|
1918
2282
|
if (input.targetId === "sqlite") {
|
|
1919
|
-
for (const namespace of input.namespaces) {
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
span: namespace.span
|
|
1926
|
-
});
|
|
1927
|
-
}
|
|
2283
|
+
for (const namespace of input.namespaces) input.diagnostics.push({
|
|
2284
|
+
code: "PSL_UNSUPPORTED_NAMESPACE_BLOCK",
|
|
2285
|
+
message: `SQLite does not support \`namespace ${namespace.name} { … }\` blocks (SQLite has no schema concept; declare models at the document top level instead).`,
|
|
2286
|
+
sourceId: input.sourceId,
|
|
2287
|
+
span: nodePslSpan(namespace.node.syntax, input.sourceFile)
|
|
2288
|
+
});
|
|
1928
2289
|
return;
|
|
1929
2290
|
}
|
|
1930
2291
|
if (input.targetId === "postgres") {
|
|
1931
|
-
const
|
|
1932
|
-
const
|
|
1933
|
-
const hasSibling = namedBlocks.some((ns) => ns.name !== "unbound");
|
|
2292
|
+
const hasUnbound = input.namespaces.some((ns) => ns.name === "unbound");
|
|
2293
|
+
const hasSibling = input.namespaces.some((ns) => ns.name !== "unbound");
|
|
1934
2294
|
if (hasUnbound && hasSibling) {
|
|
1935
|
-
const unboundBlock =
|
|
2295
|
+
const unboundBlock = input.namespaces.find((ns) => ns.name === "unbound");
|
|
1936
2296
|
input.diagnostics.push({
|
|
1937
2297
|
code: "PSL_RESERVED_NAMESPACE_NAME",
|
|
1938
2298
|
message: "Namespace \"unbound\" is reserved for the late-binding sentinel mapping and cannot appear alongside other named namespace blocks. Use `namespace unbound { … }` alone (no sibling named namespaces) for late-binding multi-tenant contracts.",
|
|
1939
2299
|
sourceId: input.sourceId,
|
|
1940
|
-
...
|
|
2300
|
+
...unboundBlock !== void 0 ? { span: nodePslSpan(unboundBlock.node.syntax, input.sourceFile) } : {}
|
|
1941
2301
|
});
|
|
1942
2302
|
}
|
|
1943
2303
|
}
|
|
1944
2304
|
}
|
|
1945
2305
|
function processEnumDeclarations(input) {
|
|
1946
|
-
const
|
|
2306
|
+
const enumHandles = {};
|
|
1947
2307
|
const enumTypeDescriptors = /* @__PURE__ */ new Map();
|
|
1948
|
-
if (input.
|
|
1949
|
-
|
|
2308
|
+
if (input.enumBlocks.length === 0) return {
|
|
2309
|
+
enumHandles,
|
|
1950
2310
|
enumTypeDescriptors
|
|
1951
2311
|
};
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
2312
|
+
const enumDescriptor = getAuthoringEntity(input.authoringContributions, ["enum"]);
|
|
2313
|
+
if (!enumDescriptor) {
|
|
2314
|
+
for (const decl of input.enumBlocks) input.diagnostics.push({
|
|
2315
|
+
code: "PSL_ENUM_MISSING_FACTORY",
|
|
2316
|
+
message: `enum "${decl.name}" requires an "enum" entityType factory in the active authoring contributions`,
|
|
1956
2317
|
sourceId: input.sourceId,
|
|
1957
|
-
span:
|
|
2318
|
+
span: decl.span
|
|
1958
2319
|
});
|
|
1959
2320
|
return {
|
|
1960
|
-
|
|
2321
|
+
enumHandles,
|
|
1961
2322
|
enumTypeDescriptors
|
|
1962
2323
|
};
|
|
1963
2324
|
}
|
|
1964
|
-
for (const
|
|
1965
|
-
const
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
});
|
|
1973
|
-
const values = enumDeclaration.values.map((value) => value.name);
|
|
1974
|
-
const constructed = instantiateAuthoringEntityType("enum", input.enumEntityDescriptor, [{
|
|
1975
|
-
name: enumDeclaration.name,
|
|
1976
|
-
nativeType,
|
|
1977
|
-
values
|
|
1978
|
-
}], input.entityContext);
|
|
1979
|
-
if (!isPostgresEnumStorageEntry(constructed)) {
|
|
1980
|
-
input.diagnostics.push({
|
|
1981
|
-
code: "PSL_UNSUPPORTED_NAMED_TYPE_BASE",
|
|
1982
|
-
message: `Enum "${enumDeclaration.name}": enum entity-type factory must return a PostgresEnumStorageEntry-shaped value (kind: 'postgres-enum')`,
|
|
1983
|
-
sourceId: input.sourceId,
|
|
1984
|
-
span: enumDeclaration.span
|
|
1985
|
-
});
|
|
1986
|
-
continue;
|
|
1987
|
-
}
|
|
1988
|
-
const descriptor = {
|
|
1989
|
-
codecId: constructed.codecId,
|
|
1990
|
-
nativeType: constructed.nativeType,
|
|
1991
|
-
typeRef: enumDeclaration.name
|
|
1992
|
-
};
|
|
1993
|
-
enumTypeDescriptors.set(enumDeclaration.name, descriptor);
|
|
1994
|
-
storageTypes[enumDeclaration.name] = constructed;
|
|
1995
|
-
}
|
|
1996
|
-
return {
|
|
1997
|
-
storageTypes,
|
|
1998
|
-
enumTypeDescriptors
|
|
1999
|
-
};
|
|
2000
|
-
}
|
|
2001
|
-
function validateNamedTypeAttributes(input) {
|
|
2002
|
-
const [dbNativeTypeAttribute, ...extraDbNativeTypeAttributes] = input.allowDbNativeType ? input.declaration.attributes.filter((attribute) => attribute.name.startsWith("db.")) : [];
|
|
2003
|
-
let hasUnsupportedNamedTypeAttribute = false;
|
|
2004
|
-
for (const extra of extraDbNativeTypeAttributes) {
|
|
2005
|
-
input.diagnostics.push({
|
|
2006
|
-
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
2007
|
-
message: `Named type "${input.declaration.name}" can declare at most one @db.* attribute`,
|
|
2008
|
-
sourceId: input.sourceId,
|
|
2009
|
-
span: extra.span
|
|
2010
|
-
});
|
|
2011
|
-
hasUnsupportedNamedTypeAttribute = true;
|
|
2012
|
-
}
|
|
2013
|
-
for (const attribute of input.declaration.attributes) {
|
|
2014
|
-
if (input.allowDbNativeType && attribute.name.startsWith("db.")) continue;
|
|
2015
|
-
const uncomposedNamespace = checkUncomposedNamespace(attribute.name, input.composedExtensions, {
|
|
2016
|
-
familyId: input.familyId,
|
|
2017
|
-
targetId: input.targetId,
|
|
2018
|
-
authoringContributions: input.authoringContributions
|
|
2019
|
-
});
|
|
2020
|
-
if (uncomposedNamespace) {
|
|
2021
|
-
reportUncomposedNamespace({
|
|
2022
|
-
subjectLabel: `Attribute "@${attribute.name}"`,
|
|
2023
|
-
namespace: uncomposedNamespace,
|
|
2024
|
-
sourceId: input.sourceId,
|
|
2025
|
-
span: attribute.span,
|
|
2026
|
-
diagnostics: input.diagnostics
|
|
2027
|
-
});
|
|
2028
|
-
hasUnsupportedNamedTypeAttribute = true;
|
|
2029
|
-
continue;
|
|
2030
|
-
}
|
|
2031
|
-
input.diagnostics.push({
|
|
2032
|
-
code: "PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE",
|
|
2033
|
-
message: `Named type "${input.declaration.name}" uses unsupported attribute "${attribute.name}"`,
|
|
2034
|
-
sourceId: input.sourceId,
|
|
2035
|
-
span: attribute.span
|
|
2325
|
+
for (const decl of input.enumBlocks) {
|
|
2326
|
+
const handle = instantiateAuthoringEntityType("enum", enumDescriptor, [decl], input.entityContext);
|
|
2327
|
+
if (handle === void 0 || handle === null) continue;
|
|
2328
|
+
const enumHandle = blindCast(handle);
|
|
2329
|
+
enumHandles[decl.name] = enumHandle;
|
|
2330
|
+
enumTypeDescriptors.set(decl.name, {
|
|
2331
|
+
codecId: enumHandle.codecId,
|
|
2332
|
+
nativeType: enumHandle.nativeType
|
|
2036
2333
|
});
|
|
2037
|
-
hasUnsupportedNamedTypeAttribute = true;
|
|
2038
2334
|
}
|
|
2039
2335
|
return {
|
|
2040
|
-
|
|
2041
|
-
|
|
2336
|
+
enumHandles,
|
|
2337
|
+
enumTypeDescriptors
|
|
2042
2338
|
};
|
|
2043
2339
|
}
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
const
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
declaration,
|
|
2051
|
-
sourceId: input.sourceId,
|
|
2052
|
-
diagnostics: input.diagnostics,
|
|
2053
|
-
composedExtensions: input.composedExtensions,
|
|
2054
|
-
authoringContributions: input.authoringContributions,
|
|
2055
|
-
allowDbNativeType: false,
|
|
2056
|
-
familyId: input.familyId,
|
|
2057
|
-
targetId: input.targetId
|
|
2058
|
-
});
|
|
2059
|
-
if (hasUnsupportedNamedTypeAttribute) continue;
|
|
2060
|
-
const helperPath = declaration.typeConstructor.path.join(".");
|
|
2061
|
-
const typeConstructor = resolvePslTypeConstructorDescriptor({
|
|
2062
|
-
call: declaration.typeConstructor,
|
|
2063
|
-
authoringContributions: input.authoringContributions,
|
|
2064
|
-
composedExtensions: input.composedExtensions,
|
|
2065
|
-
familyId: input.familyId,
|
|
2066
|
-
targetId: input.targetId,
|
|
2067
|
-
diagnostics: input.diagnostics,
|
|
2068
|
-
sourceId: input.sourceId,
|
|
2069
|
-
unsupportedCode: "PSL_UNSUPPORTED_NAMED_TYPE_CONSTRUCTOR",
|
|
2070
|
-
unsupportedMessage: `Named type "${declaration.name}" references unsupported constructor "${helperPath}"`
|
|
2071
|
-
});
|
|
2072
|
-
if (!typeConstructor) continue;
|
|
2073
|
-
const storageType = instantiatePslTypeConstructor({
|
|
2074
|
-
call: declaration.typeConstructor,
|
|
2075
|
-
descriptor: typeConstructor,
|
|
2076
|
-
diagnostics: input.diagnostics,
|
|
2077
|
-
sourceId: input.sourceId,
|
|
2078
|
-
entityLabel: `Named type "${declaration.name}"`
|
|
2079
|
-
});
|
|
2080
|
-
if (!storageType) continue;
|
|
2081
|
-
namedTypeDescriptors.set(declaration.name, toNamedTypeFieldDescriptor(declaration.name, storageType));
|
|
2082
|
-
storageTypes[declaration.name] = {
|
|
2083
|
-
kind: "codec-instance",
|
|
2084
|
-
codecId: storageType.codecId,
|
|
2085
|
-
nativeType: storageType.nativeType,
|
|
2086
|
-
typeParams: storageType.typeParams ?? {}
|
|
2087
|
-
};
|
|
2088
|
-
continue;
|
|
2089
|
-
}
|
|
2090
|
-
if (declaration.baseType === void 0) {
|
|
2091
|
-
input.diagnostics.push({
|
|
2092
|
-
code: "PSL_UNSUPPORTED_NAMED_TYPE_BASE",
|
|
2093
|
-
message: `Named type "${declaration.name}" must declare a base type or constructor`,
|
|
2094
|
-
sourceId: input.sourceId,
|
|
2095
|
-
span: declaration.span
|
|
2096
|
-
});
|
|
2097
|
-
continue;
|
|
2098
|
-
}
|
|
2099
|
-
const { baseType } = declaration;
|
|
2100
|
-
const baseDescriptor = input.enumTypeDescriptors.get(baseType) ?? input.scalarTypeDescriptors.get(baseType);
|
|
2101
|
-
if (!baseDescriptor) {
|
|
2102
|
-
input.diagnostics.push({
|
|
2103
|
-
code: "PSL_UNSUPPORTED_NAMED_TYPE_BASE",
|
|
2104
|
-
message: `Named type "${declaration.name}" references unsupported base type "${baseType}"`,
|
|
2105
|
-
sourceId: input.sourceId,
|
|
2106
|
-
span: declaration.span
|
|
2107
|
-
});
|
|
2108
|
-
continue;
|
|
2109
|
-
}
|
|
2110
|
-
const { dbNativeTypeAttribute, hasUnsupportedNamedTypeAttribute } = validateNamedTypeAttributes({
|
|
2111
|
-
declaration,
|
|
2112
|
-
sourceId: input.sourceId,
|
|
2113
|
-
diagnostics: input.diagnostics,
|
|
2114
|
-
composedExtensions: input.composedExtensions,
|
|
2115
|
-
authoringContributions: input.authoringContributions,
|
|
2116
|
-
allowDbNativeType: true,
|
|
2117
|
-
familyId: input.familyId,
|
|
2118
|
-
targetId: input.targetId
|
|
2119
|
-
});
|
|
2120
|
-
if (hasUnsupportedNamedTypeAttribute) continue;
|
|
2121
|
-
if (dbNativeTypeAttribute) {
|
|
2122
|
-
const descriptor = resolveDbNativeTypeAttribute({
|
|
2123
|
-
attribute: dbNativeTypeAttribute,
|
|
2124
|
-
baseType,
|
|
2125
|
-
baseDescriptor,
|
|
2126
|
-
diagnostics: input.diagnostics,
|
|
2127
|
-
sourceId: input.sourceId,
|
|
2128
|
-
entityLabel: `Named type "${declaration.name}"`
|
|
2129
|
-
});
|
|
2130
|
-
if (!descriptor) continue;
|
|
2131
|
-
namedTypeDescriptors.set(declaration.name, toNamedTypeFieldDescriptor(declaration.name, descriptor));
|
|
2132
|
-
storageTypes[declaration.name] = {
|
|
2133
|
-
kind: "codec-instance",
|
|
2134
|
-
codecId: descriptor.codecId,
|
|
2135
|
-
nativeType: descriptor.nativeType,
|
|
2136
|
-
typeParams: descriptor.typeParams ?? {}
|
|
2137
|
-
};
|
|
2138
|
-
continue;
|
|
2139
|
-
}
|
|
2140
|
-
const descriptor = toNamedTypeFieldDescriptor(declaration.name, baseDescriptor);
|
|
2141
|
-
namedTypeDescriptors.set(declaration.name, descriptor);
|
|
2142
|
-
storageTypes[declaration.name] = {
|
|
2143
|
-
kind: "codec-instance",
|
|
2144
|
-
codecId: baseDescriptor.codecId,
|
|
2145
|
-
nativeType: baseDescriptor.nativeType,
|
|
2146
|
-
typeParams: {}
|
|
2147
|
-
};
|
|
2148
|
-
}
|
|
2149
|
-
return {
|
|
2150
|
-
storageTypes,
|
|
2151
|
-
namedTypeDescriptors
|
|
2152
|
-
};
|
|
2340
|
+
/** Generic top-level blocks are supported only when a composed descriptor claims their keyword. */
|
|
2341
|
+
function composedBlockKeywords(authoringContributions) {
|
|
2342
|
+
const keywords = /* @__PURE__ */ new Set();
|
|
2343
|
+
const descriptors = authoringContributions?.pslBlockDescriptors ?? {};
|
|
2344
|
+
for (const [keyword, value] of Object.entries(descriptors)) if (isAuthoringPslBlockDescriptor(value)) keywords.add(keyword);
|
|
2345
|
+
return keywords;
|
|
2153
2346
|
}
|
|
2154
2347
|
function buildModelNodeFromPsl(input) {
|
|
2155
2348
|
const { model, mapping, sourceId, diagnostics } = input;
|
|
@@ -2169,7 +2362,8 @@ function buildModelNodeFromPsl(input) {
|
|
|
2169
2362
|
generatorDescriptorById: input.generatorDescriptorById,
|
|
2170
2363
|
diagnostics,
|
|
2171
2364
|
sourceId,
|
|
2172
|
-
scalarTypeDescriptors: input.scalarTypeDescriptors
|
|
2365
|
+
scalarTypeDescriptors: input.scalarTypeDescriptors,
|
|
2366
|
+
...ifDefined("enumHandles", input.enumHandles)
|
|
2173
2367
|
});
|
|
2174
2368
|
const inlineIdFields = resolvedFields.filter((field) => field.isId);
|
|
2175
2369
|
if (inlineIdFields.length > 1) diagnostics.push({
|
|
@@ -2188,7 +2382,7 @@ function buildModelNodeFromPsl(input) {
|
|
|
2188
2382
|
let controlPolicyDeclared = false;
|
|
2189
2383
|
let controlPolicy;
|
|
2190
2384
|
const resultBackrelationCandidates = [];
|
|
2191
|
-
for (const field of model.fields) {
|
|
2385
|
+
for (const field of Object.values(model.fields)) {
|
|
2192
2386
|
if (!field.list || !input.modelNames.has(field.typeName)) continue;
|
|
2193
2387
|
const attributesValid = validateNavigationListFieldAttributes({
|
|
2194
2388
|
modelName: model.name,
|
|
@@ -2240,7 +2434,7 @@ function buildModelNodeFromPsl(input) {
|
|
|
2240
2434
|
...ifDefined("relationName", relationName)
|
|
2241
2435
|
});
|
|
2242
2436
|
}
|
|
2243
|
-
const relationAttributes = model.fields.map((field) => ({
|
|
2437
|
+
const relationAttributes = Object.values(model.fields).map((field) => ({
|
|
2244
2438
|
field,
|
|
2245
2439
|
relation: getAttribute(field.attributes, "relation")
|
|
2246
2440
|
})).filter((entry) => Boolean(entry.relation));
|
|
@@ -2311,7 +2505,7 @@ function buildModelNodeFromPsl(input) {
|
|
|
2311
2505
|
});
|
|
2312
2506
|
continue;
|
|
2313
2507
|
}
|
|
2314
|
-
const nullableFieldName = fieldNames.find((name) => model.fields
|
|
2508
|
+
const nullableFieldName = fieldNames.find((name) => model.fields[name]?.optional === true);
|
|
2315
2509
|
if (nullableFieldName !== void 0) {
|
|
2316
2510
|
diagnostics.push({
|
|
2317
2511
|
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
@@ -2698,6 +2892,7 @@ function buildModelNodeFromPsl(input) {
|
|
|
2698
2892
|
declaringModelName: model.name,
|
|
2699
2893
|
declaringFieldName: relationAttribute.field.name,
|
|
2700
2894
|
declaringTableName: tableName,
|
|
2895
|
+
...ifDefined("declaringNamespaceId", input.modelNamespaceIds.get(model.name)),
|
|
2701
2896
|
targetModelName: targetMapping.model.name,
|
|
2702
2897
|
targetTableName: targetMapping.tableName,
|
|
2703
2898
|
...ifDefined("targetNamespaceId", targetNamespaceId),
|
|
@@ -2710,14 +2905,18 @@ function buildModelNodeFromPsl(input) {
|
|
|
2710
2905
|
modelNode: {
|
|
2711
2906
|
modelName: model.name,
|
|
2712
2907
|
tableName,
|
|
2713
|
-
fields: resolvedFields.map((resolvedField) =>
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2908
|
+
fields: resolvedFields.map((resolvedField) => {
|
|
2909
|
+
const enumHandle = input.enumHandles?.get(resolvedField.field.typeName);
|
|
2910
|
+
return {
|
|
2911
|
+
fieldName: resolvedField.field.name,
|
|
2912
|
+
columnName: resolvedField.columnName,
|
|
2913
|
+
descriptor: resolvedField.descriptor,
|
|
2914
|
+
nullable: resolvedField.nullable,
|
|
2915
|
+
...ifDefined("default", resolvedField.defaultValue),
|
|
2916
|
+
...ifDefined("executionDefaults", resolvedField.executionDefaults),
|
|
2917
|
+
...ifDefined("enumTypeHandle", enumHandle)
|
|
2918
|
+
};
|
|
2919
|
+
}),
|
|
2721
2920
|
...ifDefined("id", primaryKey),
|
|
2722
2921
|
...uniqueConstraints.length > 0 ? { uniques: uniqueConstraints } : {},
|
|
2723
2922
|
...indexNodes.length > 0 ? { indexes: indexNodes } : {},
|
|
@@ -2736,7 +2935,7 @@ function buildValueObjects(input) {
|
|
|
2736
2935
|
const compositeTypeNames = new Set(compositeTypes.map((ct) => ct.name));
|
|
2737
2936
|
for (const compositeType of compositeTypes) {
|
|
2738
2937
|
const fields = {};
|
|
2739
|
-
for (const field of compositeType.fields) {
|
|
2938
|
+
for (const field of Object.values(compositeType.fields)) {
|
|
2740
2939
|
if (compositeTypeNames.has(field.typeName)) {
|
|
2741
2940
|
const result = {
|
|
2742
2941
|
type: {
|
|
@@ -2842,7 +3041,7 @@ function collectPolymorphismDeclarations(models, sourceId, diagnostics) {
|
|
|
2842
3041
|
});
|
|
2843
3042
|
continue;
|
|
2844
3043
|
}
|
|
2845
|
-
const discField = model.fields
|
|
3044
|
+
const discField = model.fields[fieldName];
|
|
2846
3045
|
if (discField && discField.typeName !== "String") {
|
|
2847
3046
|
diagnostics.push({
|
|
2848
3047
|
code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
|
|
@@ -3147,7 +3346,7 @@ function stripStorageOnlyDomainFields(model, fieldNames) {
|
|
|
3147
3346
|
};
|
|
3148
3347
|
}
|
|
3149
3348
|
function interpretPslDocumentToSqlContract(input) {
|
|
3150
|
-
const sourceId = input.
|
|
3349
|
+
const sourceId = input.sourceId;
|
|
3151
3350
|
if (!input.target) return notOk({
|
|
3152
3351
|
summary: "PSL to SQL contract interpretation failed",
|
|
3153
3352
|
diagnostics: [{
|
|
@@ -3164,22 +3363,27 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
3164
3363
|
sourceId
|
|
3165
3364
|
}]
|
|
3166
3365
|
});
|
|
3167
|
-
const
|
|
3366
|
+
const { topLevel } = input.symbolTable;
|
|
3367
|
+
const sourceFile = input.sourceFile;
|
|
3368
|
+
const namespaceSymbols = Object.values(topLevel.namespaces);
|
|
3369
|
+
const diagnostics = [...input.seedDiagnostics ?? []];
|
|
3168
3370
|
validateNamespaceBlocksForSqlTarget({
|
|
3169
|
-
namespaces:
|
|
3371
|
+
namespaces: namespaceSymbols,
|
|
3170
3372
|
targetId: input.target.targetId,
|
|
3171
3373
|
sourceId,
|
|
3374
|
+
sourceFile,
|
|
3172
3375
|
diagnostics
|
|
3173
3376
|
});
|
|
3174
3377
|
const models = [];
|
|
3175
3378
|
const modelEntries = [];
|
|
3176
3379
|
const modelNamespaceIds = /* @__PURE__ */ new Map();
|
|
3177
|
-
|
|
3380
|
+
const compositeTypes = [];
|
|
3381
|
+
const collectScope = (bucketName, scopeModels, scopeCompositeTypes) => {
|
|
3178
3382
|
const resolvedNamespaceId = resolveNamespaceIdForSqlTarget({
|
|
3179
|
-
bucketName
|
|
3383
|
+
bucketName,
|
|
3180
3384
|
targetId: input.target.targetId
|
|
3181
3385
|
});
|
|
3182
|
-
for (const model of
|
|
3386
|
+
for (const model of scopeModels) {
|
|
3183
3387
|
models.push(model);
|
|
3184
3388
|
modelEntries.push({
|
|
3185
3389
|
model,
|
|
@@ -3187,21 +3391,11 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
3187
3391
|
});
|
|
3188
3392
|
if (resolvedNamespaceId !== void 0) modelNamespaceIds.set(model.name, resolvedNamespaceId);
|
|
3189
3393
|
}
|
|
3190
|
-
|
|
3394
|
+
for (const compositeType of scopeCompositeTypes) compositeTypes.push(compositeType);
|
|
3395
|
+
};
|
|
3396
|
+
collectScope(UNSPECIFIED_PSL_NAMESPACE_NAME, Object.values(topLevel.models), Object.values(topLevel.compositeTypes));
|
|
3397
|
+
for (const namespace of namespaceSymbols) collectScope(namespace.name, Object.values(namespace.models), Object.values(namespace.compositeTypes));
|
|
3191
3398
|
const defaultNamespaceId = input.target.defaultNamespaceId;
|
|
3192
|
-
const topLevelEnums = input.document.ast.namespaces.filter((ns) => ns.name === UNSPECIFIED_PSL_NAMESPACE_NAME).flatMap((ns) => ns.enums);
|
|
3193
|
-
const namedNamespaceEnumsByNsId = /* @__PURE__ */ new Map();
|
|
3194
|
-
for (const ns of input.document.ast.namespaces) {
|
|
3195
|
-
if (ns.name === UNSPECIFIED_PSL_NAMESPACE_NAME || ns.enums.length === 0) continue;
|
|
3196
|
-
const resolvedId = resolveNamespaceIdForSqlTarget({
|
|
3197
|
-
bucketName: ns.name,
|
|
3198
|
-
targetId: input.target.targetId
|
|
3199
|
-
});
|
|
3200
|
-
if (resolvedId === void 0) continue;
|
|
3201
|
-
const existing = namedNamespaceEnumsByNsId.get(resolvedId) ?? [];
|
|
3202
|
-
namedNamespaceEnumsByNsId.set(resolvedId, [...existing, ...ns.enums]);
|
|
3203
|
-
}
|
|
3204
|
-
const compositeTypes = input.document.ast.namespaces.flatMap((ns) => ns.compositeTypes);
|
|
3205
3399
|
const modelNames = new Set(models.map((model) => model.name));
|
|
3206
3400
|
const compositeTypeNames = new Set(compositeTypes.map((ct) => ct.name));
|
|
3207
3401
|
const composedExtensions = new Set(input.composedExtensionPacks ?? []);
|
|
@@ -3210,35 +3404,55 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
3210
3404
|
const generatorDescriptors = input.controlMutationDefaults?.generatorDescriptors ?? [];
|
|
3211
3405
|
const generatorDescriptorById = /* @__PURE__ */ new Map();
|
|
3212
3406
|
for (const descriptor of generatorDescriptors) generatorDescriptorById.set(descriptor.id, descriptor);
|
|
3213
|
-
const
|
|
3214
|
-
const
|
|
3215
|
-
|
|
3216
|
-
|
|
3407
|
+
const isEnumBlock = (block) => block.keyword === "enum";
|
|
3408
|
+
const legitimateBlockKeywords = composedBlockKeywords(input.authoringContributions);
|
|
3409
|
+
const reportUnsupportedTopLevelBlock = (block) => {
|
|
3410
|
+
diagnostics.push({
|
|
3411
|
+
code: "PSL_UNSUPPORTED_TOP_LEVEL_BLOCK",
|
|
3412
|
+
message: `Unsupported top-level block "${block.keyword}"`,
|
|
3413
|
+
sourceId,
|
|
3414
|
+
span: keywordPslSpan(block.node.syntax, block.keyword, sourceFile)
|
|
3415
|
+
});
|
|
3217
3416
|
};
|
|
3417
|
+
const topLevelEnums = Object.values(topLevel.blocks).filter((block) => {
|
|
3418
|
+
if (!legitimateBlockKeywords.has(block.keyword)) {
|
|
3419
|
+
reportUnsupportedTopLevelBlock(block);
|
|
3420
|
+
return false;
|
|
3421
|
+
}
|
|
3422
|
+
return isEnumBlock(block);
|
|
3423
|
+
}).map((block) => block.block);
|
|
3424
|
+
for (const namespace of namespaceSymbols) for (const block of Object.values(namespace.blocks)) {
|
|
3425
|
+
if (isEnumBlock(block)) {
|
|
3426
|
+
diagnostics.push({
|
|
3427
|
+
code: "PSL_ENUM_NAMESPACE_NOT_SUPPORTED",
|
|
3428
|
+
message: `enum "${block.name}" inside namespace "${namespace.name}" is not supported; declare enum at the top level`,
|
|
3429
|
+
sourceId,
|
|
3430
|
+
span: nodePslSpan(block.node.syntax, sourceFile)
|
|
3431
|
+
});
|
|
3432
|
+
continue;
|
|
3433
|
+
}
|
|
3434
|
+
if (!legitimateBlockKeywords.has(block.keyword)) reportUnsupportedTopLevelBlock(block);
|
|
3435
|
+
}
|
|
3218
3436
|
const enumResult = processEnumDeclarations({
|
|
3219
|
-
|
|
3437
|
+
enumBlocks: topLevelEnums,
|
|
3220
3438
|
sourceId,
|
|
3221
|
-
|
|
3222
|
-
entityContext:
|
|
3439
|
+
authoringContributions: input.authoringContributions,
|
|
3440
|
+
entityContext: {
|
|
3441
|
+
family: input.target.familyId,
|
|
3442
|
+
target: input.target.targetId,
|
|
3443
|
+
...ifDefined("codecLookup", input.codecLookup),
|
|
3444
|
+
sourceId,
|
|
3445
|
+
diagnostics: { push: (d) => {
|
|
3446
|
+
diagnostics.push(blindCast(d));
|
|
3447
|
+
} }
|
|
3448
|
+
},
|
|
3223
3449
|
diagnostics
|
|
3224
3450
|
});
|
|
3225
3451
|
const allEnumTypeDescriptors = new Map(enumResult.enumTypeDescriptors);
|
|
3226
|
-
const
|
|
3227
|
-
|
|
3228
|
-
const nsEnumResult = processEnumDeclarations({
|
|
3229
|
-
enums: nsEnums,
|
|
3230
|
-
sourceId,
|
|
3231
|
-
enumEntityDescriptor,
|
|
3232
|
-
entityContext: enumEntityContext,
|
|
3233
|
-
diagnostics
|
|
3234
|
-
});
|
|
3235
|
-
for (const [name, descriptor] of nsEnumResult.enumTypeDescriptors) allEnumTypeDescriptors.set(name, descriptor);
|
|
3236
|
-
const nsEntries = {};
|
|
3237
|
-
for (const [name, entry] of Object.entries(nsEnumResult.storageTypes)) if (isPostgresEnumStorageEntry(entry)) nsEntries[name] = entry;
|
|
3238
|
-
if (Object.keys(nsEntries).length > 0) namespaceEnumStorageTypes[nsId] = nsEntries;
|
|
3239
|
-
}
|
|
3452
|
+
const validEnumHandles = { ...enumResult.enumHandles };
|
|
3453
|
+
const enumHandlesByName = new Map(Object.entries(validEnumHandles));
|
|
3240
3454
|
const namedTypeResult = resolveNamedTypeDeclarations({
|
|
3241
|
-
declarations:
|
|
3455
|
+
declarations: [...Object.values(topLevel.scalars), ...Object.values(topLevel.typeAliases)],
|
|
3242
3456
|
sourceId,
|
|
3243
3457
|
enumTypeDescriptors: allEnumTypeDescriptors,
|
|
3244
3458
|
scalarTypeDescriptors: input.scalarTypeDescriptors,
|
|
@@ -3248,10 +3462,7 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
3248
3462
|
authoringContributions: input.authoringContributions,
|
|
3249
3463
|
diagnostics
|
|
3250
3464
|
});
|
|
3251
|
-
const storageTypes = {
|
|
3252
|
-
...enumResult.storageTypes,
|
|
3253
|
-
...namedTypeResult.storageTypes
|
|
3254
|
-
};
|
|
3465
|
+
const storageTypes = { ...namedTypeResult.storageTypes };
|
|
3255
3466
|
const modelMappingsByCoordinate = buildModelMappings(modelEntries, defaultNamespaceId, diagnostics, sourceId);
|
|
3256
3467
|
const modelMappings = /* @__PURE__ */ new Map();
|
|
3257
3468
|
for (const mapping of modelMappingsByCoordinate.values()) modelMappings.set(mapping.model.name, mapping);
|
|
@@ -3283,7 +3494,8 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
3283
3494
|
scalarTypeDescriptors: input.scalarTypeDescriptors,
|
|
3284
3495
|
sourceId,
|
|
3285
3496
|
diagnostics,
|
|
3286
|
-
modelNamespaceIds
|
|
3497
|
+
modelNamespaceIds,
|
|
3498
|
+
...enumHandlesByName.size > 0 ? { enumHandles: enumHandlesByName } : {}
|
|
3287
3499
|
});
|
|
3288
3500
|
modelNodes.push(namespaceId !== void 0 ? {
|
|
3289
3501
|
...result.modelNode,
|
|
@@ -3297,10 +3509,14 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
3297
3509
|
crossSpaceRelationsByModel.set(model.name, [...existing, ...result.crossSpaceRelations]);
|
|
3298
3510
|
}
|
|
3299
3511
|
}
|
|
3300
|
-
const { modelRelations, fkRelationsByPair } = indexFkRelations({ fkRelationMetadata });
|
|
3512
|
+
const { modelRelations, fkRelationsByPair, fkRelationsByDeclaringModel } = indexFkRelations({ fkRelationMetadata });
|
|
3513
|
+
const modelIdColumns = /* @__PURE__ */ new Map();
|
|
3514
|
+
for (const modelNode of modelNodes) if (modelNode.id) modelIdColumns.set(modelNode.modelName, modelNode.id.columns);
|
|
3301
3515
|
applyBackrelationCandidates({
|
|
3302
3516
|
backrelationCandidates,
|
|
3303
3517
|
fkRelationsByPair,
|
|
3518
|
+
fkRelationsByDeclaringModel,
|
|
3519
|
+
modelIdColumns,
|
|
3304
3520
|
modelRelations,
|
|
3305
3521
|
diagnostics,
|
|
3306
3522
|
sourceId
|
|
@@ -3329,13 +3545,13 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
3329
3545
|
});
|
|
3330
3546
|
if (diagnostics.length > 0) return notOk({
|
|
3331
3547
|
summary: "PSL to SQL contract interpretation failed",
|
|
3332
|
-
diagnostics
|
|
3548
|
+
diagnostics
|
|
3333
3549
|
});
|
|
3334
3550
|
const contract = buildSqlContractFromDefinition({
|
|
3335
3551
|
target: input.target,
|
|
3336
3552
|
...ifDefined("extensionPacks", buildComposedExtensionPackRefs(input.target, [...composedExtensions].sort(compareStrings), input.composedExtensionPackRefs)),
|
|
3337
3553
|
...Object.keys(storageTypes).length > 0 ? { storageTypes } : {},
|
|
3338
|
-
...Object.keys(
|
|
3554
|
+
...Object.keys(validEnumHandles).length > 0 ? { enums: validEnumHandles } : {},
|
|
3339
3555
|
...ifDefined("createNamespace", input.createNamespace),
|
|
3340
3556
|
models: stiColumnModelNodes.map((model) => ({
|
|
3341
3557
|
...model,
|
|
@@ -3345,7 +3561,7 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
3345
3561
|
const modelsForPatch = {};
|
|
3346
3562
|
for (const [namespaceId, namespaceSlice] of Object.entries(contract.domain.namespaces)) for (const [modelName, model] of Object.entries(namespaceSlice.models)) {
|
|
3347
3563
|
const coordinate = modelCoordinateKey(namespaceId, modelName);
|
|
3348
|
-
|
|
3564
|
+
invariant(!Object.hasOwn(modelsForPatch, coordinate), `symbol table guarantees coordinate uniqueness; duplicate model "${namespaceId}.${modelName}" reached interpretation`);
|
|
3349
3565
|
modelsForPatch[coordinate] = model;
|
|
3350
3566
|
}
|
|
3351
3567
|
let patchedModels = patchModelDomainFields(modelsForPatch, modelResolvedFields);
|
|
@@ -3362,6 +3578,7 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
3362
3578
|
roots: filteredRoots,
|
|
3363
3579
|
domain: { namespaces: Object.fromEntries(Object.entries(contract.domain.namespaces).map(([namespaceId, namespaceSlice]) => [namespaceId, {
|
|
3364
3580
|
models: Object.fromEntries(Object.entries(namespaceSlice.models).map(([modelName, model]) => [modelName, patchedModels[modelCoordinateKey(namespaceId, modelName)] ?? model])),
|
|
3581
|
+
...namespaceSlice.enum !== void 0 ? { enum: namespaceSlice.enum } : {},
|
|
3365
3582
|
...namespaceSlice.valueObjects !== void 0 ? { valueObjects: namespaceSlice.valueObjects } : {},
|
|
3366
3583
|
...namespaceId === input.target.defaultNamespaceId && Object.keys(valueObjects).length > 0 ? { valueObjects } : {}
|
|
3367
3584
|
}])) }
|
|
@@ -3370,4 +3587,4 @@ function interpretPslDocumentToSqlContract(input) {
|
|
|
3370
3587
|
//#endregion
|
|
3371
3588
|
export { interpretPslDocumentToSqlContract as t };
|
|
3372
3589
|
|
|
3373
|
-
//# sourceMappingURL=interpreter-
|
|
3590
|
+
//# sourceMappingURL=interpreter-CygvamTk.mjs.map
|