@sap/cds-dk 10.0.4 → 10.0.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 +37 -0
  2. package/bin/debug.js +6 -2
  3. package/bin/export.js +1 -1
  4. package/bin/up.js +1 -1
  5. package/bin/version.js +7 -3
  6. package/bin/watch.js +1 -0
  7. package/lib/build/plugins/hana/index.js +19 -2
  8. package/lib/build/plugins/helm/files/templates/_deployment_helpers.tpl +2 -0
  9. package/lib/build/plugins/nodejs/index.js +6 -3
  10. package/lib/build/util.js +1 -1
  11. package/lib/init/projectReader.js +4 -2
  12. package/lib/init/template/ams/files/containerize.yaml.hbs +1 -0
  13. package/lib/init/template/ams/files/package.json.hbs +1 -1
  14. package/lib/init/template/approuter/files/values.yaml.hbs +9 -1
  15. package/lib/init/template/data/as-csv.js +4 -2
  16. package/lib/init/template/data/as-json.js +4 -1
  17. package/lib/init/template/github-actions/files/workflows/cf.yaml.hbs +6 -6
  18. package/lib/init/template/ias/files/values.yaml.hbs +4 -4
  19. package/lib/init/template/portal/files/values.yaml.hbs +12 -0
  20. package/lib/init/template/sample/files/app/browse/fiori-service.cds +2 -0
  21. package/lib/init/template/test/files/test/test.xs.hbs +3 -2
  22. package/lib/init/template/typer/files/xsconfig.json +1 -1
  23. package/lib/init/template/typescript/files/package.json +1 -1
  24. package/lib/init/template/xsuaa/index.js +11 -7
  25. package/lib/lint/eslintHelp.json +1 -1
  26. package/lib/watch/esm-resolve-registration.js +27 -0
  27. package/lib/watch/vite-watch.js +1 -1
  28. package/node_modules/@sap/cds/CHANGELOG.md +9 -0
  29. package/node_modules/@sap/cds/lib/env/defaults.js +1 -0
  30. package/node_modules/@sap/cds/lib/plugins.js +4 -2
  31. package/node_modules/@sap/cds/lib/srv/srv-handlers.js +1 -1
  32. package/node_modules/@sap/cds/libx/_runtime/common/Service.js +1 -1
  33. package/node_modules/@sap/cds/libx/_runtime/remote/Service.js +1 -1
  34. package/node_modules/@sap/cds/package.json +1 -1
  35. package/node_modules/@sap/cds-compiler/CHANGELOG.md +26 -0
  36. package/node_modules/@sap/cds-compiler/bin/cdsse.js +1 -1
  37. package/node_modules/@sap/cds-compiler/bin/cdsv2m.js +1 -1
  38. package/node_modules/@sap/cds-compiler/doc/IncompatibleChanges_v7.md +20 -13
  39. package/node_modules/@sap/cds-compiler/lib/base/builtins.js +4 -1
  40. package/node_modules/@sap/cds-compiler/lib/base/message-registry.js +15 -2
  41. package/node_modules/@sap/cds-compiler/lib/checks/defaultValues.js +0 -1
  42. package/node_modules/@sap/cds-compiler/lib/compiler/populate.js +1 -0
  43. package/node_modules/@sap/cds-compiler/lib/compiler/resolve.js +7 -6
  44. package/node_modules/@sap/cds-compiler/lib/compiler/tweak-assocs.js +27 -6
  45. package/node_modules/@sap/cds-compiler/lib/edm/annotations/edmJson.js +5 -0
  46. package/node_modules/@sap/cds-compiler/lib/json/from-csn.js +3 -3
  47. package/node_modules/@sap/cds-compiler/lib/json/to-csn.js +5 -4
  48. package/node_modules/@sap/cds-compiler/lib/model/csnUtils.js +13 -8
  49. package/node_modules/@sap/cds-compiler/lib/parsers/AstBuildingParser.js +14 -3
  50. package/node_modules/@sap/cds-compiler/lib/tool-lib/enrichCsn.js +1 -1
  51. package/node_modules/@sap/cds-compiler/lib/transform/db/cdsPersistence.js +3 -2
  52. package/node_modules/@sap/cds-compiler/lib/transform/db/views.js +2 -2
  53. package/node_modules/@sap/cds-compiler/lib/transform/transformUtils.js +1 -2
  54. package/node_modules/@sap/cds-compiler/package.json +1 -1
  55. package/node_modules/media-typer/README.md +15 -14
  56. package/node_modules/media-typer/index.js +0 -8
  57. package/node_modules/media-typer/package.json +16 -12
  58. package/node_modules/ws/lib/receiver.js +15 -32
  59. package/node_modules/ws/lib/websocket-server.js +4 -4
  60. package/node_modules/ws/lib/websocket.js +4 -4
  61. package/node_modules/ws/package.json +5 -1
  62. package/package.json +1 -1
