@sap/cds-compiler 6.9.0 → 6.9.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/CHANGELOG.md CHANGED
@@ -13,6 +13,19 @@ we might not list every change in its behavior here.
13
13
  Productive code should never require a `beta` flag to be set, and
14
14
  might use a deprecated flag only for a limited period of time.
15
15
 
16
+ ## Version 6.9.1 - 2026-05-05
17
+
18
+ ### Bug Fixes
19
+
20
+ - **compiler:**
21
+ + make an element added via `extend` correctly shadow an element from an include
22
+ + do not issue a warning for a correct use of `$projection`
23
+ - **odata:**
24
+ + do not generate wrong ReferentialConstraints for unmanaged Composition without a partner
25
+ + render Partner attribute on forward association correctly
26
+
27
+
28
+
16
29
  ## Version 6.9.0 - 2026-04-21
17
30
 
18
31
  ### Features
@@ -1138,7 +1138,6 @@ const centralMessageTexts = {
1138
1138
  'old-not-target': 'Expected element $(NAME) not to be an association, because it overrides the included element from $(ART)',
1139
1139
  },
1140
1140
 
1141
- 'ref-expecting-$self': 'Use $(NEWCODE) instead of $(CODE) here or remove $(CODE) altogether if possible; the compiler has rewritten it to $(NEWCODE) in CSN',
1142
1141
  'ref-expecting-assoc': {
1143
1142
  std: 'Expecting path $(ELEMREF) following “EXISTS” predicate to end with association/composition',
1144
1143
  'with-type': 'Expecting path $(ELEMREF) following “EXISTS” predicate to end with association/composition, found $(TYPE)',
@@ -1173,7 +1173,8 @@ function extend( model ) {
1173
1173
  }
1174
1174
  if (art._extensions?.$add)
1175
1175
  extendArtifact( art._extensions.$add, art );
1176
- // TODO: for proper shadowing: first collect defined element & action names
1176
+ checkRedefinitionThroughIncludes( art, 'elements' );
1177
+ checkRedefinitionThroughIncludes( art, 'actions' );
1177
1178
  }
1178
1179
 
1179
1180
  /**
@@ -1497,6 +1498,9 @@ function extend( model ) {
1497
1498
  }
1498
1499
 
1499
1500
  const existing = parent[prop]?.[name];
1501
+ const shadowsIncludeMember = construct !== parent &&
1502
+ elem.$inferred !== 'include' &&
1503
+ existing?.$inferred === 'include';
1500
1504
  const add = construct !== parent && (!existing || elem.$inferred !== 'include');
1501
1505
  if (!add && existing?.$inferred === 'include' && elem.$inferred === 'include') {
1502
1506
  includeCollisions.push( {
@@ -1507,9 +1511,17 @@ function extend( model ) {
1507
1511
  const { $duplicates } = elem;
1508
1512
  if ($duplicates === true && add)
1509
1513
  elem.$duplicates = null;
1510
- setMemberParent( elem, name, parent, add && prop );
1511
- if (!$duplicates) // not already reported
1512
- checkRedefinition( elem );
1514
+ if (add && elem.$inferred === 'include' && Array.isArray( $duplicates ))
1515
+ elem.$duplicates = null;
1516
+ if (shadowsIncludeMember) {
1517
+ parent[prop][name] = elem;
1518
+ setMemberParent( elem, name, parent );
1519
+ }
1520
+ else {
1521
+ setMemberParent( elem, name, parent, add && prop );
1522
+ if (!$duplicates) // not already reported
1523
+ checkRedefinition( elem );
1524
+ }
1513
1525
  initMembers( elem, elem, elem._block );
1514
1526
  if (elem.kind === 'action' || elem.kind === 'function')
1515
1527
  initBoundSelfParam( elem.params, elem._main );
@@ -1740,9 +1752,6 @@ function extend( model ) {
1740
1752
  } );
1741
1753
  }
1742
1754
  }
1743
-
1744
- checkRedefinitionThroughIncludes( parent, prop );
1745
-
1746
1755
  if (!hasNewElement && members) {
1747
1756
  ext[prop] = members;
1748
1757
  }
@@ -1788,7 +1797,8 @@ function extend( model ) {
1788
1797
 
1789
1798
  /**
1790
1799
  * Report duplicates in parent[prop] that happen due to multiple includes having the
1791
- * same member. Covers `entity G : E, G {};` but not `entity G : E {}; extend G with F;`.
1800
+ * same member. Run after includes and extends so shadowing members have already
1801
+ * replaced any include-derived survivors in-place.
1792
1802
  */
1793
1803
  function checkRedefinitionThroughIncludes( parent, prop ) {
1794
1804
  if (!parent[prop])
@@ -984,8 +984,6 @@ function fns( model ) {
984
984
  art.kind === '$self' && path[0].id === '$projection') {
985
985
  // Rewrite $projection to $self
986
986
  path[0].id = '$self';
987
- warning( 'ref-expecting-$self', [ path[0].location, user ],
988
- { code: '$projection', newcode: '$self' });
989
987
  }
990
988
  return art.name?.$inferred !== '$internal'; // not a compiler-generated internal alias
991
989
  }
@@ -472,8 +472,10 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
472
472
 
473
473
  navigationProperties.forEach((np) => {
474
474
  if (options.isV4()) {
475
- // V4: No referential constraints for Containment Relationships
476
- if ((!np.isContainment() || (options.renderForeignKeys)) && !np.isToMany())
475
+ // V4: No referential constraints for Containment Relationships or
476
+ // unmanaged Compositions without a partner
477
+ const isUnmanagedCompositionWithoutPartner = np._csn.type === 'cds.Composition' && np._csn.on && !np._csn._constraints._partnerCsn;
478
+ if ((!np.isContainment() || (options.renderForeignKeys)) && !np.isToMany() && !isUnmanagedCompositionWithoutPartner)
477
479
  np.addReferentialConstraintNodes();
478
480
  }
479
481
  else {
package/lib/edm/edm.js CHANGED
@@ -1053,11 +1053,14 @@ function getEdm( options ) {
1053
1053
  delete this._edmAttributes.Nullable;
1054
1054
  }
1055
1055
  // we have exactly one selfReference or the default partner
1056
-
1057
- if ( !csn.$noPartner) {
1056
+ // $noPartner can be set when a second projection creates an ambiguous backlink, but if exactly
1057
+ // one self-reference targets the same entity as this association, that unambiguous partner still applies.
1058
+ const selfRefsToTarget = csn._selfReferences.filter(ref => ref.$abspath[0] === csn._target?.name);
1059
+ const isPartner = selfRefsToTarget.length === 1 && selfRefsToTarget[0]._constraints?._partnerCsn === csn;
1060
+ if (!csn.$noPartner || isPartner) {
1058
1061
  const partner = csn._selfReferences.length === 1
1059
1062
  ? csn._selfReferences[0]
1060
- : csn._constraints._partnerCsn;
1063
+ : selfRefsToTarget[0] || csn._constraints._partnerCsn;
1061
1064
  if (partner && partner['@odata.navigable'] !== false && this._csn._edmParentCsn.kind !== 'type') {
1062
1065
  // $abspath[0] is main entity
1063
1066
  this._edmAttributes.Partner = partner.$abspath.slice(1).join('/');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "6.9.0",
3
+ "version": "6.9.1",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",