@metaobjectsdev/metadata 1.0.3 → 1.0.4-rc.1
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/core/relationship/derive-m2m-fields.d.ts +3 -1
- package/dist/core/relationship/derive-m2m-fields.d.ts.map +1 -1
- package/dist/core/relationship/derive-m2m-fields.js +110 -15
- package/dist/core/relationship/derive-m2m-fields.js.map +1 -1
- package/dist/core/relationship/find-reference.d.ts +21 -5
- package/dist/core/relationship/find-reference.d.ts.map +1 -1
- package/dist/core/relationship/find-reference.js +29 -10
- package/dist/core/relationship/find-reference.js.map +1 -1
- package/dist/core/relationship/relationship-definition.embedded.js +8 -8
- package/dist/core/relationship/relationship-definition.embedded.js.map +1 -1
- package/dist/core/relationship/resolve-relationship-reference.d.ts +27 -0
- package/dist/core/relationship/resolve-relationship-reference.d.ts.map +1 -0
- package/dist/core/relationship/resolve-relationship-reference.js +84 -0
- package/dist/core/relationship/resolve-relationship-reference.js.map +1 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +10 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/library/embedded-library.generated.d.ts +2 -0
- package/dist/library/embedded-library.generated.d.ts.map +1 -1
- package/dist/library/embedded-library.generated.js +11 -1
- package/dist/library/embedded-library.generated.js.map +1 -1
- package/dist/library/index.d.ts +2 -0
- package/dist/library/index.d.ts.map +1 -0
- package/dist/library/index.js +24 -0
- package/dist/library/index.js.map +1 -0
- package/dist/library/library-sources.d.ts +99 -6
- package/dist/library/library-sources.d.ts.map +1 -1
- package/dist/library/library-sources.js +165 -43
- package/dist/library/library-sources.js.map +1 -1
- package/dist/loader/meta-data-loader.d.ts.map +1 -1
- package/dist/loader/meta-data-loader.js +6 -1
- package/dist/loader/meta-data-loader.js.map +1 -1
- package/dist/loader/validation-passes.d.ts +2 -0
- package/dist/loader/validation-passes.d.ts.map +1 -1
- package/dist/loader/validation-passes.js +162 -22
- package/dist/loader/validation-passes.js.map +1 -1
- package/dist/parser-core.d.ts.map +1 -1
- package/dist/parser-core.js +13 -2
- package/dist/parser-core.js.map +1 -1
- package/dist/vocabulary-rewrite-yaml.d.ts +2 -0
- package/dist/vocabulary-rewrite-yaml.d.ts.map +1 -0
- package/dist/vocabulary-rewrite-yaml.js +22 -0
- package/dist/vocabulary-rewrite-yaml.js.map +1 -0
- package/package.json +7 -7
- package/src/core/relationship/derive-m2m-fields.ts +115 -15
- package/src/core/relationship/find-reference.ts +34 -11
- package/src/core/relationship/relationship-definition.embedded.ts +8 -8
- package/src/core/relationship/resolve-relationship-reference.ts +99 -0
- package/src/errors.ts +10 -0
- package/src/index.ts +6 -1
- package/src/library/embedded-library.generated.ts +12 -1
- package/src/library/index.ts +23 -0
- package/src/library/library-sources.ts +197 -47
- package/src/loader/meta-data-loader.ts +7 -1
- package/src/loader/validation-passes.ts +181 -23
- package/src/parser-core.ts +15 -2
- package/src/vocabulary-rewrite-yaml.ts +21 -0
|
@@ -17,7 +17,9 @@ export interface M2MFields {
|
|
|
17
17
|
*
|
|
18
18
|
* @param rel the M:N relationship (carries @objectRef + @through + optional
|
|
19
19
|
* @sourceRefField / @symmetric)
|
|
20
|
-
* @param source the entity
|
|
20
|
+
* @param source the entity the caller is navigating from. Accepted alongside
|
|
21
|
+
* `rel.parent` as a name for the relationship's subject, and used
|
|
22
|
+
* as the declaring entity when `rel` has no entity parent.
|
|
21
23
|
* @param root the loaded model root (to find the junction entity)
|
|
22
24
|
* @throws M2MDerivationError when the junction is missing/malformed or the
|
|
23
25
|
* self-join is ambiguous.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"derive-m2m-fields.d.ts","sourceRoot":"","sources":["../../../src/core/relationship/derive-m2m-fields.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"derive-m2m-fields.d.ts","sourceRoot":"","sources":["../../../src/core/relationship/derive-m2m-fields.ts"],"names":[],"mappings":"AAyCA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAM/D,6EAA6E;AAC7E,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,8BAA8B;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,SAAS;IACxB,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AA6BD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,gBAAgB,EACrB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,GACb,SAAS,CA+IX"}
|
|
@@ -17,7 +17,30 @@
|
|
|
17
17
|
// two references are taken in declaration order (sourceField = first,
|
|
18
18
|
// targetField = second). Resolution unions both at read time.
|
|
19
19
|
// Ambiguous (source == target, neither @sourceRefField nor @symmetric) → throw.
|
|
20
|
+
//
|
|
21
|
+
// "source" above means the relationship's SUBJECT, and under `extends` there are two
|
|
22
|
+
// legitimate names for it. Every caller walks the RESOLVING `obj.relationships()`
|
|
23
|
+
// (codegen-ts's relation-resolver, runtime-ts's n2m-resolver, docs-site's link-graph)
|
|
24
|
+
// and passes the entity it is ITERATING, which for an inherited relationship is not
|
|
25
|
+
// the one that declared it. So the DECLARING entity is resolved here from `rel.parent`
|
|
26
|
+
// (same shape as the #368 loader fix, validation-passes' `declaringEntity = rel.parent
|
|
27
|
+
// ?? obj`), and the passed `source` is kept alongside it rather than discarded: a
|
|
28
|
+
// junction FK usually references the CONCRETE entity, because the abstract base has no
|
|
29
|
+
// table, while @objectRef on an inherited self-join names the base. Both are accepted,
|
|
30
|
+
// for the self-join classification and the hetero reference match alike. `source` is
|
|
31
|
+
// also the fallback when `rel` has no entity parent, which keeps the exported signature
|
|
32
|
+
// unchanged. Not covered: a junction reference naming an entity strictly BETWEEN the
|
|
33
|
+
// base and the navigating entity in a deeper hierarchy.
|
|
34
|
+
//
|
|
35
|
+
// The subject comparison is made on RESOLVED OBJECT IDENTITY, not on stripped
|
|
36
|
+
// short names, matching the Java reference (M2MFields.java). Bare-name equality
|
|
37
|
+
// cannot tell `a::NodeBase` from `b::NodeBase`, so once the subject set held two
|
|
38
|
+
// names a genuine CROSS-PACKAGE hetero M:N whose target shares a short name with
|
|
39
|
+
// the subject read as a self-join and refused to derive. Scoped deliberately to
|
|
40
|
+
// this one predicate — every other name comparison in this file is untouched.
|
|
20
41
|
import { stripPackage } from "../../naming.js";
|
|
42
|
+
import { TYPE_OBJECT } from "../../shared/base-types.js";
|
|
43
|
+
import { PACKAGE_SEPARATOR } from "../../shared/structural.js";
|
|
21
44
|
/** Thrown when a M:N relationship's junction FK fields cannot be derived. */
|
|
22
45
|
export class M2MDerivationError extends Error {
|
|
23
46
|
code = "ERR_INVALID_RELATIONSHIP";
|
|
@@ -30,20 +53,53 @@ export class M2MDerivationError extends Error {
|
|
|
30
53
|
function refFkField(ref) {
|
|
31
54
|
return ref.fields[0];
|
|
32
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* The root entity a reference name denotes, or undefined. Mirrors the Java
|
|
58
|
+
* reference's `M2MFields.findObject` exactly: a FULLY-QUALIFIED name (one
|
|
59
|
+
* containing "::") resolves EXACTLY on the object's package-folded key, never a
|
|
60
|
+
* bare-tail fallback; a bare name matches a short name, first match wins (the
|
|
61
|
+
* bare-collision case is the deferred follow-up Java records as issue #174).
|
|
62
|
+
*
|
|
63
|
+
* This exists so the SUBJECT comparison below can be made on object IDENTITY the
|
|
64
|
+
* way Java's already is. A bare-name compare cannot tell `a::NodeBase` from
|
|
65
|
+
* `b::NodeBase`, which made a genuine cross-package hetero M:N read as a
|
|
66
|
+
* self-join the moment the subject set held two names.
|
|
67
|
+
*/
|
|
68
|
+
function findEntity(root, name) {
|
|
69
|
+
if (name === undefined || name === "")
|
|
70
|
+
return undefined;
|
|
71
|
+
const objects = root.objects();
|
|
72
|
+
if (name.includes(PACKAGE_SEPARATOR)) {
|
|
73
|
+
return objects.find((o) => o.resolutionKey() === name);
|
|
74
|
+
}
|
|
75
|
+
const bare = stripPackage(name);
|
|
76
|
+
return objects.find((o) => o.name === bare);
|
|
77
|
+
}
|
|
33
78
|
/**
|
|
34
79
|
* Derive the source/target junction FK fields for a M:N relationship.
|
|
35
80
|
*
|
|
36
81
|
* @param rel the M:N relationship (carries @objectRef + @through + optional
|
|
37
82
|
* @sourceRefField / @symmetric)
|
|
38
|
-
* @param source the entity
|
|
83
|
+
* @param source the entity the caller is navigating from. Accepted alongside
|
|
84
|
+
* `rel.parent` as a name for the relationship's subject, and used
|
|
85
|
+
* as the declaring entity when `rel` has no entity parent.
|
|
39
86
|
* @param root the loaded model root (to find the junction entity)
|
|
40
87
|
* @throws M2MDerivationError when the junction is missing/malformed or the
|
|
41
88
|
* self-join is ambiguous.
|
|
42
89
|
*/
|
|
43
90
|
export function deriveM2MFields(rel, source, root) {
|
|
91
|
+
// The entity that DECLARES `rel` — see the header note. `rel.parent` is the
|
|
92
|
+
// owning entity for both an own declaration and an inherited one (an unmodified
|
|
93
|
+
// inherited child is the SAME node object, reused in place by
|
|
94
|
+
// MetaData._effectiveChildren; an override is a genuinely different node whose
|
|
95
|
+
// parent is the overriding entity, which is also correct).
|
|
96
|
+
const relParent = rel.parent;
|
|
97
|
+
const declaringEntity = relParent !== undefined && relParent.type === TYPE_OBJECT
|
|
98
|
+
? relParent
|
|
99
|
+
: source;
|
|
44
100
|
const throughName = rel.through;
|
|
45
101
|
if (throughName === undefined) {
|
|
46
|
-
throw new M2MDerivationError(`relationship "${
|
|
102
|
+
throw new M2MDerivationError(`relationship "${declaringEntity.name}.${rel.name}" is missing @through (required for M:N derivation)`);
|
|
47
103
|
}
|
|
48
104
|
// @through may be package-qualified (FQN); findObject is keyed by bare name,
|
|
49
105
|
// so fall back to the bare suffix after the last "::". Mirrors the
|
|
@@ -54,27 +110,66 @@ export function deriveM2MFields(rel, source, root) {
|
|
|
54
110
|
? root.findObject(throughName.slice(throughName.lastIndexOf("::") + 2))
|
|
55
111
|
: undefined);
|
|
56
112
|
if (junction === undefined) {
|
|
57
|
-
throw new M2MDerivationError(`relationship "${
|
|
113
|
+
throw new M2MDerivationError(`relationship "${declaringEntity.name}.${rel.name}" @through "${throughName}" does not resolve to an entity`);
|
|
58
114
|
}
|
|
59
115
|
const targetName = rel.objectRef;
|
|
60
116
|
if (targetName === undefined) {
|
|
61
|
-
throw new M2MDerivationError(`relationship "${
|
|
117
|
+
throw new M2MDerivationError(`relationship "${declaringEntity.name}.${rel.name}" is missing @objectRef (the M:N target)`);
|
|
62
118
|
}
|
|
63
119
|
const refs = junction.referenceIdentities();
|
|
64
120
|
if (refs.length !== 2) {
|
|
65
|
-
throw new M2MDerivationError(`junction "${throughName}" for relationship "${
|
|
121
|
+
throw new M2MDerivationError(`junction "${throughName}" for relationship "${declaringEntity.name}.${rel.name}" must declare exactly two ` +
|
|
66
122
|
`identity.reference children (found ${refs.length})`);
|
|
67
123
|
}
|
|
68
|
-
|
|
124
|
+
// The relationship's SUBJECT — the entity the M:N hangs off. Under `extends`
|
|
125
|
+
// there are two legitimate names for it and BOTH occur in real models:
|
|
126
|
+
// * the DECLARING entity (rel.parent) — what @objectRef names for a self-join
|
|
127
|
+
// declared on an abstract base, and what a junction reference names when the
|
|
128
|
+
// author points the FK at the base type;
|
|
129
|
+
// * the NAVIGATING entity (`source`) — the concrete entity the caller is
|
|
130
|
+
// iterating, which is what a junction FK usually references, because that is
|
|
131
|
+
// the entity with the physical table.
|
|
132
|
+
// Accepting either is what makes the derivation independent of which entity's
|
|
133
|
+
// effective view reached the relationship. (Not covered: a junction reference
|
|
134
|
+
// naming an entity strictly BETWEEN the declaring base and the navigating
|
|
135
|
+
// entity in a deeper hierarchy — no model does that, and widening to the whole
|
|
136
|
+
// super chain would make the "must declare one identity.reference to ..." error
|
|
137
|
+
// unfalsifiable.)
|
|
138
|
+
const subjectNames = declaringEntity.name === source.name
|
|
139
|
+
? [declaringEntity.name]
|
|
140
|
+
: [declaringEntity.name, source.name];
|
|
141
|
+
const subjectLabel = subjectNames.map((n) => `"${n}"`).join(" or ");
|
|
142
|
+
// Compared by resolved object IDENTITY, matching the Java reference. A
|
|
143
|
+
// stripPackage() compare cannot distinguish `a::NodeBase` from `b::NodeBase`,
|
|
144
|
+
// so with two names in the set a genuine cross-package hetero M:N read as a
|
|
145
|
+
// self-join and refused to derive.
|
|
146
|
+
const isSubject = (entity) => entity !== undefined && (entity === declaringEntity || entity === source);
|
|
147
|
+
const isSubjectName = (name) => name !== undefined && subjectNames.includes(stripPackage(name));
|
|
148
|
+
// Defensive bare fallback when @objectRef does not resolve — loader validation
|
|
149
|
+
// normally guarantees it does. Same carve-out the Java reference makes.
|
|
150
|
+
const targetEntityNode = findEntity(root, targetName);
|
|
151
|
+
const isSelfJoin = targetEntityNode !== undefined
|
|
152
|
+
? isSubject(targetEntityNode)
|
|
153
|
+
: isSubjectName(targetName);
|
|
69
154
|
if (!isSelfJoin) {
|
|
70
|
-
// Hetero: match each reference by the
|
|
71
|
-
const sourceRef = refs.find((r) =>
|
|
72
|
-
|
|
155
|
+
// Hetero: match each reference by the ENTITY OBJECT it resolves to.
|
|
156
|
+
const sourceRef = refs.find((r) => isSubject(findEntity(root, r.targetEntity)));
|
|
157
|
+
// Identity here too. The two searches are INDEPENDENT — nothing excludes
|
|
158
|
+
// sourceRef from this one, unlike the directed self-join branch below — so a
|
|
159
|
+
// bare compare could match the SOURCE-side reference again whenever the
|
|
160
|
+
// target's short name equals the source's, and silently return (srcFk, srcFk).
|
|
161
|
+
// Structurally unreachable while isSelfJoin was also bare (a colliding short
|
|
162
|
+
// name forced the self-join branch); making only isSelfJoin identity-based
|
|
163
|
+
// broke that invariant. Java matches identity on both sides (findRefToSubject
|
|
164
|
+
// + findRefToObject) and never had the hole.
|
|
165
|
+
const targetRef = targetEntityNode !== undefined
|
|
166
|
+
? refs.find((r) => findEntity(root, r.targetEntity) === targetEntityNode)
|
|
167
|
+
: refs.find((r) => r.targetEntity !== undefined && stripPackage(r.targetEntity) === stripPackage(targetName));
|
|
73
168
|
const sourceField = sourceRef ? refFkField(sourceRef) : undefined;
|
|
74
169
|
const targetField = targetRef ? refFkField(targetRef) : undefined;
|
|
75
170
|
if (sourceField === undefined || targetField === undefined) {
|
|
76
|
-
throw new M2MDerivationError(`junction "${throughName}" for relationship "${
|
|
77
|
-
`identity.reference to
|
|
171
|
+
throw new M2MDerivationError(`junction "${throughName}" for relationship "${declaringEntity.name}.${rel.name}" must declare one ` +
|
|
172
|
+
`identity.reference to ${subjectLabel} and one to "${stripPackage(targetName)}"`);
|
|
78
173
|
}
|
|
79
174
|
return { sourceField, targetField };
|
|
80
175
|
}
|
|
@@ -84,25 +179,25 @@ export function deriveM2MFields(rel, source, root) {
|
|
|
84
179
|
const a = refFkField(refs[0]);
|
|
85
180
|
const b = refFkField(refs[1]);
|
|
86
181
|
if (a === undefined || b === undefined) {
|
|
87
|
-
throw new M2MDerivationError(`symmetric junction "${throughName}" for "${
|
|
182
|
+
throw new M2MDerivationError(`symmetric junction "${throughName}" for "${declaringEntity.name}.${rel.name}" has a reference with no @fields`);
|
|
88
183
|
}
|
|
89
184
|
return { sourceField: a, targetField: b };
|
|
90
185
|
}
|
|
91
186
|
const sourceRefField = rel.sourceRefField;
|
|
92
187
|
if (sourceRefField === undefined) {
|
|
93
|
-
throw new M2MDerivationError(`self-join relationship "${
|
|
188
|
+
throw new M2MDerivationError(`self-join relationship "${declaringEntity.name}.${rel.name}" through "${throughName}" is ambiguous: ` +
|
|
94
189
|
`set @sourceRefField (directed) or @symmetric (undirected)`);
|
|
95
190
|
}
|
|
96
191
|
// Directed self-join: @sourceRefField names the source-side FK; the other ref is the target.
|
|
97
192
|
const sourceRef = refs.find((r) => refFkField(r) === sourceRefField);
|
|
98
193
|
if (sourceRef === undefined) {
|
|
99
|
-
throw new M2MDerivationError(`@sourceRefField "${sourceRefField}" on "${
|
|
194
|
+
throw new M2MDerivationError(`@sourceRefField "${sourceRefField}" on "${declaringEntity.name}.${rel.name}" does not match any ` +
|
|
100
195
|
`identity.reference FK field on junction "${throughName}"`);
|
|
101
196
|
}
|
|
102
197
|
const targetRef = refs.find((r) => r !== sourceRef);
|
|
103
198
|
const targetField = targetRef ? refFkField(targetRef) : undefined;
|
|
104
199
|
if (targetField === undefined) {
|
|
105
|
-
throw new M2MDerivationError(`junction "${throughName}" for "${
|
|
200
|
+
throw new M2MDerivationError(`junction "${throughName}" for "${declaringEntity.name}.${rel.name}" has no distinct target-side reference`);
|
|
106
201
|
}
|
|
107
202
|
return { sourceField: sourceRefField, targetField };
|
|
108
203
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"derive-m2m-fields.js","sourceRoot":"","sources":["../../../src/core/relationship/derive-m2m-fields.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,kFAAkF;AAClF,EAAE;AACF,sEAAsE;AACtE,iFAAiF;AACjF,iFAAiF;AACjF,6EAA6E;AAC7E,+CAA+C;AAC/C,EAAE;AACF,uCAAuC;AACvC,+EAA+E;AAC/E,6EAA6E;AAC7E,wEAAwE;AACxE,2EAA2E;AAC3E,qEAAqE;AACrE,iFAAiF;AACjF,2EAA2E;AAC3E,mEAAmE;AACnE,gFAAgF;
|
|
1
|
+
{"version":3,"file":"derive-m2m-fields.js","sourceRoot":"","sources":["../../../src/core/relationship/derive-m2m-fields.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,15 +8,31 @@ export interface ReferenceLookup {
|
|
|
8
8
|
/** The matching identity.reference declaration on `holder`. */
|
|
9
9
|
readonly referenceIdentity: MetaReferenceIdentity;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Every identity.reference on `a` or `b` whose @references targets the other
|
|
13
|
+
* side, `a` walked first. Comparison is package-insensitive: `@references` may
|
|
14
|
+
* be a bare entity name ("User") or a fully-qualified one ("pkg::User"); both
|
|
15
|
+
* match `other.name`.
|
|
16
|
+
*
|
|
17
|
+
* #368: an entity may legally declare more than one identity.reference onto
|
|
18
|
+
* the same target (e.g. Match.homeTeamRef and Match.awayTeamRef both -> Team).
|
|
19
|
+
* Callers that must not silently guess which one is meant enumerate here and
|
|
20
|
+
* report the ambiguity themselves; see also `findReferenceBetween`, which
|
|
21
|
+
* keeps the historical first-match contract for callers that legitimately
|
|
22
|
+
* expect at most one reference between the pair.
|
|
23
|
+
*/
|
|
24
|
+
export declare function findReferencesBetween(a: MetaObject, b: MetaObject): ReferenceLookup[];
|
|
11
25
|
/**
|
|
12
26
|
* Find an identity.reference on either `a` or `b` whose @references targets the
|
|
13
27
|
* other side. Returns undefined if neither side declares one.
|
|
14
28
|
*
|
|
15
|
-
*
|
|
16
|
-
* (
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
29
|
+
* Delegates to `findReferencesBetween` and returns its first entry — kept for
|
|
30
|
+
* backward compatibility (this is exported public API) and for the mutual-1:1
|
|
31
|
+
* case where both sides declare references targeting each other. #368: two
|
|
32
|
+
* references onto the SAME target are also legal and indistinguishable from
|
|
33
|
+
* here — a caller that must not guess which one is meant should call
|
|
34
|
+
* `findReferencesBetween` directly and handle ambiguity explicitly rather than
|
|
35
|
+
* relying on this function's first-match behaviour.
|
|
20
36
|
*/
|
|
21
37
|
export declare function findReferenceBetween(a: MetaObject, b: MetaObject): ReferenceLookup | undefined;
|
|
22
38
|
//# sourceMappingURL=find-reference.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find-reference.d.ts","sourceRoot":"","sources":["../../../src/core/relationship/find-reference.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAG1E,MAAM,WAAW,eAAe;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,uCAAuC;IACvC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,+DAA+D;IAC/D,QAAQ,CAAC,iBAAiB,EAAE,qBAAqB,CAAC;CACnD;AAED
|
|
1
|
+
{"version":3,"file":"find-reference.d.ts","sourceRoot":"","sources":["../../../src/core/relationship/find-reference.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAG1E,MAAM,WAAW,eAAe;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,uCAAuC;IACvC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,+DAA+D;IAC/D,QAAQ,CAAC,iBAAiB,EAAE,qBAAqB,CAAC;CACnD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CACnC,CAAC,EAAE,UAAU,EACb,CAAC,EAAE,UAAU,GACZ,eAAe,EAAE,CAUnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,CAAC,EAAE,UAAU,EACb,CAAC,EAAE,UAAU,GACZ,eAAe,GAAG,SAAS,CAE7B"}
|
|
@@ -7,23 +7,42 @@
|
|
|
7
7
|
// migration schema comparison.
|
|
8
8
|
import { stripPackage } from "../../naming.js";
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* ("User") or a fully-qualified one ("pkg::User"); both match `other.name`.
|
|
10
|
+
* Every identity.reference on `a` or `b` whose @references targets the other
|
|
11
|
+
* side, `a` walked first. Comparison is package-insensitive: `@references` may
|
|
12
|
+
* be a bare entity name ("User") or a fully-qualified one ("pkg::User"); both
|
|
13
|
+
* match `other.name`.
|
|
15
14
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* #368: an entity may legally declare more than one identity.reference onto
|
|
16
|
+
* the same target (e.g. Match.homeTeamRef and Match.awayTeamRef both -> Team).
|
|
17
|
+
* Callers that must not silently guess which one is meant enumerate here and
|
|
18
|
+
* report the ambiguity themselves; see also `findReferenceBetween`, which
|
|
19
|
+
* keeps the historical first-match contract for callers that legitimately
|
|
20
|
+
* expect at most one reference between the pair.
|
|
18
21
|
*/
|
|
19
|
-
export function
|
|
22
|
+
export function findReferencesBetween(a, b) {
|
|
23
|
+
const found = [];
|
|
20
24
|
for (const [holder, other] of [[a, b], [b, a]]) {
|
|
21
25
|
for (const ref of holder.referenceIdentities()) {
|
|
22
26
|
if (stripPackage(ref.targetEntity) === other.name) {
|
|
23
|
-
|
|
27
|
+
found.push({ holder, other, referenceIdentity: ref });
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
30
|
}
|
|
27
|
-
return
|
|
31
|
+
return found;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Find an identity.reference on either `a` or `b` whose @references targets the
|
|
35
|
+
* other side. Returns undefined if neither side declares one.
|
|
36
|
+
*
|
|
37
|
+
* Delegates to `findReferencesBetween` and returns its first entry — kept for
|
|
38
|
+
* backward compatibility (this is exported public API) and for the mutual-1:1
|
|
39
|
+
* case where both sides declare references targeting each other. #368: two
|
|
40
|
+
* references onto the SAME target are also legal and indistinguishable from
|
|
41
|
+
* here — a caller that must not guess which one is meant should call
|
|
42
|
+
* `findReferencesBetween` directly and handle ambiguity explicitly rather than
|
|
43
|
+
* relying on this function's first-match behaviour.
|
|
44
|
+
*/
|
|
45
|
+
export function findReferenceBetween(a, b) {
|
|
46
|
+
return findReferencesBetween(a, b)[0];
|
|
28
47
|
}
|
|
29
48
|
//# sourceMappingURL=find-reference.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find-reference.js","sourceRoot":"","sources":["../../../src/core/relationship/find-reference.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,4EAA4E;AAC5E,gFAAgF;AAChF,2EAA2E;AAC3E,0EAA0E;AAC1E,+BAA+B;AAI/B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAW/C
|
|
1
|
+
{"version":3,"file":"find-reference.js","sourceRoot":"","sources":["../../../src/core/relationship/find-reference.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,4EAA4E;AAC5E,gFAAgF;AAChF,2EAA2E;AAC3E,0EAA0E;AAC1E,+BAA+B;AAI/B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAW/C;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,qBAAqB,CACnC,CAAa,EACb,CAAa;IAEb,MAAM,KAAK,GAAsB,EAAE,CAAC;IACpC,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAU,EAAE,CAAC;QACxD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,mBAAmB,EAAE,EAAE,CAAC;YAC/C,IAAI,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;gBAClD,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAClC,CAAa,EACb,CAAa;IAEb,OAAO,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxC,CAAC"}
|
|
@@ -5,7 +5,7 @@ export const RELATIONSHIP_DEFINITION = {
|
|
|
5
5
|
"type": "relationship",
|
|
6
6
|
"subType": "base",
|
|
7
7
|
"description": "Abstract relationship base — shared shape for the concrete association/aggregation/composition subtypes; not authored directly. A `relationship.base` node fails to load (ERR_ABSTRACT_SUBTYPE_AUTHORED): this subtype is a registry anchor concrete subtypes inherit from, never a node in a document.",
|
|
8
|
-
"rules": "@cardinality is an open string at the metamodel level ('one'/'many', and Java-canonical composite forms such as 'many-to-one'); @objectRef names the target entity. M:N is expressed by @cardinality:'many' + @objectRef + @through: @through names a junction entity that MUST declare two identity.reference children (one per FK side), and the relationship's FK fields are DERIVED from those references — never restated. @sourceRefField disambiguates a DIRECTED self-join by naming the source-side FK field on the junction; @symmetric marks an UNDIRECTED self-join (union-on-read) valid only when @objectRef == the declaring entity; the two are mutually exclusive. @onDelete/@onUpdate carry referential actions (cascade/set-null/restrict/no-action).",
|
|
8
|
+
"rules": "@cardinality is an open string at the metamodel level ('one'/'many', and Java-canonical composite forms such as 'many-to-one'); @objectRef names the target entity. M:N is expressed by @cardinality:'many' + @objectRef + @through: @through names a junction entity that MUST declare two identity.reference children (one per FK side), and the relationship's FK fields are DERIVED from those references — never restated. @sourceRefField disambiguates a DIRECTED self-join by naming the source-side FK field on the junction, or on a @cardinality:'one' relationship names which of several identity.reference nodes onto the same target it navigates when name-pairing does not resolve it uniquely; @symmetric marks an UNDIRECTED self-join (union-on-read) valid only when @objectRef == the declaring entity; the two are mutually exclusive. @onDelete/@onUpdate carry referential actions (cascade/set-null/restrict/no-action).",
|
|
9
9
|
"children": [
|
|
10
10
|
{
|
|
11
11
|
"type": "attr",
|
|
@@ -37,7 +37,7 @@ export const RELATIONSHIP_DEFINITION = {
|
|
|
37
37
|
"name": "sourceRefField",
|
|
38
38
|
"min": 0,
|
|
39
39
|
"max": 1,
|
|
40
|
-
"description": "
|
|
40
|
+
"description": "Disambiguates which reference/FK field a relationship uses when more than one candidate exists: on a directed self-join M:N it names the junction's source-side reference (mutually exclusive with @symmetric), and on a @cardinality:'one' relationship it names which of several identity.reference nodes onto the same target the relationship navigates when name-pairing does not resolve it uniquely."
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"type": "attr",
|
|
@@ -82,7 +82,7 @@ export const RELATIONSHIP_DEFINITION = {
|
|
|
82
82
|
"subType": "association",
|
|
83
83
|
"description": "A plain reference to another entity — no ownership; the target has an independent lifecycle (default @onDelete restrict).",
|
|
84
84
|
"whenToUse": "A plain directed reference to another entity, no ownership or cascade. The lightest link — when you just need to point at another entity.",
|
|
85
|
-
"rules": "M:N is expressed by @cardinality:'many' + @objectRef + @through: @through names a junction entity that MUST declare two identity.reference children (one per FK side), and the relationship's FK fields are DERIVED from those references — never restated. @sourceRefField disambiguates a DIRECTED self-join by naming the source-side FK field on the junction; @symmetric marks an UNDIRECTED self-join (union-on-read) valid only when @objectRef == the declaring entity; the two are mutually exclusive. Association is a plain reference — the target's lifecycle is independent (default @onDelete restrict).",
|
|
85
|
+
"rules": "M:N is expressed by @cardinality:'many' + @objectRef + @through: @through names a junction entity that MUST declare two identity.reference children (one per FK side), and the relationship's FK fields are DERIVED from those references — never restated. @sourceRefField disambiguates a DIRECTED self-join by naming the source-side FK field on the junction, or on a @cardinality:'one' relationship names which of several identity.reference nodes onto the same target it navigates when name-pairing does not resolve it uniquely; @symmetric marks an UNDIRECTED self-join (union-on-read) valid only when @objectRef == the declaring entity; the two are mutually exclusive. Association is a plain reference — the target's lifecycle is independent (default @onDelete restrict).",
|
|
86
86
|
"children": [
|
|
87
87
|
{
|
|
88
88
|
"type": "attr",
|
|
@@ -114,7 +114,7 @@ export const RELATIONSHIP_DEFINITION = {
|
|
|
114
114
|
"name": "sourceRefField",
|
|
115
115
|
"min": 0,
|
|
116
116
|
"max": 1,
|
|
117
|
-
"description": "
|
|
117
|
+
"description": "Disambiguates which reference/FK field a relationship uses when more than one candidate exists: on a directed self-join M:N it names the junction's source-side reference (mutually exclusive with @symmetric), and on a @cardinality:'one' relationship it names which of several identity.reference nodes onto the same target the relationship navigates when name-pairing does not resolve it uniquely."
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
"type": "attr",
|
|
@@ -159,7 +159,7 @@ export const RELATIONSHIP_DEFINITION = {
|
|
|
159
159
|
"subType": "aggregation",
|
|
160
160
|
"description": "A shared/independent containment — the parent groups the target but does not own its lifecycle (default @onDelete set-null).",
|
|
161
161
|
"whenToUse": "One entity groups others it does NOT own (children outlive the parent; delete sets the FK null). Use instead of composition when there is no ownership.",
|
|
162
|
-
"rules": "M:N is expressed by @cardinality:'many' + @objectRef + @through: @through names a junction entity that MUST declare two identity.reference children (one per FK side), and the relationship's FK fields are DERIVED from those references — never restated. @sourceRefField disambiguates a DIRECTED self-join by naming the source-side FK field on the junction; @symmetric marks an UNDIRECTED self-join (union-on-read) valid only when @objectRef == the declaring entity; the two are mutually exclusive. Aggregation is shared/independent — the target outlives the parent (default @onDelete set-null).",
|
|
162
|
+
"rules": "M:N is expressed by @cardinality:'many' + @objectRef + @through: @through names a junction entity that MUST declare two identity.reference children (one per FK side), and the relationship's FK fields are DERIVED from those references — never restated. @sourceRefField disambiguates a DIRECTED self-join by naming the source-side FK field on the junction, or on a @cardinality:'one' relationship names which of several identity.reference nodes onto the same target it navigates when name-pairing does not resolve it uniquely; @symmetric marks an UNDIRECTED self-join (union-on-read) valid only when @objectRef == the declaring entity; the two are mutually exclusive. Aggregation is shared/independent — the target outlives the parent (default @onDelete set-null).",
|
|
163
163
|
"children": [
|
|
164
164
|
{
|
|
165
165
|
"type": "attr",
|
|
@@ -191,7 +191,7 @@ export const RELATIONSHIP_DEFINITION = {
|
|
|
191
191
|
"name": "sourceRefField",
|
|
192
192
|
"min": 0,
|
|
193
193
|
"max": 1,
|
|
194
|
-
"description": "
|
|
194
|
+
"description": "Disambiguates which reference/FK field a relationship uses when more than one candidate exists: on a directed self-join M:N it names the junction's source-side reference (mutually exclusive with @symmetric), and on a @cardinality:'one' relationship it names which of several identity.reference nodes onto the same target the relationship navigates when name-pairing does not resolve it uniquely."
|
|
195
195
|
},
|
|
196
196
|
{
|
|
197
197
|
"type": "attr",
|
|
@@ -236,7 +236,7 @@ export const RELATIONSHIP_DEFINITION = {
|
|
|
236
236
|
"subType": "composition",
|
|
237
237
|
"description": "An owned containment — the parent owns the target's lifecycle; deleting the parent deletes the children (default @onDelete cascade).",
|
|
238
238
|
"whenToUse": "You need a parent that OWNS a child collection (one-to-many, cascade on delete). Declare it to generate the FK + typed navigation instead of a bare FK field + hand-written joins.",
|
|
239
|
-
"rules": "M:N is expressed by @cardinality:'many' + @objectRef + @through: @through names a junction entity that MUST declare two identity.reference children (one per FK side), and the relationship's FK fields are DERIVED from those references — never restated. @sourceRefField disambiguates a DIRECTED self-join by naming the source-side FK field on the junction; @symmetric marks an UNDIRECTED self-join (union-on-read) valid only when @objectRef == the declaring entity; the two are mutually exclusive. Composition is owned lifecycle — the children do not outlive the parent (default @onDelete cascade).",
|
|
239
|
+
"rules": "M:N is expressed by @cardinality:'many' + @objectRef + @through: @through names a junction entity that MUST declare two identity.reference children (one per FK side), and the relationship's FK fields are DERIVED from those references — never restated. @sourceRefField disambiguates a DIRECTED self-join by naming the source-side FK field on the junction, or on a @cardinality:'one' relationship names which of several identity.reference nodes onto the same target it navigates when name-pairing does not resolve it uniquely; @symmetric marks an UNDIRECTED self-join (union-on-read) valid only when @objectRef == the declaring entity; the two are mutually exclusive. Composition is owned lifecycle — the children do not outlive the parent (default @onDelete cascade).",
|
|
240
240
|
"children": [
|
|
241
241
|
{
|
|
242
242
|
"type": "attr",
|
|
@@ -268,7 +268,7 @@ export const RELATIONSHIP_DEFINITION = {
|
|
|
268
268
|
"name": "sourceRefField",
|
|
269
269
|
"min": 0,
|
|
270
270
|
"max": 1,
|
|
271
|
-
"description": "
|
|
271
|
+
"description": "Disambiguates which reference/FK field a relationship uses when more than one candidate exists: on a directed self-join M:N it names the junction's source-side reference (mutually exclusive with @symmetric), and on a @cardinality:'one' relationship it names which of several identity.reference nodes onto the same target the relationship navigates when name-pairing does not resolve it uniquely."
|
|
272
272
|
},
|
|
273
273
|
{
|
|
274
274
|
"type": "attr",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship-definition.embedded.js","sourceRoot":"","sources":["../../../src/core/relationship/relationship-definition.embedded.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,uBAAuB,GAAuB;IACzD,UAAU,EAAE,wBAAwB;IACpC,OAAO,EAAE;QACP;YACE,MAAM,EAAE,cAAc;YACtB,SAAS,EAAE,MAAM;YACjB,aAAa,EAAE,ySAAyS;YACxT,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"relationship-definition.embedded.js","sourceRoot":"","sources":["../../../src/core/relationship/relationship-definition.embedded.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { MetaObject } from "../object/meta-object.js";
|
|
2
|
+
import type { MetaReferenceIdentity } from "../identity/meta-identity.js";
|
|
3
|
+
/**
|
|
4
|
+
* The set of lowercased names a candidate reference answers to: its own name
|
|
5
|
+
* and its FK field, each with and without one stripped suffix.
|
|
6
|
+
*/
|
|
7
|
+
export declare function referencePairingKeys(ref: MetaReferenceIdentity): Set<string>;
|
|
8
|
+
/**
|
|
9
|
+
* Every identity.reference on `holder` whose @references targets `targetEntity`.
|
|
10
|
+
* Package-insensitive on both sides: @references and @objectRef may each be bare
|
|
11
|
+
* or fully qualified.
|
|
12
|
+
*/
|
|
13
|
+
export declare function referenceCandidatesFor(holder: MetaObject, targetEntity: string): MetaReferenceIdentity[];
|
|
14
|
+
/**
|
|
15
|
+
* Which identity.reference does this `@cardinality: one` relationship navigate
|
|
16
|
+
* through? The ladder, in order:
|
|
17
|
+
*
|
|
18
|
+
* 1. exactly one candidate -> that one (the common case; unchanged behaviour)
|
|
19
|
+
* 2. `@sourceRefField` declared -> the candidate whose FK field it names
|
|
20
|
+
* 3. exactly one candidate name-pairs -> that one
|
|
21
|
+
* 4. otherwise -> undefined (caller reports the ambiguity)
|
|
22
|
+
*
|
|
23
|
+
* Returns undefined for "no candidate" and "cannot choose" alike; callers that
|
|
24
|
+
* need to tell them apart use referenceCandidatesFor().
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveRelationshipReference(holder: MetaObject, relationshipName: string, targetEntity: string, sourceRefField?: string): MetaReferenceIdentity | undefined;
|
|
27
|
+
//# sourceMappingURL=resolve-relationship-reference.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-relationship-reference.d.ts","sourceRoot":"","sources":["../../../src/core/relationship/resolve-relationship-reference.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAwB1E;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,qBAAqB,GAAG,GAAG,CAAC,MAAM,CAAC,CAW5E;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,UAAU,EAClB,YAAY,EAAE,MAAM,GACnB,qBAAqB,EAAE,CAOzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,UAAU,EAClB,gBAAgB,EAAE,MAAM,EACxB,YAAY,EAAE,MAAM,EACpB,cAAc,CAAC,EAAE,MAAM,GACtB,qBAAqB,GAAG,SAAS,CAYnC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Association -> identity.reference resolution (issue #368).
|
|
2
|
+
//
|
|
3
|
+
// An entity may declare more than one identity.reference onto the SAME target
|
|
4
|
+
// entity (Match.homeTeamRef and Match.awayTeamRef both -> Team). A
|
|
5
|
+
// `@cardinality: one` relationship names only its target, so when two
|
|
6
|
+
// references match, the target alone cannot say which FK the navigation uses.
|
|
7
|
+
// Taking the first match emits a join on the wrong column that typechecks, has
|
|
8
|
+
// correct DDL and passes verify — so the ladder below resolves it explicitly or
|
|
9
|
+
// not at all. ADR-0029 §5: ambiguity is a load error naming the candidates.
|
|
10
|
+
import { stripPackage } from "../../naming.js";
|
|
11
|
+
/**
|
|
12
|
+
* Trailing suffixes stripped from a CANDIDATE's name/FK field when building its
|
|
13
|
+
* pairing keys. Ordered — first match wins, so "reference" is tested before
|
|
14
|
+
* "ref". Never applied to the relationship name (see referencePairingKeys).
|
|
15
|
+
*/
|
|
16
|
+
const PAIRING_SUFFIXES = ["reference", "ref", "id", "key"];
|
|
17
|
+
function stripOneSuffix(value) {
|
|
18
|
+
for (const suffix of PAIRING_SUFFIXES) {
|
|
19
|
+
if (value.length > suffix.length && value.endsWith(suffix)) {
|
|
20
|
+
return value.slice(0, value.length - suffix.length);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
/** The FK field a reference is anchored on (first field; composite FKs pair on their first column). */
|
|
26
|
+
function refFkField(ref) {
|
|
27
|
+
return ref.fields.length > 0 ? ref.fields[0] : undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The set of lowercased names a candidate reference answers to: its own name
|
|
31
|
+
* and its FK field, each with and without one stripped suffix.
|
|
32
|
+
*/
|
|
33
|
+
export function referencePairingKeys(ref) {
|
|
34
|
+
const keys = new Set();
|
|
35
|
+
const add = (value) => {
|
|
36
|
+
if (!value)
|
|
37
|
+
return;
|
|
38
|
+
const lower = value.toLowerCase();
|
|
39
|
+
keys.add(lower);
|
|
40
|
+
keys.add(stripOneSuffix(lower));
|
|
41
|
+
};
|
|
42
|
+
add(ref.name);
|
|
43
|
+
add(refFkField(ref));
|
|
44
|
+
return keys;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Every identity.reference on `holder` whose @references targets `targetEntity`.
|
|
48
|
+
* Package-insensitive on both sides: @references and @objectRef may each be bare
|
|
49
|
+
* or fully qualified.
|
|
50
|
+
*/
|
|
51
|
+
export function referenceCandidatesFor(holder, targetEntity) {
|
|
52
|
+
const target = stripPackage(targetEntity);
|
|
53
|
+
// ADR-0039: resolving — referenceIdentities() honors references inherited via extends.
|
|
54
|
+
return holder
|
|
55
|
+
.referenceIdentities()
|
|
56
|
+
.filter((ref) => stripPackage(ref.targetEntity ?? "") === target)
|
|
57
|
+
.filter((ref) => refFkField(ref) !== undefined);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Which identity.reference does this `@cardinality: one` relationship navigate
|
|
61
|
+
* through? The ladder, in order:
|
|
62
|
+
*
|
|
63
|
+
* 1. exactly one candidate -> that one (the common case; unchanged behaviour)
|
|
64
|
+
* 2. `@sourceRefField` declared -> the candidate whose FK field it names
|
|
65
|
+
* 3. exactly one candidate name-pairs -> that one
|
|
66
|
+
* 4. otherwise -> undefined (caller reports the ambiguity)
|
|
67
|
+
*
|
|
68
|
+
* Returns undefined for "no candidate" and "cannot choose" alike; callers that
|
|
69
|
+
* need to tell them apart use referenceCandidatesFor().
|
|
70
|
+
*/
|
|
71
|
+
export function resolveRelationshipReference(holder, relationshipName, targetEntity, sourceRefField) {
|
|
72
|
+
const candidates = referenceCandidatesFor(holder, targetEntity);
|
|
73
|
+
if (candidates.length === 0)
|
|
74
|
+
return undefined;
|
|
75
|
+
if (candidates.length === 1)
|
|
76
|
+
return candidates[0];
|
|
77
|
+
if (sourceRefField !== undefined && sourceRefField !== "") {
|
|
78
|
+
return candidates.find((ref) => refFkField(ref) === sourceRefField);
|
|
79
|
+
}
|
|
80
|
+
const wanted = relationshipName.toLowerCase();
|
|
81
|
+
const paired = candidates.filter((ref) => referencePairingKeys(ref).has(wanted));
|
|
82
|
+
return paired.length === 1 ? paired[0] : undefined;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=resolve-relationship-reference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-relationship-reference.js","sourceRoot":"","sources":["../../../src/core/relationship/resolve-relationship-reference.ts"],"names":[],"mappings":"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"}
|