@prisma-next/sql-contract-psl 0.14.0-dev.4 → 0.14.0-dev.6

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.
@@ -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 { namespacePslExtensionBlocks } from "@prisma-next/framework-components/psl-ast";
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 instantiatePslFieldPreset(input) {
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 = instantiatePslFieldPreset({
982
+ const instantiated = instantiateFieldPreset({
977
983
  call: input.field.typeConstructor,
978
984
  descriptor: presetDescriptor,
979
985
  diagnostics: input.diagnostics,
@@ -1301,7 +1307,6 @@ 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);
@@ -1309,8 +1314,18 @@ function resolveColumnDescriptor(field, enumTypeDescriptors, namedTypeDescriptor
1309
1314
  //#endregion
1310
1315
  //#region src/psl-field-resolution.ts
1311
1316
  function lowerEnumDefaultForField(input) {
1312
- const expressionEntry = getPositionalArgumentEntry(input.defaultAttribute);
1313
- if (!expressionEntry) return {};
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
+ }
1314
1329
  const raw = expressionEntry.value.trim();
1315
1330
  const isQuotedString = /^(['"]).*\1$/.test(raw);
1316
1331
  const isFunctionCall = raw.includes("(") && raw.endsWith(")");
@@ -1414,7 +1429,7 @@ function extractFieldConstraintNames(input) {
1414
1429
  function collectResolvedFields(input) {
1415
1430
  const { model, mapping, enumTypeDescriptors, namedTypeDescriptors, modelNames, compositeTypeNames, composedExtensions, authoringContributions, familyId, targetId, defaultFunctionRegistry, generatorDescriptorById, diagnostics, sourceId, scalarTypeDescriptors, enumHandles } = input;
1416
1431
  const resolvedFields = [];
1417
- for (const field of model.fields) {
1432
+ for (const field of Object.values(model.fields)) {
1418
1433
  const isModelField = modelNames.has(field.typeName);
1419
1434
  if (field.list && isModelField) continue;
1420
1435
  validateFieldAttributes({
@@ -1534,7 +1549,7 @@ function collectResolvedFields(input) {
1534
1549
  });
1535
1550
  continue;
1536
1551
  }
1537
- const fieldUsesNamedType = field.typeRef !== void 0 || namedTypeDescriptors.has(field.typeName);
1552
+ const fieldUsesNamedType = namedTypeDescriptors.has(field.typeName);
1538
1553
  if (loweredOnCreate && !fieldUsesNamedType) {
1539
1554
  const generatedDescriptor = generatorDescriptorById.get(loweredOnCreate.id)?.resolveGeneratedColumnDescriptor?.({ generated: loweredOnCreate });
1540
1555
  if (generatedDescriptor) descriptor = generatedDescriptor;
@@ -1571,6 +1586,7 @@ function collectResolvedFields(input) {
1571
1586
  field,
1572
1587
  columnName: mappedColumnName,
1573
1588
  descriptor,
1589
+ nullable: presetContributions?.nullable ?? field.optional,
1574
1590
  ...ifDefined("defaultValue", fieldDefaultValue),
1575
1591
  ...ifDefined("executionDefaults", fieldExecutionDefaults),
1576
1592
  isId: isIdField || Boolean(presetContributions?.id),
@@ -1596,7 +1612,7 @@ function buildModelMappings(modelEntries, defaultNamespaceId, diagnostics, sourc
1596
1612
  span: model.span
1597
1613
  });
1598
1614
  const fieldColumns = /* @__PURE__ */ new Map();
1599
- for (const field of model.fields) {
1615
+ for (const field of Object.values(model.fields)) {
1600
1616
  const columnName = parseMapName({
1601
1617
  attribute: getAttribute(field.attributes, "map"),
1602
1618
  defaultValue: field.name,
@@ -1616,6 +1632,171 @@ function buildModelMappings(modelEntries, defaultNamespaceId, diagnostics, sourc
1616
1632
  return result;
1617
1633
  }
1618
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
1619
1800
  //#region src/psl-relation-resolution.ts
1620
1801
  const REFERENTIAL_ACTION_MAP = {
1621
1802
  NoAction: "noAction",
@@ -1869,32 +2050,11 @@ function buildComposedExtensionPackRefs(target, extensionIds, extensionPackRefs
1869
2050
  version: "0.0.1"
1870
2051
  }]));
1871
2052
  }
1872
- function diagnosticDedupKey(diagnostic) {
1873
- const span = diagnostic.span;
1874
- const spanKey = span ? `${span.start.offset}:${span.end.offset}:${span.start.line}:${span.end.line}` : "";
1875
- return `${diagnostic.code}\u0000${diagnostic.sourceId}\u0000${spanKey}\u0000${diagnostic.message}`;
1876
- }
1877
- function dedupeDiagnostics(diagnostics) {
1878
- const seen = /* @__PURE__ */ new Map();
1879
- for (const diagnostic of diagnostics) {
1880
- const key = diagnosticDedupKey(diagnostic);
1881
- if (!seen.has(key)) seen.set(key, diagnostic);
1882
- }
1883
- return [...seen.values()];
1884
- }
1885
2053
  function compareStrings(left, right) {
1886
2054
  if (left < right) return -1;
1887
2055
  if (left > right) return 1;
1888
2056
  return 0;
1889
2057
  }
1890
- function mapParserDiagnostics(document) {
1891
- return document.diagnostics.map((diagnostic) => ({
1892
- code: diagnostic.code,
1893
- message: diagnostic.message,
1894
- sourceId: diagnostic.sourceId,
1895
- span: diagnostic.span
1896
- }));
1897
- }
1898
2058
  /**
1899
2059
  * Name of the framework-parser synthesised bucket for top-level
1900
2060
  * declarations. Re-declared here so the per-target dispatch does not
@@ -1956,28 +2116,24 @@ function resolveNamespaceIdForSqlTarget(input) {
1956
2116
  }
1957
2117
  function validateNamespaceBlocksForSqlTarget(input) {
1958
2118
  if (input.targetId === "sqlite") {
1959
- for (const namespace of input.namespaces) {
1960
- if (namespace.name === UNSPECIFIED_PSL_NAMESPACE_NAME) continue;
1961
- input.diagnostics.push({
1962
- code: "PSL_UNSUPPORTED_NAMESPACE_BLOCK",
1963
- message: `SQLite does not support \`namespace ${namespace.name} { … }\` blocks (SQLite has no schema concept; declare models at the document top level instead).`,
1964
- sourceId: input.sourceId,
1965
- span: namespace.span
1966
- });
1967
- }
2119
+ for (const namespace of input.namespaces) input.diagnostics.push({
2120
+ code: "PSL_UNSUPPORTED_NAMESPACE_BLOCK",
2121
+ message: `SQLite does not support \`namespace ${namespace.name} { … }\` blocks (SQLite has no schema concept; declare models at the document top level instead).`,
2122
+ sourceId: input.sourceId,
2123
+ span: nodePslSpan(namespace.node.syntax, input.sourceFile)
2124
+ });
1968
2125
  return;
1969
2126
  }
1970
2127
  if (input.targetId === "postgres") {
1971
- const namedBlocks = input.namespaces.filter((ns) => ns.name !== UNSPECIFIED_PSL_NAMESPACE_NAME);
1972
- const hasUnbound = namedBlocks.some((ns) => ns.name === "unbound");
1973
- const hasSibling = namedBlocks.some((ns) => ns.name !== "unbound");
2128
+ const hasUnbound = input.namespaces.some((ns) => ns.name === "unbound");
2129
+ const hasSibling = input.namespaces.some((ns) => ns.name !== "unbound");
1974
2130
  if (hasUnbound && hasSibling) {
1975
- const unboundBlock = namedBlocks.find((ns) => ns.name === "unbound");
2131
+ const unboundBlock = input.namespaces.find((ns) => ns.name === "unbound");
1976
2132
  input.diagnostics.push({
1977
2133
  code: "PSL_RESERVED_NAMESPACE_NAME",
1978
2134
  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.",
1979
2135
  sourceId: input.sourceId,
1980
- ...ifDefined("span", unboundBlock?.span)
2136
+ ...unboundBlock !== void 0 ? { span: nodePslSpan(unboundBlock.node.syntax, input.sourceFile) } : {}
1981
2137
  });
1982
2138
  }
1983
2139
  }
@@ -2017,158 +2173,12 @@ function processEnumDeclarations(input) {
2017
2173
  enumTypeDescriptors
2018
2174
  };
2019
2175
  }
2020
- function validateNamedTypeAttributes(input) {
2021
- const [dbNativeTypeAttribute, ...extraDbNativeTypeAttributes] = input.allowDbNativeType ? input.declaration.attributes.filter((attribute) => attribute.name.startsWith("db.")) : [];
2022
- let hasUnsupportedNamedTypeAttribute = false;
2023
- for (const extra of extraDbNativeTypeAttributes) {
2024
- input.diagnostics.push({
2025
- code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
2026
- message: `Named type "${input.declaration.name}" can declare at most one @db.* attribute`,
2027
- sourceId: input.sourceId,
2028
- span: extra.span
2029
- });
2030
- hasUnsupportedNamedTypeAttribute = true;
2031
- }
2032
- for (const attribute of input.declaration.attributes) {
2033
- if (input.allowDbNativeType && attribute.name.startsWith("db.")) continue;
2034
- const uncomposedNamespace = checkUncomposedNamespace(attribute.name, input.composedExtensions, {
2035
- familyId: input.familyId,
2036
- targetId: input.targetId,
2037
- authoringContributions: input.authoringContributions
2038
- });
2039
- if (uncomposedNamespace) {
2040
- reportUncomposedNamespace({
2041
- subjectLabel: `Attribute "@${attribute.name}"`,
2042
- namespace: uncomposedNamespace,
2043
- sourceId: input.sourceId,
2044
- span: attribute.span,
2045
- diagnostics: input.diagnostics
2046
- });
2047
- hasUnsupportedNamedTypeAttribute = true;
2048
- continue;
2049
- }
2050
- input.diagnostics.push({
2051
- code: "PSL_UNSUPPORTED_NAMED_TYPE_ATTRIBUTE",
2052
- message: `Named type "${input.declaration.name}" uses unsupported attribute "${attribute.name}"`,
2053
- sourceId: input.sourceId,
2054
- span: attribute.span
2055
- });
2056
- hasUnsupportedNamedTypeAttribute = true;
2057
- }
2058
- return {
2059
- dbNativeTypeAttribute,
2060
- hasUnsupportedNamedTypeAttribute
2061
- };
2062
- }
2063
- function resolveNamedTypeDeclarations(input) {
2064
- const storageTypes = {};
2065
- const namedTypeDescriptors = /* @__PURE__ */ new Map();
2066
- for (const declaration of input.declarations) {
2067
- if (declaration.typeConstructor) {
2068
- const { hasUnsupportedNamedTypeAttribute } = validateNamedTypeAttributes({
2069
- declaration,
2070
- sourceId: input.sourceId,
2071
- diagnostics: input.diagnostics,
2072
- composedExtensions: input.composedExtensions,
2073
- authoringContributions: input.authoringContributions,
2074
- allowDbNativeType: false,
2075
- familyId: input.familyId,
2076
- targetId: input.targetId
2077
- });
2078
- if (hasUnsupportedNamedTypeAttribute) continue;
2079
- const helperPath = declaration.typeConstructor.path.join(".");
2080
- const typeConstructor = resolvePslTypeConstructorDescriptor({
2081
- call: declaration.typeConstructor,
2082
- authoringContributions: input.authoringContributions,
2083
- composedExtensions: input.composedExtensions,
2084
- familyId: input.familyId,
2085
- targetId: input.targetId,
2086
- diagnostics: input.diagnostics,
2087
- sourceId: input.sourceId,
2088
- unsupportedCode: "PSL_UNSUPPORTED_NAMED_TYPE_CONSTRUCTOR",
2089
- unsupportedMessage: `Named type "${declaration.name}" references unsupported constructor "${helperPath}"`
2090
- });
2091
- if (!typeConstructor) continue;
2092
- const storageType = instantiatePslTypeConstructor({
2093
- call: declaration.typeConstructor,
2094
- descriptor: typeConstructor,
2095
- diagnostics: input.diagnostics,
2096
- sourceId: input.sourceId,
2097
- entityLabel: `Named type "${declaration.name}"`
2098
- });
2099
- if (!storageType) continue;
2100
- namedTypeDescriptors.set(declaration.name, toNamedTypeFieldDescriptor(declaration.name, storageType));
2101
- storageTypes[declaration.name] = {
2102
- kind: "codec-instance",
2103
- codecId: storageType.codecId,
2104
- nativeType: storageType.nativeType,
2105
- typeParams: storageType.typeParams ?? {}
2106
- };
2107
- continue;
2108
- }
2109
- if (declaration.baseType === void 0) {
2110
- input.diagnostics.push({
2111
- code: "PSL_UNSUPPORTED_NAMED_TYPE_BASE",
2112
- message: `Named type "${declaration.name}" must declare a base type or constructor`,
2113
- sourceId: input.sourceId,
2114
- span: declaration.span
2115
- });
2116
- continue;
2117
- }
2118
- const { baseType } = declaration;
2119
- const baseDescriptor = input.enumTypeDescriptors.get(baseType) ?? input.scalarTypeDescriptors.get(baseType);
2120
- if (!baseDescriptor) {
2121
- input.diagnostics.push({
2122
- code: "PSL_UNSUPPORTED_NAMED_TYPE_BASE",
2123
- message: `Named type "${declaration.name}" references unsupported base type "${baseType}"`,
2124
- sourceId: input.sourceId,
2125
- span: declaration.span
2126
- });
2127
- continue;
2128
- }
2129
- const { dbNativeTypeAttribute, hasUnsupportedNamedTypeAttribute } = validateNamedTypeAttributes({
2130
- declaration,
2131
- sourceId: input.sourceId,
2132
- diagnostics: input.diagnostics,
2133
- composedExtensions: input.composedExtensions,
2134
- authoringContributions: input.authoringContributions,
2135
- allowDbNativeType: true,
2136
- familyId: input.familyId,
2137
- targetId: input.targetId
2138
- });
2139
- if (hasUnsupportedNamedTypeAttribute) continue;
2140
- if (dbNativeTypeAttribute) {
2141
- const descriptor = resolveDbNativeTypeAttribute({
2142
- attribute: dbNativeTypeAttribute,
2143
- baseType,
2144
- baseDescriptor,
2145
- diagnostics: input.diagnostics,
2146
- sourceId: input.sourceId,
2147
- entityLabel: `Named type "${declaration.name}"`
2148
- });
2149
- if (!descriptor) continue;
2150
- namedTypeDescriptors.set(declaration.name, toNamedTypeFieldDescriptor(declaration.name, descriptor));
2151
- storageTypes[declaration.name] = {
2152
- kind: "codec-instance",
2153
- codecId: descriptor.codecId,
2154
- nativeType: descriptor.nativeType,
2155
- typeParams: descriptor.typeParams ?? {}
2156
- };
2157
- continue;
2158
- }
2159
- const descriptor = toNamedTypeFieldDescriptor(declaration.name, baseDescriptor);
2160
- namedTypeDescriptors.set(declaration.name, descriptor);
2161
- storageTypes[declaration.name] = {
2162
- kind: "codec-instance",
2163
- codecId: baseDescriptor.codecId,
2164
- nativeType: baseDescriptor.nativeType,
2165
- typeParams: {}
2166
- };
2167
- }
2168
- return {
2169
- storageTypes,
2170
- namedTypeDescriptors
2171
- };
2176
+ /** Generic top-level blocks are supported only when a composed descriptor claims their keyword. */
2177
+ function composedBlockKeywords(authoringContributions) {
2178
+ const keywords = /* @__PURE__ */ new Set();
2179
+ const descriptors = authoringContributions?.pslBlockDescriptors ?? {};
2180
+ for (const [keyword, value] of Object.entries(descriptors)) if (isAuthoringPslBlockDescriptor(value)) keywords.add(keyword);
2181
+ return keywords;
2172
2182
  }
2173
2183
  function buildModelNodeFromPsl(input) {
2174
2184
  const { model, mapping, sourceId, diagnostics } = input;
@@ -2208,7 +2218,7 @@ function buildModelNodeFromPsl(input) {
2208
2218
  let controlPolicyDeclared = false;
2209
2219
  let controlPolicy;
2210
2220
  const resultBackrelationCandidates = [];
2211
- for (const field of model.fields) {
2221
+ for (const field of Object.values(model.fields)) {
2212
2222
  if (!field.list || !input.modelNames.has(field.typeName)) continue;
2213
2223
  const attributesValid = validateNavigationListFieldAttributes({
2214
2224
  modelName: model.name,
@@ -2260,7 +2270,7 @@ function buildModelNodeFromPsl(input) {
2260
2270
  ...ifDefined("relationName", relationName)
2261
2271
  });
2262
2272
  }
2263
- const relationAttributes = model.fields.map((field) => ({
2273
+ const relationAttributes = Object.values(model.fields).map((field) => ({
2264
2274
  field,
2265
2275
  relation: getAttribute(field.attributes, "relation")
2266
2276
  })).filter((entry) => Boolean(entry.relation));
@@ -2331,7 +2341,7 @@ function buildModelNodeFromPsl(input) {
2331
2341
  });
2332
2342
  continue;
2333
2343
  }
2334
- const nullableFieldName = fieldNames.find((name) => model.fields.find((f) => f.name === name)?.optional === true);
2344
+ const nullableFieldName = fieldNames.find((name) => model.fields[name]?.optional === true);
2335
2345
  if (nullableFieldName !== void 0) {
2336
2346
  diagnostics.push({
2337
2347
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
@@ -2736,7 +2746,7 @@ function buildModelNodeFromPsl(input) {
2736
2746
  fieldName: resolvedField.field.name,
2737
2747
  columnName: resolvedField.columnName,
2738
2748
  descriptor: resolvedField.descriptor,
2739
- nullable: resolvedField.field.optional,
2749
+ nullable: resolvedField.nullable,
2740
2750
  ...ifDefined("default", resolvedField.defaultValue),
2741
2751
  ...ifDefined("executionDefaults", resolvedField.executionDefaults),
2742
2752
  ...ifDefined("enumTypeHandle", enumHandle)
@@ -2760,7 +2770,7 @@ function buildValueObjects(input) {
2760
2770
  const compositeTypeNames = new Set(compositeTypes.map((ct) => ct.name));
2761
2771
  for (const compositeType of compositeTypes) {
2762
2772
  const fields = {};
2763
- for (const field of compositeType.fields) {
2773
+ for (const field of Object.values(compositeType.fields)) {
2764
2774
  if (compositeTypeNames.has(field.typeName)) {
2765
2775
  const result = {
2766
2776
  type: {
@@ -2866,7 +2876,7 @@ function collectPolymorphismDeclarations(models, sourceId, diagnostics) {
2866
2876
  });
2867
2877
  continue;
2868
2878
  }
2869
- const discField = model.fields.find((f) => f.name === fieldName);
2879
+ const discField = model.fields[fieldName];
2870
2880
  if (discField && discField.typeName !== "String") {
2871
2881
  diagnostics.push({
2872
2882
  code: "PSL_INVALID_ATTRIBUTE_ARGUMENT",
@@ -3171,7 +3181,7 @@ function stripStorageOnlyDomainFields(model, fieldNames) {
3171
3181
  };
3172
3182
  }
3173
3183
  function interpretPslDocumentToSqlContract(input) {
3174
- const sourceId = input.document.ast.sourceId;
3184
+ const sourceId = input.sourceId;
3175
3185
  if (!input.target) return notOk({
3176
3186
  summary: "PSL to SQL contract interpretation failed",
3177
3187
  diagnostics: [{
@@ -3188,22 +3198,27 @@ function interpretPslDocumentToSqlContract(input) {
3188
3198
  sourceId
3189
3199
  }]
3190
3200
  });
3191
- const diagnostics = mapParserDiagnostics(input.document);
3201
+ const { topLevel } = input.symbolTable;
3202
+ const sourceFile = input.sourceFile;
3203
+ const namespaceSymbols = Object.values(topLevel.namespaces);
3204
+ const diagnostics = [...input.seedDiagnostics ?? []];
3192
3205
  validateNamespaceBlocksForSqlTarget({
3193
- namespaces: input.document.ast.namespaces,
3206
+ namespaces: namespaceSymbols,
3194
3207
  targetId: input.target.targetId,
3195
3208
  sourceId,
3209
+ sourceFile,
3196
3210
  diagnostics
3197
3211
  });
3198
3212
  const models = [];
3199
3213
  const modelEntries = [];
3200
3214
  const modelNamespaceIds = /* @__PURE__ */ new Map();
3201
- for (const namespace of input.document.ast.namespaces) {
3215
+ const compositeTypes = [];
3216
+ const collectScope = (bucketName, scopeModels, scopeCompositeTypes) => {
3202
3217
  const resolvedNamespaceId = resolveNamespaceIdForSqlTarget({
3203
- bucketName: namespace.name,
3218
+ bucketName,
3204
3219
  targetId: input.target.targetId
3205
3220
  });
3206
- for (const model of namespace.models) {
3221
+ for (const model of scopeModels) {
3207
3222
  models.push(model);
3208
3223
  modelEntries.push({
3209
3224
  model,
@@ -3211,9 +3226,11 @@ function interpretPslDocumentToSqlContract(input) {
3211
3226
  });
3212
3227
  if (resolvedNamespaceId !== void 0) modelNamespaceIds.set(model.name, resolvedNamespaceId);
3213
3228
  }
3214
- }
3229
+ for (const compositeType of scopeCompositeTypes) compositeTypes.push(compositeType);
3230
+ };
3231
+ collectScope(UNSPECIFIED_PSL_NAMESPACE_NAME, Object.values(topLevel.models), Object.values(topLevel.compositeTypes));
3232
+ for (const namespace of namespaceSymbols) collectScope(namespace.name, Object.values(namespace.models), Object.values(namespace.compositeTypes));
3215
3233
  const defaultNamespaceId = input.target.defaultNamespaceId;
3216
- const compositeTypes = input.document.ast.namespaces.flatMap((ns) => ns.compositeTypes);
3217
3234
  const modelNames = new Set(models.map((model) => model.name));
3218
3235
  const compositeTypeNames = new Set(compositeTypes.map((ct) => ct.name));
3219
3236
  const composedExtensions = new Set(input.composedExtensionPacks ?? []);
@@ -3222,17 +3239,34 @@ function interpretPslDocumentToSqlContract(input) {
3222
3239
  const generatorDescriptors = input.controlMutationDefaults?.generatorDescriptors ?? [];
3223
3240
  const generatorDescriptorById = /* @__PURE__ */ new Map();
3224
3241
  for (const descriptor of generatorDescriptors) generatorDescriptorById.set(descriptor.id, descriptor);
3225
- const topLevelEnums = input.document.ast.namespaces.filter((ns) => ns.name === UNSPECIFIED_PSL_NAMESPACE_NAME).flatMap((ns) => namespacePslExtensionBlocks(ns).filter((b) => b.kind === "enum"));
3226
- for (const ns of input.document.ast.namespaces) {
3227
- if (ns.name === UNSPECIFIED_PSL_NAMESPACE_NAME) continue;
3228
- const nsEnums = namespacePslExtensionBlocks(ns).filter((b) => b.kind === "enum");
3229
- if (nsEnums.length === 0) continue;
3230
- for (const decl of nsEnums) diagnostics.push({
3231
- code: "PSL_ENUM_NAMESPACE_NOT_SUPPORTED",
3232
- message: `enum "${decl.name}" inside namespace "${ns.name}" is not supported; declare enum at the top level`,
3242
+ const isEnumBlock = (block) => block.keyword === "enum";
3243
+ const legitimateBlockKeywords = composedBlockKeywords(input.authoringContributions);
3244
+ const reportUnsupportedTopLevelBlock = (block) => {
3245
+ diagnostics.push({
3246
+ code: "PSL_UNSUPPORTED_TOP_LEVEL_BLOCK",
3247
+ message: `Unsupported top-level block "${block.keyword}"`,
3233
3248
  sourceId,
3234
- span: decl.span
3249
+ span: keywordPslSpan(block.node.syntax, block.keyword, sourceFile)
3235
3250
  });
3251
+ };
3252
+ const topLevelEnums = Object.values(topLevel.blocks).filter((block) => {
3253
+ if (!legitimateBlockKeywords.has(block.keyword)) {
3254
+ reportUnsupportedTopLevelBlock(block);
3255
+ return false;
3256
+ }
3257
+ return isEnumBlock(block);
3258
+ }).map((block) => block.block);
3259
+ for (const namespace of namespaceSymbols) for (const block of Object.values(namespace.blocks)) {
3260
+ if (isEnumBlock(block)) {
3261
+ diagnostics.push({
3262
+ code: "PSL_ENUM_NAMESPACE_NOT_SUPPORTED",
3263
+ message: `enum "${block.name}" inside namespace "${namespace.name}" is not supported; declare enum at the top level`,
3264
+ sourceId,
3265
+ span: nodePslSpan(block.node.syntax, sourceFile)
3266
+ });
3267
+ continue;
3268
+ }
3269
+ if (!legitimateBlockKeywords.has(block.keyword)) reportUnsupportedTopLevelBlock(block);
3236
3270
  }
3237
3271
  const enumResult = processEnumDeclarations({
3238
3272
  enumBlocks: topLevelEnums,
@@ -3253,7 +3287,7 @@ function interpretPslDocumentToSqlContract(input) {
3253
3287
  const validEnumHandles = { ...enumResult.enumHandles };
3254
3288
  const enumHandlesByName = new Map(Object.entries(validEnumHandles));
3255
3289
  const namedTypeResult = resolveNamedTypeDeclarations({
3256
- declarations: input.document.ast.types?.declarations ?? [],
3290
+ declarations: [...Object.values(topLevel.scalars), ...Object.values(topLevel.typeAliases)],
3257
3291
  sourceId,
3258
3292
  enumTypeDescriptors: allEnumTypeDescriptors,
3259
3293
  scalarTypeDescriptors: input.scalarTypeDescriptors,
@@ -3342,7 +3376,7 @@ function interpretPslDocumentToSqlContract(input) {
3342
3376
  });
3343
3377
  if (diagnostics.length > 0) return notOk({
3344
3378
  summary: "PSL to SQL contract interpretation failed",
3345
- diagnostics: dedupeDiagnostics(diagnostics)
3379
+ diagnostics
3346
3380
  });
3347
3381
  const contract = buildSqlContractFromDefinition({
3348
3382
  target: input.target,
@@ -3358,7 +3392,7 @@ function interpretPslDocumentToSqlContract(input) {
3358
3392
  const modelsForPatch = {};
3359
3393
  for (const [namespaceId, namespaceSlice] of Object.entries(contract.domain.namespaces)) for (const [modelName, model] of Object.entries(namespaceSlice.models)) {
3360
3394
  const coordinate = modelCoordinateKey(namespaceId, modelName);
3361
- if (Object.hasOwn(modelsForPatch, coordinate)) throw new Error(`duplicate model "${namespaceId}.${modelName}" during PSL interpretation`);
3395
+ invariant(!Object.hasOwn(modelsForPatch, coordinate), `symbol table guarantees coordinate uniqueness; duplicate model "${namespaceId}.${modelName}" reached interpretation`);
3362
3396
  modelsForPatch[coordinate] = model;
3363
3397
  }
3364
3398
  let patchedModels = patchModelDomainFields(modelsForPatch, modelResolvedFields);
@@ -3384,4 +3418,4 @@ function interpretPslDocumentToSqlContract(input) {
3384
3418
  //#endregion
3385
3419
  export { interpretPslDocumentToSqlContract as t };
3386
3420
 
3387
- //# sourceMappingURL=interpreter-B0BsCLKT.mjs.map
3421
+ //# sourceMappingURL=interpreter-kDkm5opL.mjs.map