@sap/cds-compiler 6.3.6 → 6.4.6

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +101 -3
  2. package/LICENSE +32 -0
  3. package/README.md +14 -2
  4. package/bin/cdsse.js +0 -3
  5. package/doc/CHANGELOG_BETA.md +1 -1
  6. package/doc/CHANGELOG_DEPRECATED.md +1 -1
  7. package/lib/base/message-registry.js +9 -2
  8. package/lib/base/messages.js +1 -1
  9. package/lib/base/model.js +2 -0
  10. package/lib/checks/existsExpressionsOnlyForeignKeys.js +16 -10
  11. package/lib/checks/existsMustEndInAssoc.js +1 -1
  12. package/lib/checks/existsMustNotStartWithDollarSelf.js +31 -0
  13. package/lib/checks/validator.js +4 -2
  14. package/lib/compiler/assert-consistency.js +3 -2
  15. package/lib/compiler/builtins.js +5 -6
  16. package/lib/compiler/checks.js +37 -26
  17. package/lib/compiler/define.js +1 -1
  18. package/lib/compiler/extend.js +39 -50
  19. package/lib/compiler/finalize-parse-cdl.js +1 -1
  20. package/lib/compiler/lsp-api.js +1 -1
  21. package/lib/compiler/populate.js +2 -2
  22. package/lib/compiler/propagator.js +29 -6
  23. package/lib/compiler/resolve.js +13 -3
  24. package/lib/compiler/shared.js +157 -133
  25. package/lib/compiler/tweak-assocs.js +87 -29
  26. package/lib/compiler/xpr-rewrite.js +164 -160
  27. package/lib/edm/annotations/edmJson.js +206 -37
  28. package/lib/edm/csn2edm.js +13 -0
  29. package/lib/edm/edmUtils.js +2 -2
  30. package/lib/gen/BaseParser.js +106 -72
  31. package/lib/gen/CdlGrammar.checksum +1 -1
  32. package/lib/gen/CdlParser.js +1501 -1509
  33. package/lib/json/to-csn.js +8 -5
  34. package/lib/language/genericAntlrParser.js +0 -0
  35. package/lib/main.js +19 -16
  36. package/lib/model/csnRefs.js +589 -521
  37. package/lib/model/csnUtils.js +8 -5
  38. package/lib/model/enrichCsn.js +1 -0
  39. package/lib/parsers/AstBuildingParser.js +73 -28
  40. package/lib/render/toCdl.js +2 -1
  41. package/lib/render/toHdbcds.js +6 -3
  42. package/lib/render/toSql.js +5 -0
  43. package/lib/transform/db/applyTransformations.js +1 -1
  44. package/lib/transform/db/assertUnique.js +4 -1
  45. package/lib/transform/db/assocsToQueries/transformExists.js +3 -10
  46. package/lib/transform/db/assocsToQueries/utils.js +0 -5
  47. package/lib/transform/db/cdsPersistence.js +17 -18
  48. package/lib/transform/db/expansion.js +179 -3
  49. package/lib/transform/db/flattening.js +16 -5
  50. package/lib/transform/db/rewriteCalculatedElements.js +79 -283
  51. package/lib/transform/effective/main.js +8 -1
  52. package/lib/transform/forOdata.js +1 -1
  53. package/lib/transform/forRelationalDB.js +21 -80
  54. package/lib/transform/localized.js +75 -127
  55. package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +89 -63
  56. package/lib/transform/transformUtils.js +23 -21
  57. package/lib/transform/translateAssocsToJoins.js +7 -5
  58. package/lib/transform/tupleExpansion.js +16 -3
  59. package/package.json +3 -3
  60. package/doc/DeprecatedOptions_v2.md +0 -150
  61. package/doc/NameResolution.md +0 -837
  62. package/lib/transform/parseExpr.js +0 -415
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  const edmUtils = require('../edmUtils.js');
4
- const { parseExpr } = require('../../transform/parseExpr');
5
4
  const {
6
5
  EdmTypeFacetMap,
7
6
  EdmTypeFacetNames,
@@ -9,6 +8,7 @@ const {
9
8
  } = require('../EdmPrimitiveTypeDefinitions.js');
10
9
  const { isBuiltinType, isAnnotationExpression } = require('../../base/builtins');
11
10
  const { transformExpression } = require('../../transform/db/applyTransformations.js');
11
+ const { conditionAsTree, expressionAsTree } = require('../../model/xprAsTree');
12
12
 
13
13
  /**
14
14
  * Translate a given token stream expression into an edmJson representation
@@ -77,7 +77,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
77
77
  };
78
78
  //----------------------------------
79
79
  // Error transformer
80
- const notADynExpr = (parent, op, xpr, csnPath, parentparent, parentprop, txt) => {
80
+ const notADynExpr = (parent, op, xpr, csnPath, parentParent, parentProp, txt) => {
81
81
  error('odata-anno-xpr', location, {
82
82
  anno, op: txt ?? op, '#': 'notadynexpr',
83
83
  });
@@ -104,15 +104,15 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
104
104
 
105
105
  //----------------------------------
106
106
  // list is a $Collection => []
107
- transform.list = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
108
- parentparent[parentprop] = xpr.filter(a => a);
109
- transformExpression(parentparent, parentprop, transform);
107
+ transform.list = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
108
+ parentParent[parentProp] = xpr.filter(a => a);
109
+ transformExpression(parentParent, parentProp, transform);
110
110
  };
111
111
  // XPR
112
- transform.xpr = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
112
+ transform.xpr = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
113
113
  // eliminate 'xpr' node by pulling up xpr node to its parent
114
- parentparent[parentprop] = xpr;
115
- transformExpression(parentparent, parentprop, transform);
114
+ parentParent[parentProp] = xpr;
115
+ transformExpression(parentParent, parentProp, transform);
116
116
  };
117
117
  //----------------------------------
118
118
  // CASE
@@ -150,7 +150,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
150
150
  };
151
151
  //----------------------------------
152
152
  // Cast => $Cast
153
- transform.cast = (parent, prop, castExpr, csnPath, parentparent, parentprop) => {
153
+ transform.cast = (parent, prop, castExpr, csnPath, parentParent, parentProp) => {
154
154
  const csnType = castExpr[0];
155
155
  // try to resolve to final scalar base type and use that instead of derived type
156
156
  if (!isBuiltinType(csnType.type)) {
@@ -184,8 +184,8 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
184
184
  typeFunc.args.push( { func: 'Scale', args: [ { val: csnType.scale } ] });
185
185
 
186
186
 
187
- parentparent[parentprop] = castFunc;
188
- transformExpression(parentparent, parentprop, transform);
187
+ parentParent[parentProp] = castFunc;
188
+ transformExpression(parentParent, parentProp, transform);
189
189
  };
190
190
  //----------------------------------
191
191
  const evalArgs = (argDef, args, propName) => {
@@ -261,11 +261,11 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
261
261
  transformExpression(parent, undefined, transform);
262
262
  };
263
263
  transform.$In = noOp;
264
- transform.between = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
264
+ transform.between = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
265
265
  evalArgs({ exact: 2 }, xpr.slice(1), prop);
266
266
  transformExpression(xpr, undefined, transform);
267
267
  delete parent[prop];
268
- parentparent[parentprop]
268
+ parentParent[parentProp]
269
269
  = {
270
270
  $And: [
271
271
  { $Le: [ xpr[1], xpr[0] ] },
@@ -273,23 +273,23 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
273
273
  ],
274
274
  };
275
275
  };
276
- transform['||'] = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
276
+ transform['||'] = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
277
277
  evalArgs({ exact: 2 }, xpr, prop);
278
278
  transformExpression(xpr, undefined, transform);
279
279
  delete parent[prop];
280
- parentparent[parentprop].$Apply = xpr;
281
- parentparent[parentprop].$Function = 'odata.concat';
280
+ parentParent[parentProp].$Apply = xpr;
281
+ parentParent[parentProp].$Function = 'odata.concat';
282
282
  };
283
283
  //----------------------------------
284
284
  // ARITHMETICAL AND UNARY
285
- transform['+'] = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
285
+ transform['+'] = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
286
286
  if (Array.isArray(xpr)) {
287
287
  op('$Add')(parent, prop, xpr);
288
288
  }
289
289
  else {
290
290
  delete parent[prop];
291
- parentparent[parentprop] = xpr;
292
- transformExpression(parentparent, parentprop, transform);
291
+ parentParent[parentProp] = xpr;
292
+ transformExpression(parentParent, parentProp, transform);
293
293
  }
294
294
  };
295
295
  transform.$Add = noOp;
@@ -305,19 +305,19 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
305
305
  // $Div, $Mod are functions
306
306
  //----------------------------------
307
307
  // LITERALS
308
- transform.val = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
308
+ transform.val = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
309
309
  if (xpr === null)
310
310
  parent.$Null = true;
311
311
  else
312
- parentparent[parentprop] = xpr;
312
+ parentParent[parentProp] = xpr;
313
313
  };
314
- transform['#'] = (parent, prop, xpr, csnPath, parentParent, parentprop, txt) => {
314
+ transform['#'] = (parent, prop, xpr, csnPath, parentParent, parentProp, txt) => {
315
315
  if (parent['#'] && parent.val) // enum reference that was resolved by the compiler
316
316
  delete parent['#'];
317
317
  else
318
- notADynExpr(parent, prop, xpr, csnPath, parentParent, parentprop, txt);
318
+ notADynExpr(parent, prop, xpr, csnPath, parentParent, parentProp, txt);
319
319
  };
320
- transform.ref = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
320
+ transform.ref = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
321
321
  // until empty filter syntax is introduced for the annotation expressions,
322
322
  // we ignore the filters in order to generate EDMX
323
323
  if (xpr.some(ps => ps.args/* || ps.where */)) {
@@ -328,11 +328,11 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
328
328
  const [ head, ...tail ] = xpr;
329
329
  if ((head.id || head) === '$self')
330
330
  xpr = tail;
331
- parentparent[parentprop] = { $Path: xpr.map(ps => ps.id || ps).join('/') };
331
+ parentParent[parentProp] = { $Path: xpr.map(ps => ps.id || ps).join('/') };
332
332
  };