@@ -15,6 +15,32 @@ might use a deprecated flag only for a limited period of time.
15
15
 
16
16
 
17
17
 
18
+ ## Version 7.0.3 - 2026-07-21
19
+
20
+ ### Bug Fixes
21
+
22
+ - **compiler:** When a backlink target is at least distantly related to the entity
23
+ embedding an association with a backlink comparison, the compiler issues a
24
+ warning ref-deprecated-backlink instead of reporting an error ref-invalid-backlink;
25
+ this is now also the case if the target entity is auto-exposed.
26
+ - **sql:** Avoid dump in to.sql.migration with complex models.
27
+
28
+
29
+
30
+ ## Version 7.0.2 - 2026-07-14
31
+
32
+ ### Bug Fixes
33
+
34
+ - **compiler:**
35
+ + Avoid dump for a new association in a projection with backlink comparison while reporting a missing backlink target element.
36
+ + Soften incompatibility concerning backlink comparisons: missing target elements which are referred to in the foreign keys or ON-condition of the backlink
37
+ association now only lead to a warning.
38
+ + Soften incompatibility concerning structure values for annotations: a structure property like `list` won't be rejected; we avoid an ambiguity with an
39
+ _expression_ `(1, 2, 3)` by putting `'=': true` next to `list: …` in the CSN, like in compiler v6. A `xpr` expression is rendered without a sibling `=`,
40
+ though.
41
+
42
+
43
+
18
44
  ## Version 7.0.1 - 2026-06-18
19
45
 
20
46
  ### ⚠ BREAKING CHANGES
@@ -201,7 +201,7 @@ function offset( buf, alsoSuffix ) { // for line and column
201
201
  return false;
202
202
  }
203
203
  let cursor = pos + column - 1;
204
- // eslint-disable-next-line sonarjs/slow-regex
204
+ // eslint-disable-next-line sonarjs/super-linear-regex
205
205
  const prefix = /[a-z_0-9$]*$/i.exec( buf.substring( pos, cursor ) ).index + pos;
206
206
  const col = column + prefix - cursor;
207
207
  if (alsoSuffix)
