@prisma-next/sql-contract-psl 0.12.0-dev.78 → 0.12.0-dev.79
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-B5_yovSP.mjs → interpreter-B_KtZusL.mjs} +50 -33
- package/dist/interpreter-B_KtZusL.mjs.map +1 -0
- package/dist/provider.mjs +1 -1
- package/package.json +12 -12
- package/src/interpreter.ts +78 -36
- package/src/psl-field-resolution.ts +22 -3
- package/src/psl-relation-resolution.ts +3 -0
- package/dist/interpreter-B5_yovSP.mjs.map +0 -1
package/dist/provider.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as interpretPslDocumentToSqlContract } from "./interpreter-
|
|
1
|
+
import { t as interpretPslDocumentToSqlContract } from "./interpreter-B_KtZusL.mjs";
|
|
2
2
|
import { ifDefined } from "@prisma-next/utils/defined";
|
|
3
3
|
import { notOk, ok } from "@prisma-next/utils/result";
|
|
4
4
|
import { parsePslDocument } from "@prisma-next/psl-parser";
|
package/package.json
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/sql-contract-psl",
|
|
3
|
-
"version": "0.12.0-dev.
|
|
3
|
+
"version": "0.12.0-dev.79",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"description": "PSL-to-SQL ContractIR interpreter for Prisma Next",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@prisma-next/config": "0.12.0-dev.
|
|
10
|
-
"@prisma-next/contract": "0.12.0-dev.
|
|
11
|
-
"@prisma-next/framework-components": "0.12.0-dev.
|
|
12
|
-
"@prisma-next/psl-parser": "0.12.0-dev.
|
|
13
|
-
"@prisma-next/sql-contract": "0.12.0-dev.
|
|
14
|
-
"@prisma-next/sql-contract-ts": "0.12.0-dev.
|
|
15
|
-
"@prisma-next/utils": "0.12.0-dev.
|
|
9
|
+
"@prisma-next/config": "0.12.0-dev.79",
|
|
10
|
+
"@prisma-next/contract": "0.12.0-dev.79",
|
|
11
|
+
"@prisma-next/framework-components": "0.12.0-dev.79",
|
|
12
|
+
"@prisma-next/psl-parser": "0.12.0-dev.79",
|
|
13
|
+
"@prisma-next/sql-contract": "0.12.0-dev.79",
|
|
14
|
+
"@prisma-next/sql-contract-ts": "0.12.0-dev.79",
|
|
15
|
+
"@prisma-next/utils": "0.12.0-dev.79",
|
|
16
16
|
"pathe": "^2.0.3"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@prisma-next/contract-authoring": "0.12.0-dev.
|
|
20
|
-
"@prisma-next/test-utils": "0.12.0-dev.
|
|
21
|
-
"@prisma-next/tsconfig": "0.12.0-dev.
|
|
22
|
-
"@prisma-next/tsdown": "0.12.0-dev.
|
|
19
|
+
"@prisma-next/contract-authoring": "0.12.0-dev.79",
|
|
20
|
+
"@prisma-next/test-utils": "0.12.0-dev.79",
|
|
21
|
+
"@prisma-next/tsconfig": "0.12.0-dev.79",
|
|
22
|
+
"@prisma-next/tsdown": "0.12.0-dev.79",
|
|
23
23
|
"arktype": "^2.2.0",
|
|
24
24
|
"tsdown": "0.22.1",
|
|
25
25
|
"typescript": "5.9.3",
|
package/src/interpreter.ts
CHANGED
|
@@ -82,6 +82,8 @@ import {
|
|
|
82
82
|
buildModelMappings,
|
|
83
83
|
collectResolvedFields,
|
|
84
84
|
type ModelNameMapping,
|
|
85
|
+
type ModelNamespaceEntry,
|
|
86
|
+
modelCoordinateKey,
|
|
85
87
|
type ResolvedField,
|
|
86
88
|
} from './psl-field-resolution';
|
|
87
89
|
import {
|
|
@@ -594,6 +596,12 @@ interface BuildModelNodeInput {
|
|
|
594
596
|
readonly model: PslModel;
|
|
595
597
|
readonly mapping: ModelNameMapping;
|
|
596
598
|
readonly modelMappings: ReadonlyMap<string, ModelNameMapping>;
|
|
599
|
+
/**
|
|
600
|
+
* Model mappings keyed by `(namespaceId, modelName)` coordinate. Used to
|
|
601
|
+
* resolve a namespace-qualified relation target (`auth.User`) to the exact
|
|
602
|
+
* model even when the bare name is shared across namespaces.
|
|
603
|
+
*/
|
|
604
|
+
readonly modelMappingsByCoordinate: ReadonlyMap<string, ModelNameMapping>;
|
|
597
605
|
readonly modelNames: Set<string>;
|
|
598
606
|
readonly compositeTypeNames: ReadonlySet<string>;
|
|
599
607
|
readonly enumTypeDescriptors: Map<string, ColumnDescriptor>;
|
|
@@ -1166,19 +1174,23 @@ function buildModelNodeFromPsl(input: BuildModelNodeInput): BuildModelNodeResult
|
|
|
1166
1174
|
continue;
|
|
1167
1175
|
}
|
|
1168
1176
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
fieldTypeNamespaceId === 'unbound'
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1177
|
+
const normalizedQualifier =
|
|
1178
|
+
fieldTypeNamespaceId === undefined
|
|
1179
|
+
? undefined
|
|
1180
|
+
: fieldTypeNamespaceId === 'unbound'
|
|
1181
|
+
? '__unbound__'
|
|
1182
|
+
: fieldTypeNamespaceId;
|
|
1183
|
+
if (
|
|
1184
|
+
normalizedQualifier !== undefined &&
|
|
1185
|
+
!input.modelMappingsByCoordinate.has(modelCoordinateKey(normalizedQualifier, fieldTypeName))
|
|
1186
|
+
) {
|
|
1187
|
+
diagnostics.push({
|
|
1188
|
+
code: 'PSL_INVALID_RELATION_TARGET',
|
|
1189
|
+
message: `Relation field "${model.name}.${relationAttribute.field.name}" references unknown model "${qualifiedTypeName}"`,
|
|
1190
|
+
sourceId,
|
|
1191
|
+
span: relationAttribute.field.span,
|
|
1192
|
+
});
|
|
1193
|
+
continue;
|
|
1182
1194
|
}
|
|
1183
1195
|
|
|
1184
1196
|
const parsedRelation = parseRelationAttribute({
|
|
@@ -1201,7 +1213,12 @@ function buildModelNodeFromPsl(input: BuildModelNodeInput): BuildModelNodeResult
|
|
|
1201
1213
|
continue;
|
|
1202
1214
|
}
|
|
1203
1215
|
|
|
1204
|
-
const targetMapping =
|
|
1216
|
+
const targetMapping =
|
|
1217
|
+
normalizedQualifier !== undefined
|
|
1218
|
+
? input.modelMappingsByCoordinate.get(
|
|
1219
|
+
modelCoordinateKey(normalizedQualifier, fieldTypeName),
|
|
1220
|
+
)
|
|
1221
|
+
: input.modelMappings.get(fieldTypeName);
|
|
1205
1222
|
if (!targetMapping) {
|
|
1206
1223
|
diagnostics.push({
|
|
1207
1224
|
code: 'PSL_INVALID_RELATION_TARGET',
|
|
@@ -1269,7 +1286,10 @@ function buildModelNodeFromPsl(input: BuildModelNodeInput): BuildModelNodeResult
|
|
|
1269
1286
|
})
|
|
1270
1287
|
: undefined;
|
|
1271
1288
|
|
|
1272
|
-
const targetNamespaceId =
|
|
1289
|
+
const targetNamespaceId =
|
|
1290
|
+
normalizedQualifier !== undefined
|
|
1291
|
+
? normalizedQualifier
|
|
1292
|
+
: input.modelNamespaceIds.get(targetMapping.model.name);
|
|
1273
1293
|
foreignKeyNodes.push({
|
|
1274
1294
|
columns: localColumns,
|
|
1275
1295
|
references: {
|
|
@@ -1289,6 +1309,7 @@ function buildModelNodeFromPsl(input: BuildModelNodeInput): BuildModelNodeResult
|
|
|
1289
1309
|
declaringTableName: tableName,
|
|
1290
1310
|
targetModelName: targetMapping.model.name,
|
|
1291
1311
|
targetTableName: targetMapping.tableName,
|
|
1312
|
+
...ifDefined('targetNamespaceId', targetNamespaceId),
|
|
1292
1313
|
...ifDefined('relationName', parsedRelation.relationName),
|
|
1293
1314
|
localColumns,
|
|
1294
1315
|
referencedColumns,
|
|
@@ -1528,16 +1549,20 @@ function resolvePolymorphism(
|
|
|
1528
1549
|
): Record<string, ContractModel> {
|
|
1529
1550
|
let patched = models;
|
|
1530
1551
|
|
|
1552
|
+
const coordinateFor = (modelName: string): string =>
|
|
1553
|
+
modelCoordinateKey(modelNamespaceIds.get(modelName) ?? defaultNamespaceId, modelName);
|
|
1554
|
+
|
|
1531
1555
|
// STI variant columns were materialised onto the base storage table so the
|
|
1532
1556
|
// variants' `storage.fields` resolve. They are storage-only on the base — the
|
|
1533
1557
|
// domain field belongs to the variant — so strip them from the base model's
|
|
1534
1558
|
// domain + storage field maps (the table column, built upstream, stays).
|
|
1535
1559
|
for (const [baseName, fieldNames] of stiBaseFieldsByBase) {
|
|
1536
|
-
const
|
|
1560
|
+
const baseKey = coordinateFor(baseName);
|
|
1561
|
+
const baseModel = patched[baseKey];
|
|
1537
1562
|
if (!baseModel || fieldNames.length === 0) continue;
|
|
1538
1563
|
patched = {
|
|
1539
1564
|
...patched,
|
|
1540
|
-
[
|
|
1565
|
+
[baseKey]: stripStorageOnlyDomainFields(baseModel, fieldNames),
|
|
1541
1566
|
};
|
|
1542
1567
|
}
|
|
1543
1568
|
|
|
@@ -1552,7 +1577,7 @@ function resolvePolymorphism(
|
|
|
1552
1577
|
continue;
|
|
1553
1578
|
}
|
|
1554
1579
|
|
|
1555
|
-
const model = patched[modelName];
|
|
1580
|
+
const model = patched[coordinateFor(modelName)];
|
|
1556
1581
|
if (!model) continue;
|
|
1557
1582
|
|
|
1558
1583
|
if (!Object.hasOwn(model.fields, decl.fieldName)) {
|
|
@@ -1597,7 +1622,7 @@ function resolvePolymorphism(
|
|
|
1597
1622
|
|
|
1598
1623
|
patched = {
|
|
1599
1624
|
...patched,
|
|
1600
|
-
[modelName]: { ...model, discriminator: { field: decl.fieldName }, variants },
|
|
1625
|
+
[coordinateFor(modelName)]: { ...model, discriminator: { field: decl.fieldName }, variants },
|
|
1601
1626
|
};
|
|
1602
1627
|
}
|
|
1603
1628
|
|
|
@@ -1626,7 +1651,7 @@ function resolvePolymorphism(
|
|
|
1626
1651
|
continue;
|
|
1627
1652
|
}
|
|
1628
1653
|
|
|
1629
|
-
const variantModel = patched[variantName];
|
|
1654
|
+
const variantModel = patched[coordinateFor(variantName)];
|
|
1630
1655
|
if (!variantModel) continue;
|
|
1631
1656
|
|
|
1632
1657
|
const baseMapping = modelMappings.get(baseDecl.baseName);
|
|
@@ -1646,7 +1671,7 @@ function resolvePolymorphism(
|
|
|
1646
1671
|
|
|
1647
1672
|
patched = {
|
|
1648
1673
|
...patched,
|
|
1649
|
-
[variantName]: stripStorageOnlyDomainFields(
|
|
1674
|
+
[coordinateFor(variantName)]: stripStorageOnlyDomainFields(
|
|
1650
1675
|
patchedVariant,
|
|
1651
1676
|
syntheticPkFieldsByVariant.get(variantName) ?? [],
|
|
1652
1677
|
),
|
|
@@ -1883,6 +1908,7 @@ export function interpretPslDocumentToSqlContract(
|
|
|
1883
1908
|
// remains the input to the rest of the interpreter so non-namespace
|
|
1884
1909
|
// concerns stay structurally identical to before.
|
|
1885
1910
|
const models: PslModel[] = [];
|
|
1911
|
+
const modelEntries: ModelNamespaceEntry[] = [];
|
|
1886
1912
|
const modelNamespaceIds = new Map<string, string>();
|
|
1887
1913
|
for (const namespace of input.document.ast.namespaces) {
|
|
1888
1914
|
const resolvedNamespaceId = resolveNamespaceIdForSqlTarget({
|
|
@@ -1891,11 +1917,13 @@ export function interpretPslDocumentToSqlContract(
|
|
|
1891
1917
|
});
|
|
1892
1918
|
for (const model of namespace.models) {
|
|
1893
1919
|
models.push(model);
|
|
1920
|
+
modelEntries.push({ model, namespaceId: resolvedNamespaceId });
|
|
1894
1921
|
if (resolvedNamespaceId !== undefined) {
|
|
1895
1922
|
modelNamespaceIds.set(model.name, resolvedNamespaceId);
|
|
1896
1923
|
}
|
|
1897
1924
|
}
|
|
1898
1925
|
}
|
|
1926
|
+
const defaultNamespaceId = input.target.defaultNamespaceId;
|
|
1899
1927
|
// Top-level enums (the __unspecified__ bucket) route to `storageTypes`;
|
|
1900
1928
|
// enums inside a named namespace block route to `namespaceTypes[nsId]`.
|
|
1901
1929
|
const topLevelEnums = input.document.ast.namespaces
|
|
@@ -1992,7 +2020,20 @@ export function interpretPslDocumentToSqlContract(
|
|
|
1992
2020
|
|
|
1993
2021
|
const storageTypes = { ...enumResult.storageTypes, ...namedTypeResult.storageTypes };
|
|
1994
2022
|
|
|
1995
|
-
const
|
|
2023
|
+
const modelMappingsByCoordinate = buildModelMappings(
|
|
2024
|
+
modelEntries,
|
|
2025
|
+
defaultNamespaceId,
|
|
2026
|
+
diagnostics,
|
|
2027
|
+
sourceId,
|
|
2028
|
+
);
|
|
2029
|
+
// Bare-name view for unqualified relation targets and polymorphism, where
|
|
2030
|
+
// resolution is by bare model name. When a bare name is shared across
|
|
2031
|
+
// namespaces this collapses to the last entry; qualified relation targets
|
|
2032
|
+
// and per-model lowering use the coordinate-keyed map above instead.
|
|
2033
|
+
const modelMappings = new Map<string, ModelNameMapping>();
|
|
2034
|
+
for (const mapping of modelMappingsByCoordinate.values()) {
|
|
2035
|
+
modelMappings.set(mapping.model.name, mapping);
|
|
2036
|
+
}
|
|
1996
2037
|
const modelNodes: ModelNode[] = [];
|
|
1997
2038
|
const fkRelationMetadata: FkRelationMetadata[] = [];
|
|
1998
2039
|
const backrelationCandidates: ModelBackrelationCandidate[] = [];
|
|
@@ -2001,8 +2042,9 @@ export function interpretPslDocumentToSqlContract(
|
|
|
2001
2042
|
// modelRelations after local back-relation matching so they bypass that step.
|
|
2002
2043
|
const crossSpaceRelationsByModel = new Map<string, RelationNode[]>();
|
|
2003
2044
|
|
|
2004
|
-
for (const model of
|
|
2005
|
-
const
|
|
2045
|
+
for (const { model, namespaceId } of modelEntries) {
|
|
2046
|
+
const coordinate = modelCoordinateKey(namespaceId ?? defaultNamespaceId, model.name);
|
|
2047
|
+
const mapping = modelMappingsByCoordinate.get(coordinate);
|
|
2006
2048
|
if (!mapping) {
|
|
2007
2049
|
continue;
|
|
2008
2050
|
}
|
|
@@ -2010,6 +2052,7 @@ export function interpretPslDocumentToSqlContract(
|
|
|
2010
2052
|
model,
|
|
2011
2053
|
mapping,
|
|
2012
2054
|
modelMappings,
|
|
2055
|
+
modelMappingsByCoordinate,
|
|
2013
2056
|
modelNames,
|
|
2014
2057
|
compositeTypeNames,
|
|
2015
2058
|
enumTypeDescriptors: allEnumTypeDescriptors,
|
|
@@ -2026,15 +2069,12 @@ export function interpretPslDocumentToSqlContract(
|
|
|
2026
2069
|
diagnostics,
|
|
2027
2070
|
modelNamespaceIds,
|
|
2028
2071
|
});
|
|
2029
|
-
const resolvedNamespaceId = modelNamespaceIds.get(model.name);
|
|
2030
2072
|
modelNodes.push(
|
|
2031
|
-
|
|
2032
|
-
? { ...result.modelNode, namespaceId: resolvedNamespaceId }
|
|
2033
|
-
: result.modelNode,
|
|
2073
|
+
namespaceId !== undefined ? { ...result.modelNode, namespaceId } : result.modelNode,
|
|
2034
2074
|
);
|
|
2035
2075
|
fkRelationMetadata.push(...result.fkRelationMetadata);
|
|
2036
2076
|
backrelationCandidates.push(...result.backrelationCandidates);
|
|
2037
|
-
modelResolvedFields.set(
|
|
2077
|
+
modelResolvedFields.set(coordinate, result.resolvedFields);
|
|
2038
2078
|
if (result.crossSpaceRelations.length > 0) {
|
|
2039
2079
|
const existing = crossSpaceRelationsByModel.get(model.name) ?? [];
|
|
2040
2080
|
crossSpaceRelationsByModel.set(model.name, [...existing, ...result.crossSpaceRelations]);
|
|
@@ -2135,15 +2175,17 @@ export function interpretPslDocumentToSqlContract(
|
|
|
2135
2175
|
})),
|
|
2136
2176
|
});
|
|
2137
2177
|
|
|
2178
|
+
// Keyed by `(namespaceId, modelName)` coordinate so two models that share a
|
|
2179
|
+
// bare name across namespaces stay distinct through the patch/polymorphism
|
|
2180
|
+
// passes; only a genuine same-namespace duplicate is an error.
|
|
2138
2181
|
const modelsForPatch: Record<string, ContractModel> = {};
|
|
2139
|
-
for (const namespaceSlice of Object.
|
|
2182
|
+
for (const [namespaceId, namespaceSlice] of Object.entries(contract.domain.namespaces)) {
|
|
2140
2183
|
for (const [modelName, model] of Object.entries(namespaceSlice.models)) {
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
);
|
|
2184
|
+
const coordinate = modelCoordinateKey(namespaceId, modelName);
|
|
2185
|
+
if (Object.hasOwn(modelsForPatch, coordinate)) {
|
|
2186
|
+
throw new Error(`duplicate model "${namespaceId}.${modelName}" during PSL interpretation`);
|
|
2145
2187
|
}
|
|
2146
|
-
modelsForPatch[
|
|
2188
|
+
modelsForPatch[coordinate] = model;
|
|
2147
2189
|
}
|
|
2148
2190
|
}
|
|
2149
2191
|
let patchedModels = patchModelDomainFields(modelsForPatch, modelResolvedFields);
|
|
@@ -2188,7 +2230,7 @@ export function interpretPslDocumentToSqlContract(
|
|
|
2188
2230
|
models: Object.fromEntries(
|
|
2189
2231
|
Object.entries(namespaceSlice.models).map(([modelName, model]) => [
|
|
2190
2232
|
modelName,
|
|
2191
|
-
patchedModels[modelName] ?? model,
|
|
2233
|
+
patchedModels[modelCoordinateKey(namespaceId, modelName)] ?? model,
|
|
2192
2234
|
]),
|
|
2193
2235
|
),
|
|
2194
2236
|
...(namespaceSlice.valueObjects !== undefined
|
|
@@ -42,6 +42,24 @@ export type ModelNameMapping = {
|
|
|
42
42
|
readonly fieldColumns: Map<string, string>;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* A PSL model paired with its resolved namespace coordinate (undefined when
|
|
47
|
+
* the target leaves the model late-bound). Two models may share a bare name
|
|
48
|
+
* across namespaces, so structures that must distinguish them are keyed by
|
|
49
|
+
* the `(namespaceId, modelName)` coordinate produced by
|
|
50
|
+
* {@link modelCoordinateKey} rather than the bare model name.
|
|
51
|
+
*/
|
|
52
|
+
export type ModelNamespaceEntry = {
|
|
53
|
+
readonly model: PslModel;
|
|
54
|
+
readonly namespaceId: string | undefined;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const MODEL_COORDINATE_SEPARATOR = '\u0000';
|
|
58
|
+
|
|
59
|
+
export function modelCoordinateKey(namespaceId: string, modelName: string): string {
|
|
60
|
+
return `${namespaceId}${MODEL_COORDINATE_SEPARATOR}${modelName}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
45
63
|
export interface CollectResolvedFieldsInput {
|
|
46
64
|
readonly model: PslModel;
|
|
47
65
|
readonly mapping: ModelNameMapping;
|
|
@@ -424,12 +442,13 @@ export function collectResolvedFields(input: CollectResolvedFieldsInput): Resolv
|
|
|
424
442
|
}
|
|
425
443
|
|
|
426
444
|
export function buildModelMappings(
|
|
427
|
-
|
|
445
|
+
modelEntries: readonly ModelNamespaceEntry[],
|
|
446
|
+
defaultNamespaceId: string,
|
|
428
447
|
diagnostics: ContractSourceDiagnostic[],
|
|
429
448
|
sourceId: string,
|
|
430
449
|
): Map<string, ModelNameMapping> {
|
|
431
450
|
const result = new Map<string, ModelNameMapping>();
|
|
432
|
-
for (const model of
|
|
451
|
+
for (const { model, namespaceId } of modelEntries) {
|
|
433
452
|
const mapAttribute = getAttribute(model.attributes, 'map');
|
|
434
453
|
const tableName = parseMapName({
|
|
435
454
|
attribute: mapAttribute,
|
|
@@ -452,7 +471,7 @@ export function buildModelMappings(
|
|
|
452
471
|
});
|
|
453
472
|
fieldColumns.set(field.name, columnName);
|
|
454
473
|
}
|
|
455
|
-
result.set(model.name, {
|
|
474
|
+
result.set(modelCoordinateKey(namespaceId ?? defaultNamespaceId, model.name), {
|
|
456
475
|
model,
|
|
457
476
|
tableName,
|
|
458
477
|
fieldColumns,
|
|
@@ -42,6 +42,8 @@ export type FkRelationMetadata = {
|
|
|
42
42
|
readonly declaringTableName: string;
|
|
43
43
|
readonly targetModelName: string;
|
|
44
44
|
readonly targetTableName: string;
|
|
45
|
+
/** Resolved namespace coordinate of the related model, when known. */
|
|
46
|
+
readonly targetNamespaceId?: string;
|
|
45
47
|
readonly relationName?: string;
|
|
46
48
|
readonly localColumns: readonly string[];
|
|
47
49
|
readonly referencedColumns: readonly string[];
|
|
@@ -251,6 +253,7 @@ export function indexFkRelations(input: {
|
|
|
251
253
|
fieldName: relation.declaringFieldName,
|
|
252
254
|
toModel: relation.targetModelName,
|
|
253
255
|
toTable: relation.targetTableName,
|
|
256
|
+
...ifDefined('toNamespaceId', relation.targetNamespaceId),
|
|
254
257
|
cardinality: 'N:1',
|
|
255
258
|
on: {
|
|
256
259
|
parentTable: relation.declaringTableName,
|