333
333
  //----------------------------------
334
334
  // Functions
335
- transform.func = (parent, prop, xpr, csnPath, parentparent, parentprop) => {
335
+ transform.func = (parent, prop, xpr, csnPath, parentParent, parentProp) => {
336
336
  const rewriteArgs = (argDefs, evalVal = true) => {
337
337
  Object.entries(argDefs).forEach(([ argName, argDef ]) => {
338
338
  const [ foundProps, newArgs ]
@@ -722,8 +722,8 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
722
722
  UrlRef: [ oneArg, dollar ],
723
723
  // $Record ???
724
724
  $Collection: () => {
725
- standard(parentparent, parentprop);
726
- transformExpression(parentparent, parentprop, transform);
725
+ standard(parentParent, parentProp);
726
+ transformExpression(parentParent, parentProp, transform);
727
727
  },
728
728
  $Path: () => {
729
729
  oneArg(parent, xpr);
@@ -733,7 +733,7 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
733
733
  anno, op: `${ xpr }(…)`, meta: 'string', '#': 'wrongval_meta',
734
734
  });
735
735
  }
736
- transformExpression(parentparent, parentprop, transform);
736
+ transformExpression(parentParent, parentProp, transform);
737
737
  },
738
738
  $Null: () => {
739
739
  parent[xpr] = true;
@@ -766,11 +766,11 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
766
766
  }
767
767
  else {
768
768
  evalArgs(argDef, parent.args, xpr);
769
- parentparent[parentprop].$Apply = [ ...(parent.args || []) ];
770
- parentparent[parentprop].$Function = funcName;
771
- delete parentparent[parentprop].func;
772
- delete parentparent[parentprop].args;
773
- transformExpression(parentparent, parentprop, transform);
769
+ parentParent[parentProp].$Apply = [ ...(parent.args || []) ];
770
+ parentParent[parentProp].$Function = funcName;
771
+ delete parentParent[parentProp].func;
772
+ delete parentParent[parentProp].args;
773
+ transformExpression(parentParent, parentProp, transform);
774
774
  }
775
775
  }
776
776
  else {
@@ -783,13 +783,182 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions ) {
783
783
  };
784
784
 
785
785
  return transformExpression(carrier, anno, {
786
- '=': (parent, prop, xpr, csnPath, parentparent, parentprop) => {
786
+ '=': (parent, prop, xpr, csnPath, parentParent, parentProp) => {
787
787
  if (isAnnotationExpression(parent)) {
788
788
  delete parent['='];
789
- parentparent[parentprop] = transformExpression({ $edmJson: parseExpr( parent, { array: false }) }, undefined, transform);
789
+ const edmJson = preTransformXpr(parent);
790
+ parentParent[parentProp] = transformExpression({ $edmJson: edmJson }, undefined, transform);
790
791
  }
791
792
  },
792
- });
793
+ }, location);
794
+
795
+ /**
796
+ * Pre-transform the CSN expression into a tree structure for further EDM processing.
797
+ * Example:
798
+ * `[…, '+' …]` -> `{'+': [ …, … ]}`
799
+ *
800
+ * @param {object|Array} xpr
801
+ */
802
+ function preTransformXpr( xpr ) {
803
+ xpr = Array.isArray(xpr) ? conditionAsTree(xpr) : expressionAsTree(xpr);
804
+ xpr = preTransformExpression(xpr);
805
+ if (Array.isArray(xpr)) {
806
+ xpr = xpr.flat(Infinity);
807
+ return (xpr.length === 1) ? xpr[0] : xpr;
808
+ }
809
+ return xpr;
810
+ }
811
+
812
+ /**
813
+ * Reject any invalid expression such as `1 +`.
814
+ */
815
+ function rejectInvalidExpression( expr ) {
816
+ if (expr.length === 3 && expr[1] === 'is' && expr[2] === 'null') {
817
+ messageFunctions.error('odata-anno-xpr', location, {
818
+ '#': 'notadynexpr', anno, op: 'is null',
819
+ });
820
+ return;
821
+ }
822
+ if (expr.length === 4 && expr[1] === 'is' && expr[2] === 'not' && expr[3] === 'null') {
823
+ messageFunctions.error('odata-anno-xpr', location, {
824
+ '#': 'notadynexpr', anno, op: 'is not null',
825
+ });
826
+ return;
827
+ }
828
+ if (expr.length === 4 && expr[1] === 'not' && expr[2] === 'like') {
829
+ messageFunctions.error('odata-anno-xpr', location, {
830
+ '#': 'notadynexpr', anno, op: 'not like',
831
+ });
832
+ return;
833
+ }
834
+ if (expr.length === 3 && expr[1] === 'like') {
835
+ messageFunctions.error('odata-anno-xpr', location, {
836
+ '#': 'notadynexpr', anno, op: 'not like',
837
+ });
838
+ return;
839
+ }
840
+
841
+ // Any left-over operator is not recognized: reject it
842
+ for (let i = 0; i < expr.length; i++) {
843
+ if (expr[i] === 'not' && typeof expr[i + 1] === 'string')
844
+ ++i;
845
+
846
+ if (typeof expr[i] === 'string') {
847
+ messageFunctions.error('odata-anno-xpr', location, {
848
+ '#': 'invalid',
849
+ anno,
850
+ op: expr[i],
851
+ });
852
+ break;
853
+ }
854
+ }
855
+ }
856
+
857
+ /**
858
+ * Pre-transform a _structurized_ expression.
859
+ */
860
+ function preTransformExpression( xpr ) {
861
+ if (!xpr || typeof xpr !== 'object')
862
+ return xpr;
863
+
864
+ if (Array.isArray(xpr)) {
865
+ xpr = xpr.map(preTransformExpression); // not `preTransformXpr`, as that would re-structurize the array
866
+
867
+ if (xpr.length === 1)
868
+ return xpr[0]; // single entry, e.g. via structurizer
869
+
870
+ if (xpr[0]?.toLowerCase?.() === 'case')
871
+ return preTransformCase(xpr);
872
+
873
+ if (xpr[1]?.toLowerCase?.() === 'between')
874
+ return preTransformBetween(xpr);
875
+
876
+ if (xpr[1]?.toLowerCase?.() === 'not' &&
877
+ xpr[2]?.toLowerCase?.() === 'between')
878
+ return { not: preTransformExpression([ xpr[0], ...xpr.slice(2) ]) };
879
+
880
+ if (xpr[1]?.toLowerCase?.() === 'not' &&
881
+ xpr[2]?.toLowerCase?.() === 'in')
882
+ return { not: preTransformExpression([ xpr[0], ...xpr.slice(2) ]) };
883
+
884
+ // unary operators
885
+ if (xpr.length === 2 && (xpr[0] === '+' || xpr[0] === '-' || xpr[0] === 'not' || xpr[0] === 'new'))
886
+ return { [xpr[0]]: xpr[1] };
887
+
888
+ if (xpr.length === 3 && xpr[1] === 'is' && xpr[2] === 'null') {
889
+ rejectInvalidExpression(xpr);
890
+ return xpr;
891
+ }
892
+
893
+ // binary operators: '=', '<>', 'like', ...
894
+ if (xpr.length === 3 && typeof xpr[1] === 'string')
895
+ return { [xpr[1]]: [ xpr[0], xpr[2] ] };
896
+
897
+ rejectInvalidExpression(xpr);
898
+
899
+ return xpr;
900
+ }
901
+
902
+ if (xpr.list)
903
+ xpr.list.forEach( preTransformExpression );
904
+
905
+ if (xpr.args) {
906
+ if (!Array.isArray( xpr.args ))
907
+ Object.values( xpr.args ).forEach(preTransformExpression );
908
+ else if (xpr.args.length)
909
+ xpr.args.forEach( preTransformExpression );
910
+ }
911
+ if (xpr.ref)
912
+ xpr.ref.forEach( preTransformExpression );
913
+
914
+ if (xpr.xpr)
915
+ xpr.xpr = preTransformExpression( xpr.xpr );
916
+
917
+ if (xpr.cast) {
918
+ const castKeys = Object.keys(xpr).filter(k => k !== 'cast');
919
+ if (castKeys.length === 1)
920
+ return { cast: [ xpr.cast, { [castKeys[0]]: xpr[castKeys[0]] } ] };
921
+ }
922
+
923
+ return xpr;
924
+ }
925
+
926
+ function preTransformBetween( xpr ) {
927
+ return {
928
+ between: [
929
+ xpr[0],
930
+ xpr[2],
931
+ xpr[4],
932
+ ],
933
+ };
934
+ }
935
+
936
+ function preTransformCase( xpr ) {
937
+ const caseObj = [ ];
938
+ let pos = 1;
939
+
940
+ // CASE val WHEN val THEN …
941
+ if (xpr[pos]?.toLowerCase?.() !== 'when') {
942
+ caseObj.push( xpr[pos] );
943
+ pos++;
944
+ }
945
+
946
+ while (xpr[pos]?.toLowerCase?.() === 'when') {
947
+ if (xpr[pos + 2]?.toLowerCase?.() !== 'then')
948
+ return xpr;
949
+ caseObj.push({ when: [ xpr[pos + 1], xpr[pos + 3] ] });
950
+ pos += 4;
951
+ }
952
+ if (xpr[pos]?.toLowerCase?.() === 'else' ) {
953
+ caseObj.push(xpr[pos + 1]);
954
+ pos += 2;
955
+ }
956
+ if (xpr[pos++]?.toLowerCase?.() !== 'end')
957
+ return xpr;
958
+ return {
959
+ case: caseObj,
960
+ };
961
+ }
793
962
  }
