@prisma-next/sql-contract-psl 0.14.0-dev.7 → 0.14.0-dev.9

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 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/psl-column-resolution.ts","../src/interpreter.ts"],"mappings":";;;;;;;;;;;;;KA2CY,gBAAA;EAAA,SACD,OAAA;EAAA,SACA,UAAA;EAAA,SACA,OAAA;EAAA,SACA,UAAA,GAAa,MAAM;AAAA;;;UCuDb,sCAAA;EAAA,SACN,WAAA,EAAa,WAAA;EAAA,SACb,UAAA,EAAY,UAAA;EAAA,SACZ,QAAA;EAAA,SACA,MAAA,EAAQ,aAAA;EAAA,SACR,qBAAA,EAAuB,WAAA,SAAoB,gBAAA;EAAA,SAC3C,sBAAA;EAAA,SACA,yBAAA,YAAqC,gBAAA;EAAA,SACrC,uBAAA,GAA0B,yBAAA;EAAA,SAC1B,sBAAA,GAAyB,sBAAA;EDhEN;AAAA;;;;ACuD9B;;;;EDvD8B,SC0EnB,0BAAA,EAA4B,WAAA,SAAoB,QAAA;EAfxC;;;;;;;;;EAAA,SAyBR,eAAA,IAAmB,KAAA,EAAO,uBAAA,KAA4B,SAAA;EAAA,SACtD,WAAA,GAAc,WAAA;EAAA,SACd,eAAA,YAA2B,wBAAA;AAAA;AAAA,iBAo9CtB,iCAAA,CACd,KAAA,EAAO,sCAAA,GACN,MAAA,CAAO,QAAA,EAAU,yBAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/psl-column-resolution.ts","../src/interpreter.ts"],"mappings":";;;;;;;;;;;;;KA2CY,gBAAA;EAAA,SACD,OAAA;EAAA,SACA,UAAA;EAAA,SACA,OAAA;EAAA,SACA,UAAA,GAAa,MAAM;AAAA;;;UCuDb,sCAAA;EAAA,SACN,WAAA,EAAa,WAAA;EAAA,SACb,UAAA,EAAY,UAAA;EAAA,SACZ,QAAA;EAAA,SACA,MAAA,EAAQ,aAAA;EAAA,SACR,qBAAA,EAAuB,WAAA,SAAoB,gBAAA;EAAA,SAC3C,sBAAA;EAAA,SACA,yBAAA,YAAqC,gBAAA;EAAA,SACrC,uBAAA,GAA0B,yBAAA;EAAA,SAC1B,sBAAA,GAAyB,sBAAA;EDhEN;AAAA;;;;ACuD9B;;;;EDvD8B,SC0EnB,0BAAA,EAA4B,WAAA,SAAoB,QAAA;EAfxC;;;;;;;;;EAAA,SAyBR,eAAA,IAAmB,KAAA,EAAO,uBAAA,KAA4B,SAAA;EAAA,SACtD,WAAA,GAAc,WAAA;EAAA,SACd,eAAA,YAA2B,wBAAA;AAAA;AAAA,iBAq9CtB,iCAAA,CACd,KAAA,EAAO,sCAAA,GACN,MAAA,CAAO,QAAA,EAAU,yBAAA"}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as interpretPslDocumentToSqlContract } from "./interpreter-kDkm5opL.mjs";
1
+ import { t as interpretPslDocumentToSqlContract } from "./interpreter-CygvamTk.mjs";
2
2
  export { interpretPslDocumentToSqlContract };
@@ -1934,7 +1934,11 @@ function parseRelationAttribute(input) {
1934
1934
  function indexFkRelations(input) {
1935
1935
  const modelRelations = /* @__PURE__ */ new Map();
1936
1936
  const fkRelationsByPair = /* @__PURE__ */ new Map();
1937
+ const fkRelationsByDeclaringModel = /* @__PURE__ */ new Map();
1937
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]);
1938
1942
  const existing = modelRelations.get(relation.declaringModelName);
1939
1943
  const current = existing ?? [];
1940
1944
  if (!existing) modelRelations.set(relation.declaringModelName, current);
@@ -1961,15 +1965,177 @@ function indexFkRelations(input) {
1961
1965
  }
1962
1966
  return {
1963
1967
  modelRelations,
1964
- fkRelationsByPair
1968
+ fkRelationsByPair,
1969
+ fkRelationsByDeclaringModel
1965
1970
  };
1966
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
2079
+ };
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
+ }
1967
2109
  function applyBackrelationCandidates(input) {
1968
2110
  for (const candidate of input.backrelationCandidates) {
1969
2111
  const pairKey = fkRelationPairKey(candidate.targetModelName, candidate.modelName);
1970
2112
  const pairMatches = input.fkRelationsByPair.get(pairKey) ?? [];
1971
2113
  const matches = candidate.relationName ? pairMatches.filter((relation) => relation.relationName === candidate.relationName) : [...pairMatches];
1972
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
+ }
1973
2139
  input.diagnostics.push({
1974
2140
  code: "PSL_ORPHANED_BACKRELATION_LIST",
1975
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.`,
@@ -1990,13 +2156,11 @@ function applyBackrelationCandidates(input) {
1990
2156
  invariant(matches.length === 1, "Backrelation matching requires exactly one match");
1991
2157
  const matched = matches[0];
1992
2158
  assertDefined(matched, "Backrelation matching requires a defined relation match");
1993
- const existing = input.modelRelations.get(candidate.modelName);
1994
- const current = existing ?? [];
1995
- if (!existing) input.modelRelations.set(candidate.modelName, current);
1996
- current.push({
2159
+ relationsForModel(input.modelRelations, candidate.modelName).push({
1997
2160
  fieldName: candidate.field.name,
1998
2161
  toModel: matched.declaringModelName,
1999
2162
  toTable: matched.declaringTableName,
2163
+ ...ifDefined("toNamespaceId", matched.declaringNamespaceId),
2000
2164
  cardinality: "1:N",
2001
2165
  on: {
2002
2166
  parentTable: candidate.tableName,
@@ -2728,6 +2892,7 @@ function buildModelNodeFromPsl(input) {
2728
2892
  declaringModelName: model.name,
2729
2893
  declaringFieldName: relationAttribute.field.name,
2730
2894
  declaringTableName: tableName,
2895
+ ...ifDefined("declaringNamespaceId", input.modelNamespaceIds.get(model.name)),
2731
2896
  targetModelName: targetMapping.model.name,
2732
2897
  targetTableName: targetMapping.tableName,
2733
2898
  ...ifDefined("targetNamespaceId", targetNamespaceId),
@@ -3344,10 +3509,14 @@ function interpretPslDocumentToSqlContract(input) {
3344
3509
  crossSpaceRelationsByModel.set(model.name, [...existing, ...result.crossSpaceRelations]);
3345
3510
  }
3346
3511
  }
3347
- 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);
3348
3515
  applyBackrelationCandidates({
3349
3516
  backrelationCandidates,
3350
3517
  fkRelationsByPair,
3518
+ fkRelationsByDeclaringModel,
3519
+ modelIdColumns,
3351
3520
  modelRelations,
3352
3521
  diagnostics,
3353
3522
  sourceId
@@ -3418,4 +3587,4 @@ function interpretPslDocumentToSqlContract(input) {
3418
3587
  //#endregion
3419
3588
  export { interpretPslDocumentToSqlContract as t };
3420
3589
 
3421
- //# sourceMappingURL=interpreter-kDkm5opL.mjs.map
3590
+ //# sourceMappingURL=interpreter-CygvamTk.mjs.map