@prisma-next/sql-contract-psl 0.14.0-dev.8 → 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.
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{interpreter-kDkm5opL.mjs → interpreter-CygvamTk.mjs} +176 -7
- package/dist/interpreter-CygvamTk.mjs.map +1 -0
- package/dist/provider.mjs +1 -1
- package/package.json +12 -12
- package/src/interpreter.ts +12 -1
- package/src/psl-relation-resolution.ts +245 -7
- package/dist/interpreter-kDkm5opL.mjs.map +0 -1
package/dist/index.d.mts.map
CHANGED
|
@@ -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,
|
|
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-
|
|
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
|
-
|
|
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-
|
|
3590
|
+
//# sourceMappingURL=interpreter-CygvamTk.mjs.map
|