794
963
 
795
964
  // Not everything that can occur in OData annotations can be expressed with
@@ -688,6 +688,11 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
688
688
  props.push(new Edm.Property(v, { Name: elementName }, elementCsn));
689
689
  }
690
690
  }
691
+
692
+ if (options.isV2() && isBetaEnabled(options, 'draftAdminDataHiddenFilter')) {
693
+ if (elementCsn._edmParentCsn.name === `${ elementCsn._edmParentCsn.$mySchemaName }.DraftAdministrativeData`)
694
+ elementCsn['@UI.HiddenFilter'] ??= true;
695
+ }
691
696
  });
692
697
  }
693
698
  if (options.isV2()) {
@@ -723,6 +728,8 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
723
728
 
724
729
  // add bound/unbound actions/functions for V4
725
730
  function createActionV4( actionCsn, _name, entityCsn = undefined ) {
731
+ if (actionCsn['@cds.api.ignore'])
732
+ return;
726
733
  const iAmAnAction = actionCsn.kind === 'action';
727
734
  const actionName = edmUtils.getBaseName(actionCsn.name);
728
735
  const attributes = { Name: actionName, IsBound: false };
@@ -837,6 +844,8 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
837
844
  // Parameter Nodes
838
845
  if (actionCsn.params) {
839
846
  forEach(actionCsn.params, ( parameterName, parameterCsn ) => {
847
+ if (parameterCsn['@cds.api.ignore'])
848
+ return;
840
849
  const p = new Edm.Parameter(v, { Name: parameterName }, parameterCsn );
841
850
  const pLoc = [ ...location, 'params', p._edmAttributes.Name ];
842
851
  if (!edmUtils.isODataSimpleIdentifier(parameterName))
@@ -861,6 +870,8 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
861
870
 
862
871
  // add bound/unbound actions/functions for V2
863
872
  function createActionV2( actionCsn, name, entityCsn = undefined ) {
873
+ if (actionCsn['@cds.api.ignore'])
874
+ return;
864
875
  /** @type {object} */
865
876
  const attributes = { Name: name.replace(schemaNamePrefix, '') };
866
877
  const functionImport = new Edm.FunctionImport(v, attributes );
@@ -924,6 +935,8 @@ function csn2edmAll( _csn, _options, serviceNames, messageFunctions ) {
924
935
  // the client assume that Nullable is false.... Correct Nullable Handling is done inside Parameter constructor
925
936
  if (actionCsn.params) {
926
937
  Object.entries(actionCsn.params).forEach(([ parameterName, parameterCsn ], i) => {
938
+ if (parameterCsn['@cds.api.ignore'])
939
+ return;
927
940
  const type = parameterCsn?.items?.type || parameterCsn?.type;
928
941
  if (i === 0 && type === special$self) {
929
942
  // skip and remove the first parameter if it is a $self binding parameter to
@@ -292,7 +292,7 @@ function finalizeReferentialConstraints( csn, assocCsn, options, info ) {
292
292
  } );
293
293
  }
294
294
  // Remove all target elements that are not key in the principal entity
295
- // and all elements that annotated with '@cds.api.ignore'
295
+ // and all elements that are annotated with '@cds.api.ignore'
296
296
  const remainingPrincipalRefs = [];
297
297
  foreach(assocCsn._constraints.constraints,
298
298
  (c) => {
@@ -359,7 +359,7 @@ function finalizeReferentialConstraints( csn, assocCsn, options, info ) {
359
359
  // If FK is key in target => constraint
360
360
  // Don't consider primary key associations (fks become keys on the source entity) as
361
361
  // this would impose a constraint against the target.
362
- // Filter out all elements that annotated with '@cds.api.ignore'
362
+ // Filter out all elements that are annotated with '@cds.api.ignore'
363
363
 
364
364
  // In structured format, foreign keys of managed associations are never rendered, so
365
365
  // there are no constraints for them.