@@ -39,7 +39,7 @@ function ria() {
39
39
  // 'Choose via $(ANNO) one of $(SORTED_ARTS) as redirection target for $(TARGET) in … $(ART) otherwise'
40
40
  // NOTE: regex match on message text not for productive code!
41
41
  for (const msgObj of msgs) {
42
- // eslint-disable-next-line sonarjs/slow-regex
42
+ // eslint-disable-next-line sonarjs/super-linear-regex
43
43
  const matches = msgObj.message.match( /["“][^"”]+["”]/ug );
44
44
  matches.slice( 1, -2 ).forEach( (name) => {
45
45
  annotates[name.slice( 1, -1 )] = true;
@@ -173,7 +173,7 @@ In cds-compiler v7, we make `name-deprecated-$self` a non-configurable error.
173
173
  Before cds-compiler v7:
174
174
  The CSN representation of an expression-like annotation value is an object with
175
175
  properties `=` and one of `ref`, `xpr`, `val`, `func`, `SELECT`, `list`.
176
- In `=` we capture the (space-normalized, comments removed) string representation of the expression,
176
+ In `=` we capture the (space-normalized, comments removed) string CDL representation of the expression,
177
177
  the other one holds the parsed expression as a token stream.
178
178
 
179
179
  When the annotation is propagated ...
@@ -222,25 +222,32 @@ Resulting CSN:
222
222
  ```
223
223
 
224
224
  In cds-compiler v7:
225
- * For anno-xpr that are a simple ref (simple: can be path, but no filter, no parameters), keep current behavior:
226
- - Keep both `=` and `ref`.
225
+ * For anno-xpr that are a simple ref (simple: can be path, but no filter, no parameters)
226
+ in the compiler and runtime CSN:
227
+ - In `=`, render the `.`-concatenated `ref` items without CDL escaping
228
+ (like for unchecked references, but unlike compiler v6);
229
+ e.g. for a reference, written as `(![in])` in CDL, use `{ '=': 'in', ref: ['in'] }` in CSN.
227
230
  - When rewriting is necessary, also adapt the `=`.
231
+ * For expression which are a simple enum symbols, with or without surrounding parentheses:
232
+ - Just render the `#`, omit the `=` property.
233
+ * For expressions which are represented in CSN by a simple `xpr`:
234
+ - Do not render a `=` property anymore, just render the `xpr`.
228
235
  * For all other expressions:
229
- - Remove the `=`
230
- + both in original and in propagated annotations
231
- + no matter whether rewriting is necessary or not
232
-
233
- With the removal of `=`, any object with a `ref` or `xpr` or `val` inside an array
234
- is regarded as an expression value. This could introduce ambiguities. Outside arrays
235
- there is no danger of ambiguity due to flattening.
236
- To avoid such ambiguities in CDL, the parser in v7 throws an error for "ambiguous" sources like
236
+ - Use `=` property with value `true`, do not use the CDL source as value,
237
+ both in original and in propagated annotations.
238
+
239
+ With the removal of `=`, any object in a CSN with just a `xpr`
240
+ is regarded as an expression value. With that, a structure inside an array
241
+ with just a `xpr` property could not be represented without ambiguity.
242
+
243
+ Therefore, the parser in v7 reports an error for such invalid structure values:
237
244
  ```cds
238
245
  @A_xpr: [(2*foo)]
239
246
  @A_str: [{xpr: [{val: 2}, '*', {ref: ['foo']}]}]
240
247
  entity E { foo : Integer; }
241
248
  ```
242
- For: ref, xpr, val, func, SELECT, list.
243
-
249
+ The compiler also reports an error for structures inside arrays containing a `=` property and
250
+ a primary expression property (`ref`, `xpr`, `list`, `val`, `#`, `func`, `SELECT` and `SET`).
244
251
 
245
252
  ### Default for structures
246
253
 
@@ -107,7 +107,10 @@ const exprProperties = [
107
107
  * Return whether JSON object `val` is a representation for an annotation expression
108
108
  */
109
109
  function isAnnotationExpression( val ) {
110
- return val && primaryExprProperties.some( prop => val[prop] !== undefined );
110
+ return val && (val['=']
111
+ ? primaryExprProperties.some( prop => val[prop] !== undefined )
112
+ : val.xpr !== undefined && Object.keys( val ).length === 1 || val['#'] !== undefined
113
+ );
111
114
  }
112
115
 
113
116
  module.exports = {
@@ -147,6 +147,8 @@ const centralMessages = {
147
147
  'ref-invalid-source': { severity: 'Error' },
148
148
  'ref-invalid-target': { severity: 'Error' },
149
149
  'ref-invalid-backlink': { severity: 'Error', configurableFor: 'before_v8' },
150
+ 'ref-missing-backlink-element': { severity: 'Warning', errorFor: [ 'v8' ] },
151
+ 'ref-deprecated-backlink': { severity: 'Warning', errorFor: [ 'v8' ] },
150
152
  'ref-missing-self-counterpart': { severity: 'Error', configurableFor: true },
151
153
  'ref-sloppy-target': { severity: 'Error', configurableFor: 'deprecated_before_v8' },
152
154
 
@@ -546,8 +548,10 @@ const centralMessageTexts = {
546
548
  colon: 'Use a $(NEWCODE), not a $(CODE) between the element names in a reference',
547
549
  },
548
550
  'syntax-invalid-anno-struct': {
549
- std: 'Most CSN processors interpret a structure with property $(PROP) as expression',
550
- ref: 'The value of property $(PROP) must be a non-empty string, which most CSN processors interpret as reference',
551
+ std: 'Objects with properties $(PROP) and $(SIBLINGPROP) are reserved for expressions and can\'t be used as structures',
552
+ xpr: 'Objects with $(PROP) as only property are reserved for expressions and can\'t be used as structures',
553
+ '#': 'Objects with a property $(PROP) are reserved for enum values and can\'t be used as structures',
554
+ ref: 'The value of property $(PROP) in structures must be a non-empty string',
551
555
  },
552
556
  // 'syntax-ignoring-doc-comment' (Info)
553
557
  'syntax-unexpected-reserved-word': '$(CODE) is a reserved word - write $(DELIMITED) instead if you want to use it as name',
@@ -917,6 +921,7 @@ const centralMessageTexts = {
917
921
  'rewrite-undefined-key': {
918
922
  std: 'Can\'t redirect association $(NAME), as redirection target $(TARGET) does not project element $(ID), which is required to match foreign key of $(NAME)',
919
923
  backlink: 'Can\'t project association, as the current entity does not project element $(ID), which is required to match foreign key of the backlink association',
924
+ // TODO: delete text variant 'backlink' again
920
925
  },
921
926
  'type-unexpected-typeof': {
922
927
  std: 'Unexpected $(KEYWORD) for the type reference here',
@@ -1180,6 +1185,14 @@ const centralMessageTexts = {
1180
1185
  include: 'The open backlink target $(TARGET) of the aspect is unrelated to the current entity',
1181
1186
  backlink: 'Can\'t use $(ART) as backlink association, because its ON-condition contains a backlink comparison',
1182
1187
  },
1188
+ 'ref-deprecated-backlink': {
1189
+ std: 'The target $(TARGET) of the backlink association is just distantly related to the current entity; do not use a comparison with $(ID)',
1190
+ inferred: 'The target $(TARGET) of the backlink association $(NAME) is just distantly related to the current entity; provide an ON-condition not using a comparison with $(ID)',
1191
+ },
1192
+ 'ref-missing-backlink-element': {
1193
+ std: 'Element $(ID) must also be projected when projecting this association containing a backlink comparison',
1194
+ def: 'Element $(ID) must be projected when defining this association containing a backlink comparison',
1195
+ },
1183
1196
  'extend-columns': {
1184
1197
  std: 'Artifact $(ART) can\'t be extended with columns, only simple views/projections without JOINs and UNIONs can',
1185
1198
  join: 'Artifact $(ART) can\'t be extended with columns, because it contains a JOIN',
@@ -23,7 +23,6 @@ function validateDefaultValues( member, memberName, prop, path ) {
23
23
  // TODO: This check only counts the number of leading signs, not inbetween (e.g. 1 - - 1).
24
24
  // The message also needs to be improved.
25
25
  if (i > 1)
26
- // eslint-disable-next-line cds-compiler/message-no-quotes
27
26
  this.error(null, path, {}, 'Illegal number of unary \'+\'/\'-\' operators');
28
27
  }
29
28
  }
@@ -1094,6 +1094,7 @@ function populate( model ) {
1094
1094
  elem._main?._ancestors?.includes( target )) {
1095
1095
  // source and target of the model association are the same entity, and
1096
1096
  // the current main artifact is a suitable auto-redirection target → return it
1097
+ // TODO: check with auto-exposed entities (which do not have _ancestors)
1097
1098
  return elem._main;
1098
1099
  }
1099
1100
  const elemScope = preferredElemScope( target, service, elem, assoc._main || assoc );
@@ -1229,7 +1229,8 @@ function resolve( model ) {
1229
1229
  function checkBacklinkPartner( assoc ) {
1230
1230
  checkBareDollarSelf( assoc );
1231
1231
  // console.log('CBP:',assoc.kind,assoc._main?.name?.id,assoc.name.id,!!assoc.$backlink)
1232
- const target = assoc.$backlink?._origin?.target?._artifact;
1232
+ const origin = assoc.$backlink?._origin;
1233
+ const target = origin?.target?._artifact;
1233
1234
  if (!target)
1234
1235
  return;
1235
1236
  const main = assoc._main;
@@ -1239,7 +1240,7 @@ function resolve( model ) {
1239
1240
  if (chain) // standard correct case
1240
1241
  return;
1241
1242
 
1242
- if (target._ancestors) { // && assocHasForeignKeys( assoc.$backlink?._origin )) {
1243
+ if (target._ancestors || target.$inferred === 'autoexposed') {
1243
1244
  // with non-cyclic projection ancestors, not only for managed assoc, but also
1244
1245
  // unmanaged, see test3/Associations/Backlinks/CrossEyedBacklink.sql-err.cds
1245
1246
  // Current entity is no valid redirection target for the backlink association
@@ -1254,10 +1255,10 @@ function resolve( model ) {
1254
1255
  // TODO: we should do the "simple projection ancestor" probably only if no
1255
1256
  // include is involved in the redirection chain
1256
1257
  if (redirectionChain( null, main, ancestor, true )) {
1257
- warning( 'ref-dubious-backlink', [ assoc.$backlink.location, assoc ],
1258
- { target },
1259
- // eslint-disable-next-line @stylistic/max-len
1260
- 'The target $(TARGET) of the backlink association has just a distant relationship to the current entity' );
1258
+ const msg = (assoc.on.$inferred ? 'inferred' : 'std');
1259
+ message( 'ref-deprecated-backlink', [ assoc.$backlink.location, assoc ],
1260
+ // eslint-disable-next-line @stylistic/object-curly-newline
1261
+ { '#': msg, target, id: '$self', name: origin.name.id } );
1261
1262
  return;
1262
1263
  }
1263
1264
  }
@@ -450,13 +450,24 @@ function tweakAssocs( model ) {
450
450
  if (projectedKey.index > 0)
451
451
  targetElement.path.splice(0, projectedKey.index);
452
452
  }
453
+ else if (elem._outer?.$backlink === elem) {
454
+ const undef = targetElement.path[0];
455
+ setArtifactLink( undef, null );
456
+ setArtifactLink( targetElement, null );
457
+ const outer = elem._outer;
458
+ message( 'ref-missing-backlink-element',
459
+ [ (outer.value || outer.name).location, outer ],
460
+ { '#': (outer.value ? 'std' : 'def'), id: undef.id } );
461
+ // TODO: more info if element was already missing in "intermediate" projection
462
+ // TODO: collect all missing elements?
463
+ }
453
464
  else {
454
465
  setArtifactLink( targetElement.path[0], null );
455
466
  setArtifactLink( targetElement, null );
456
467
 
457
468
  const culprit = !elem.target.$inferred && elem.target ||
458
- elem.value?.path?.[elem.value.path.length - 1] ||
459
- elem;
469
+ elem.value?.path?.[elem.value.path.length - 1] ||
470
+ elem;
460
471
  // TODO: probably better to collect the non-projected foreign keys
461
472
  // and have one message for all
462
473
  error( 'rewrite-undefined-key', [ weakLocation( culprit.location ), elem ], {
@@ -825,8 +836,16 @@ function tweakAssocs( model ) {
825
836
  if (!item)
826
837
  return; // just $self
827
838
  // console.log('REB:',assoc.name?.id,expr.path.map(i=>i.id))
828
- if (item.id === assoc.name.id)
829
- rewritePath( expr, item, assoc, assoc, null );
839
+ if (item.id === assoc.name.id &&
840
+ rewritePath( expr, item, assoc, assoc, null )) {
841
+ const undef = expr.path.find( i => !i._artifact );
842
+ const outer = assoc._outer;
843
+ message( 'ref-missing-backlink-element',
844
+ [ (outer.value || outer.name).location, outer ],
845
+ { '#': (outer.value ? 'std' : 'def'), id: undef.id } );
846
+ // TODO: more info if element was already missing in "intermediate" projection
847
+ // TODO: collect all missing elements?
848
+ }
830
849
  }
831
850
  else if (assoc._main.query) { // from ON cond of mixin element in query
832
851
  // here also $calc
@@ -877,7 +896,7 @@ function tweakAssocs( model ) {
877
896
  element: 'This element is not originally referred to in the ON-condition of association $(MEMBER) of $(ART)',
878
897
  } );
879
898
  }
880
- rewritePath( expr, item, assoc, elem, null );
899
+ rewritePath( expr, item, assoc, elem, null ); // TODO: really no error?
881
900
  }
882
901
  }
883
902
 
@@ -1044,6 +1063,8 @@ function tweakAssocs( model ) {
1044
1063
  * Rewrite the reference `ref` with first elem/mixin ref item `item` for user
1045
1064
  * `assoc` (the query element). `elem` is usually the first (or preferred)
1046
1065
  * query element for item.
1066
+ *
1067
+ * If `location` is falsy, do not issue an error.
1047
1068
  */
1048
1069
  function rewritePath( ref, item, assoc, elem, location ) {
1049
1070
  const { path } = ref;
@@ -1092,7 +1113,7 @@ function tweakAssocs( model ) {
1092
1113
  if (state && state !== true)
1093
1114
  continue;
1094
1115
  if (!state && !location)
1095
- return true;
1116
+ return true; // no rewrite possible
1096
1117
  break;
1097
1118
  }
1098
1119
  }
@@ -978,6 +978,11 @@ function xpr2edmJson( carrier, anno, location, options, messageFunctions, generi
978
978
  return xpr;
979
979
  }
980
980
 
981
+ // "'=': true" has no meaning as an OData operator here and must be removed
982
+ // before transform runs, otherwise transform['='] misinterprets it as $Eq.
983
+ if (xpr['='] === true)
984
+ delete xpr['='];
985
+
981
986
  if (xpr.list)
982
987
  xpr.list.forEach( preTransformExpression );
983
988
 
@@ -117,7 +117,7 @@
117
117
 
118
118
  const { dictAdd } = require('../compiler/dictionaries');
119
119
  const { quotedLiteralPatterns } = require('../compiler/builtins');
120
- const { exprProperties, primaryExprProperties } = require('../base/builtins');
120
+ const { exprProperties, isAnnotationExpression } = require('../base/builtins');
121
121
  const { CompilerAssertion } = require('../base/error');
122
122
  const { Location } = require('../base/location');
123
123
  const { XsnSource } = require('../compiler/xsn-model');
@@ -1490,10 +1490,10 @@ function annoValue( val, spec ) {
1490
1490
  }
1491
1491
  return retval;
1492
1492
  }
1493
- else if (primaryExprProperties.some( prop => val[prop] !== undefined )) {
1493
+ else if (isAnnotationExpression( val )) {
1494
1494
  const s = schema['@'].schema['-expr'];
1495
1495
  const r = object( val, s );
1496
- r.$tokenTexts = true; // property `=` might not be present
1496
+ r.$tokenTexts = true;
1497
1497
  return r;
1498
1498
  }
1499
1499
  else if (val['...'] !== undefined && Object.keys( val ).length === 1) {
@@ -1149,10 +1149,11 @@ function annoValue( node ) {
1149
1149
  if (!node.$tokenTexts)
1150
1150
  return value( node );
1151
1151
  const csn = expression( node ); // expressions in annotation values
1152
- return (!csn?.ref || gensrcFlavor || universalCsn ||
1153
- csn.cast || csn.param || csn.ref.some( id => typeof id !== 'string' ))
1154
- ? csn
1155
- : Object.assign({ '=': csn.ref.join( '.' ) }, csn );
1152
+ if (!csn || csn['#'] || csn.xpr && Object.keys( csn ).length === 1)
1153
+ return csn; // '#' and 'xpr' without sibling '='
1154
+ const asTrue = !csn?.ref || gensrcFlavor || universalCsn ||
1155
+ csn.cast || csn.param || csn.ref.some( id => typeof id !== 'string' );
1156
+ return Object.assign({ '=': !!asTrue || csn.ref.join( '.' ) }, csn );
1156
1157
  }
1157
1158
 
1158
1159
  function value( node ) {
@@ -920,12 +920,10 @@ const _dependents = Symbol('_dependents');
920
920
  * _dependencies: All artifacts this artifact depends on (because it has a ref to it)
921
921
  *
922
922
  * @param {CSN.Model} csn A CSN to enrich in-place
923
- * @param {object} refs csnRefs, only used for artifactRef
924
923
  * @returns {object} CSN with _dependents/_dependencies set, "cleanup" function, _dependents/_dependencies Symbol used
925
924
  */
926
- function setDependencies( csn, refs = csnRefs(csn) ) {
925
+ function setDependencies( csn ) {
927
926
  const cleanup = [];
928
- const { artifactRef } = refs;
929
927
 
930
928
  forEachDefinition(csn, (artifact, artifactName) => {
931
929
  const queries = getNormalizedQuery(artifact).query;
@@ -933,11 +931,18 @@ function setDependencies( csn, refs = csnRefs(csn) ) {
933
931
  initDependencies(artifact);
934
932
  forAllQueries(queries, (query) => {
935
933
  if (query.SELECT?.from) {
936
- if (query.SELECT.from.args)
934
+ if (query.SELECT.from.args) {
937
935
  handleArgs(artifact, artifactName, query.SELECT.from.args);
938
-
939
- else if (typeof query.SELECT.from === 'string' || query.SELECT.from.ref)
940
- handleDependency(artifactRef.from(query.SELECT.from), artifact, artifactName);
936
+ }
937
+ else if (typeof query.SELECT.from === 'string') {
938
+ handleDependency(csn.definitions[query.SELECT.from], artifact, artifactName);
939
+ }
940
+ else if (query.SELECT.from.ref) {
941
+ if (query.SELECT.from.ref[0].id)
942
+ handleDependency(csn.definitions[query.SELECT.from.ref[0].id], artifact, artifactName);
943
+ else
944
+ handleDependency(csn.definitions[query.SELECT.from.ref[0]], artifact, artifactName);
945
+ }
941
946
  }
942
947
  }, [ 'definitions', artifactName, (artifact.projection ? 'projection' : 'query') ]);
943
948
  }
@@ -952,7 +957,7 @@ function setDependencies( csn, refs = csnRefs(csn) ) {
952
957
  if (arg.args)
953
958
  handleArgs(artifact, artifactName, arg.args);
954
959
  else if (arg.ref)
955
- handleDependency(artifactRef.from(arg), artifact, artifactName);
960
+ handleDependency(csn.definitions[arg.ref[0].id ? arg.ref[0].id : arg.ref[0]], artifact, artifactName);
956
961
  }
957
962
  }
958
963
 
@@ -1190,15 +1190,26 @@ class AstBuildingParser extends BaseParser {
1190
1190
  }
1191
1191
 
1192
1192
  checkStructProps( dict ) {
1193
+ // see also isAnnotationExpression()
1194
+ if (!dict['=']) {
1195
+ const prop = dict['#'] && '#' ||
1196
+ dict.xpr && Object.keys( dict ).length === 1 && 'xpr';
1197
+ if (!prop)
1198
+ return;
1199
+ this.error( 'syntax-invalid-anno-struct', dict[prop].name.location,
1200
+ { '#': prop, prop } );
1201
+ return;
1202
+ }
1193
1203
  const prop = primaryExprProperties.find( p => dict[p] !== undefined );
1194
1204
  if (prop) {
1195
1205
  this.error( 'syntax-invalid-anno-struct', dict[prop].name.location,
1196
- { '#': 'std', prop } );
1206
+ { '#': 'std', prop, siblingprop: '=' } );
1197
1207
  }
1198
- else if (dict['='] && (dict['='].literal !== 'string' || !dict['='].val)) {
1208
+ else if (dict['='].literal !== 'string' || !dict['='].val) {
1199
1209
  this.error( 'syntax-invalid-anno-struct', dict['='].location,
1200
1210
  { '#': 'ref', prop: '=' } );
1201
1211
  }
1212
+ // TODO: also check '...' ?
1202
1213
  }
1203
1214
 
1204
1215
  // TODO: remove the check from the parser; move it to shared.js
@@ -1635,7 +1646,7 @@ function addOneForDefinition( count, ext ) {
1635
1646
 
1636
1647
  // Significant digits (before exponent) without leading and trailing zeros
1637
1648
  function relevantDigits( val ) {
1638
- // eslint-disable-next-line sonarjs/slow-regex
1649
+ // eslint-disable-next-line sonarjs/super-linear-regex
1639
1650
  val = val.replace( /e.+$/i, '' ); // this regex has no newlines -> is not slow
1640
1651
 
1641
1652
  const init = /^[-+0.]+/g; // global flag to have lastIndex
@@ -143,7 +143,7 @@ function enrichCsn( csn, options = {} ) {
143
143
  }
144
144
  else {
145
145
  const record = !isAnnotationExpression( obj ) && assignment;
146
- // is record without primary expression property
146
+ // is record without primary expression property and usually '='
147
147
  for (const name of Object.getOwnPropertyNames( obj ) ) {
148
148
  const trans = record || transformers[name] || transformers[name.charAt(0)] || standard;
149
149
  trans( obj, name, obj[name] );
@@ -51,11 +51,12 @@ function getAssocToSkippedIgnorer( csn, options, messageFunctions, csnUtils ) {
51
51
  *
52
52
  * @param {CSN.Artifact} artifact
53
53
  * @param {string} artifactName
54
+ * @param {string} _prop
55
+ * @param {CSN.Path} _path
54
56
  */
55
57
  function ignoreAssociationToSkippedTarget( artifact, artifactName, _prop, _path ) {
56
- if (isPersistedOnDatabase(artifact)) {
58
+ if (isPersistedOnDatabase(artifact))
57
59
  forEachMemberRecursively(artifact, ignore, [ 'definitions', artifactName ]);
58
- }
59
60
  }
60
61
 
61
62
  /**
@@ -130,6 +130,7 @@ function getViewTransformer( csn, options, _messageFunctions ) {
130
130
  * @param {CSN.Element} elem
131
131
  * @param {string} elemName
132
132
  * @param {CSN.Path} elementsPath Path pointing to elements
133
+ * @param {CSN.Path} _queryPath
133
134
  */
134
135
  function handleAssociationElement( query, elements, columnMap, publishedMixins, elem, elemName, elementsPath, _queryPath ) {
135
136
  const isNotMixinByItself = checkIsNotMixinByItself(query, columnMap, elemName);
@@ -391,9 +392,8 @@ function getColumnMap( query, csnUtils ) {
391
392
  function ensureColumnNames( query, csnUtils, _options ) {
392
393
  const select = query.SELECT || query.projection;
393
394
  for (const col of select?.columns || []) {
394
- if (col !== '*' && !columnAlias(col)) {
395
+ if (col !== '*' && !columnAlias(col))
395
396
  setProp(col, 'as', csnUtils.getColumnName(col));
396
- }
397
397
  }
398
398
  }
399
399
 
@@ -218,9 +218,8 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
218
218
  if (flattenedNames.length === 1) {
219
219
  // Single element: propagate default if leaf doesn't have one
220
220
  const singleElem = result[flattenedNames[0]];
221
- if (singleElem.default?.val === undefined) {
221
+ if (singleElem.default?.val === undefined)
222
222
  singleElem.default = elem.default; // Copy entire default object {val, location}
223
- }
224
223
  }
225
224
  else if (flattenedNames.length > 1) {
226
225
  // Multi-element structure: warn that default is ignored
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "7.0.1",
3
+ "version": "7.0.3",
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)",
@@ -1,18 +1,18 @@
1
1
  # media-typer
2
2
 
3
- [![NPM Version][npm-version-image]][npm-url]
4
- [![NPM Downloads][npm-downloads-image]][npm-url]
3
+ [![NPM Version][npm-image]][npm-url]
4
+ [![NPM Downloads][downloads-image]][downloads-url]
5
5
  [![Node.js Version][node-version-image]][node-version-url]
6
- [![Build Status][travis-image]][travis-url]
6
+ [![Build Status][ci-image]][ci-url]
7
7
  [![Test Coverage][coveralls-image]][coveralls-url]
8
8
 
9
9
  Simple RFC 6838 media type parser.
10
10
 
11
- This module will parse a given media type into it's component parts, like type,
11
+ This module will parse a given media type into its component parts, like type,
12
12
  subtype, and suffix. A formatter is also provided to put them back together and
13
13
  the two can be combined to normalize media types into a canonical form.
14
14
 
15
- If you are looking to parse the string that represents a media type and it's
15
+ If you are looking to parse the string that represents a media type and its
16
16
  parameters in HTTP (for example, the `Content-Type` header), use the
17
17
  [content-type module](https://www.npmjs.com/package/content-type).
18
18
 
@@ -75,19 +75,20 @@ If any of the given object values are invalid, then a `TypeError` is thrown.
75
75
  var valid = typer.test('image/svg+xml')
76
76
  ```
77
77
 
78
- Validate a media type string. This will return `true` is the string is a well-
78
+ Validate a media type string. This will return `true` if the string is a well-
79
79
  formatted media type, or `false` otherwise.
80
80
 
81
81
  ## License
82
82
 
83
83
  [MIT](LICENSE)
84
84
 
85
- [coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/media-typer/master
86
- [coveralls-url]: https://coveralls.io/r/jshttp/media-typer?branch=master
87
- [node-version-image]: https://badgen.net/npm/node/media-typer
88
- [node-version-url]: https://nodejs.org/en/download
89
- [npm-downloads-image]: https://badgen.net/npm/dm/media-typer
85
+ [ci-image]: https://img.shields.io/github/workflow/status/jshttp/media-typer/ci/master?label=ci
86
+ [ci-url]: https://github.com/jshttp/media-typer/actions/workflows/ci.yml
87
+ [npm-image]: https://img.shields.io/npm/v/media-typer.svg
90
88
  [npm-url]: https://npmjs.org/package/media-typer
91
- [npm-version-image]: https://badgen.net/npm/v/media-typer
92
- [travis-image]: https://badgen.net/travis/jshttp/media-typer/master
93
- [travis-url]: https://travis-ci.org/jshttp/media-typer
89
+ [node-version-image]: https://img.shields.io/node/v/media-typer.svg
90
+ [node-version-url]: https://nodejs.org/en/
91
+ [coveralls-image]: https://img.shields.io/coveralls/jshttp/media-typer/master.svg
92
+ [coveralls-url]: https://coveralls.io/r/jshttp/media-typer?branch=master
93
+ [downloads-image]: https://img.shields.io/npm/dm/media-typer.svg
94
+ [downloads-url]: https://npmjs.org/package/media-typer
@@ -83,10 +83,6 @@ function format (obj) {
83
83
  */
84
84
 
85
85
  function test (string) {
86
- if (!string) {
87
- throw new TypeError('argument string is required')
88
- }
89
-
90
86
  if (typeof string !== 'string') {
91
87
  throw new TypeError('argument string is required to be a string')
92
88
  }
@@ -103,10 +99,6 @@ function test (string) {
103
99
  */
104
100
 
105
101
  function parse (string) {
106
- if (!string) {
107
- throw new TypeError('argument string is required')
108
- }
109
-
110
102
  if (typeof string !== 'string') {
111
103
  throw new TypeError('argument string is required to be a string')
112
104
  }
@@ -1,20 +1,24 @@
1
1
  {
2
2
  "name": "media-typer",
3
3
  "description": "Simple RFC 6838 media type parser and formatter",
4
- "version": "1.1.0",
4
+ "version": "1.1.1",
5
5
  "author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
6
6
  "license": "MIT",
7
7
  "repository": "jshttp/media-typer",
8
+ "funding": {
9
+ "type": "opencollective",
10
+ "url": "https://opencollective.com/express"
11
+ },
8
12
  "devDependencies": {
9
- "eslint": "5.16.0",
10
- "eslint-config-standard": "12.0.0",
11
- "eslint-plugin-import": "2.17.2",
12
- "eslint-plugin-markdown": "1.0.0",
13
- "eslint-plugin-node": "8.0.1",
14
- "eslint-plugin-promise": "4.1.1",
15
- "eslint-plugin-standard": "4.0.0",
16
- "mocha": "6.1.4",
17
- "nyc": "14.0.0"
13
+ "eslint": "7.23.0",
14
+ "eslint-config-standard": "14.1.1",
15
+ "eslint-plugin-import": "2.22.1",
16
+ "eslint-plugin-markdown": "1.0.2",
17
+ "eslint-plugin-node": "11.1.0",
18
+ "eslint-plugin-promise": "4.3.1",
19
+ "eslint-plugin-standard": "4.1.0",
20
+ "mocha": "9.2.0",
21
+ "nyc": "15.1.0"
18
22
  },
19
23
  "files": [
20
24
  "LICENSE",
@@ -27,7 +31,7 @@
27
31
  "scripts": {
28
32
  "lint": "eslint --plugin markdown --ext js,md .",
29
33
  "test": "mocha --reporter spec --check-leaks --bail test/",
30
- "test-cov": "nyc --reporter=html --reporter=text npm test",
31
- "test-travis": "nyc --reporter=text npm test"
34
+ "test-ci": "nyc --reporter=lcov --reporter=text npm test",
35
+ "test-cov": "nyc --reporter=html --reporter=text npm test"
32
36
  }
33
37
  }