@sap/cds-dk 10.0.3 → 10.0.5

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 (82) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/bin/debug.js +6 -2
  3. package/bin/up.js +1 -1
  4. package/bin/watch.js +1 -0
  5. package/lib/build/plugins/helm/files/templates/_deployment_helpers.tpl +2 -0
  6. package/lib/build/plugins/nodejs/index.js +6 -3
  7. package/lib/build/util.js +1 -1
  8. package/lib/init/template/ams/files/containerize.yaml.hbs +1 -0
  9. package/lib/init/template/approuter/files/values.yaml.hbs +9 -1
  10. package/lib/init/template/data/as-csv.js +4 -2
  11. package/lib/init/template/data/as-json.js +4 -1
  12. package/lib/init/template/github-actions/files/workflows/cf.yaml.hbs +6 -6
  13. package/lib/init/template/ias/files/values.yaml.hbs +4 -4
  14. package/lib/init/template/portal/files/values.yaml.hbs +12 -0
  15. package/lib/init/template/sample/files/app/browse/fiori-service.cds +2 -0
  16. package/lib/init/template/typer/files/xsconfig.json +1 -1
  17. package/lib/init/template/typescript/files/package.json +1 -1
  18. package/lib/lint/eslintHelp.json +1 -1
  19. package/lib/watch/esm-resolve-registration.js +27 -0
  20. package/lib/watch/vite-watch.js +1 -1
  21. package/node_modules/@cap-js/db-service/CHANGELOG.md +768 -0
  22. package/node_modules/@cap-js/db-service/LICENSE +201 -0
  23. package/node_modules/@cap-js/db-service/README.md +26 -0
  24. package/node_modules/@cap-js/db-service/index.js +19 -0
  25. package/node_modules/@cap-js/db-service/lib/InsertResults.js +122 -0
  26. package/node_modules/@cap-js/db-service/lib/SQLService.js +581 -0
  27. package/node_modules/@cap-js/db-service/lib/common/DatabaseService.js +179 -0
  28. package/node_modules/@cap-js/db-service/lib/common/factory.d.ts +5 -0
  29. package/node_modules/@cap-js/db-service/lib/common/generic-pool.js +310 -0
  30. package/node_modules/@cap-js/db-service/lib/common/session-context.js +46 -0
  31. package/node_modules/@cap-js/db-service/lib/converters.d.ts +24 -0
  32. package/node_modules/@cap-js/db-service/lib/cql-functions.js +464 -0
  33. package/node_modules/@cap-js/db-service/lib/cqn2pql.js +116 -0
  34. package/node_modules/@cap-js/db-service/lib/cqn2sql.js +1612 -0
  35. package/node_modules/@cap-js/db-service/lib/cqn4sql.js +2895 -0
  36. package/node_modules/@cap-js/db-service/lib/deep-queries.js +290 -0
  37. package/node_modules/@cap-js/db-service/lib/fill-in-keys.js +75 -0
  38. package/node_modules/@cap-js/db-service/lib/infer/cqn.d.ts +42 -0
  39. package/node_modules/@cap-js/db-service/lib/infer/index.js +1290 -0
  40. package/node_modules/@cap-js/db-service/lib/infer/join-tree.js +278 -0
  41. package/node_modules/@cap-js/db-service/lib/infer/pseudos.js +24 -0
  42. package/node_modules/@cap-js/db-service/lib/search.js +220 -0
  43. package/node_modules/@cap-js/db-service/lib/utils.js +177 -0
  44. package/node_modules/@cap-js/db-service/package.json +36 -0
  45. package/node_modules/@cap-js/sqlite/CHANGELOG.md +404 -0
  46. package/node_modules/@cap-js/sqlite/LICENSE +201 -0
  47. package/node_modules/@cap-js/sqlite/README.md +34 -0
  48. package/node_modules/@cap-js/sqlite/cds-plugin.js +0 -0
  49. package/node_modules/@cap-js/sqlite/index.js +2 -0
  50. package/node_modules/@cap-js/sqlite/lib/SQLiteService.js +324 -0
  51. package/node_modules/@cap-js/sqlite/lib/cql-functions.js +160 -0
  52. package/node_modules/@cap-js/sqlite/lib/node-sqlite.js +36 -0
  53. package/node_modules/@cap-js/sqlite/lib/session.json +3 -0
  54. package/node_modules/@cap-js/sqlite/lib/sql.js.js +99 -0
  55. package/node_modules/@cap-js/sqlite/package.json +68 -0
  56. package/node_modules/@sap/cds-compiler/CHANGELOG.md +14 -0
  57. package/node_modules/@sap/cds-compiler/bin/cdsse.js +1 -1
  58. package/node_modules/@sap/cds-compiler/bin/cdsv2m.js +1 -1
  59. package/node_modules/@sap/cds-compiler/doc/IncompatibleChanges_v7.md +20 -13
  60. package/node_modules/@sap/cds-compiler/lib/base/builtins.js +4 -1
  61. package/node_modules/@sap/cds-compiler/lib/base/message-registry.js +15 -2
  62. package/node_modules/@sap/cds-compiler/lib/checks/defaultValues.js +0 -1
  63. package/node_modules/@sap/cds-compiler/lib/compiler/resolve.js +6 -5
  64. package/node_modules/@sap/cds-compiler/lib/compiler/tweak-assocs.js +27 -6
  65. package/node_modules/@sap/cds-compiler/lib/edm/annotations/edmJson.js +5 -0
  66. package/node_modules/@sap/cds-compiler/lib/json/from-csn.js +3 -3
  67. package/node_modules/@sap/cds-compiler/lib/json/to-csn.js +5 -4
  68. package/node_modules/@sap/cds-compiler/lib/parsers/AstBuildingParser.js +14 -3
  69. package/node_modules/@sap/cds-compiler/lib/tool-lib/enrichCsn.js +1 -1
  70. package/node_modules/@sap/cds-compiler/lib/transform/db/cdsPersistence.js +3 -2
  71. package/node_modules/@sap/cds-compiler/lib/transform/db/views.js +2 -2
  72. package/node_modules/@sap/cds-compiler/lib/transform/transformUtils.js +1 -2
  73. package/node_modules/@sap/cds-compiler/package.json +1 -1
  74. package/node_modules/iconv-lite/encodings/sbcs-data.js +2 -0
  75. package/node_modules/iconv-lite/encodings/utf32.js +10 -3
  76. package/node_modules/iconv-lite/package.json +2 -2
  77. package/node_modules/iconv-lite/types/encodings.d.ts +2 -0
  78. package/node_modules/ws/lib/receiver.js +15 -32
  79. package/node_modules/ws/lib/websocket-server.js +4 -4
  80. package/node_modules/ws/lib/websocket.js +4 -4
  81. package/node_modules/ws/package.json +5 -1
  82. package/package.json +5 -3
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@cap-js/sqlite",
3
+ "version": "3.0.2",
4
+ "description": "CDS database service for SQLite",
5
+ "homepage": "https://github.com/cap-js/cds-dbs/tree/main/sqlite#cds-database-service-for-sqlite",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/cap-js/cds-dbs.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/cap-js/cds-dbs/issues"
12
+ },
13
+ "keywords": [
14
+ "CAP",
15
+ "CDS",
16
+ "SQLite"
17
+ ],
18
+ "author": "SAP SE (https://www.sap.com)",
19
+ "main": "index.js",
20
+ "files": [
21
+ "cds-plugin.js",
22
+ "lib",
23
+ "CHANGELOG.md"
24
+ ],
25
+ "scripts": {
26
+ "test": "cds-test"
27
+ },
28
+ "dependencies": {
29
+ "@cap-js/db-service": "^3.0.1"
30
+ },
31
+ "peerDependencies": {
32
+ "@sap/cds": "^10",
33
+ "sql.js": "^1.13.0",
34
+ "better-sqlite3": "^12.0.0"
35
+ },
36
+ "peerDependenciesMeta": {
37
+ "sql.js": {
38
+ "optional": true
39
+ },
40
+ "better-sqlite3": {
41
+ "optional": true
42
+ }
43
+ },
44
+ "cds": {
45
+ "requires": {
46
+ "kinds": {
47
+ "sql": {
48
+ "[development]": {
49
+ "kind": "sqlite",
50
+ "credentials": {
51
+ "url": ":memory:"
52
+ }
53
+ }
54
+ },
55
+ "sqlite": {
56
+ "impl": "@cap-js/sqlite",
57
+ "pool": {
58
+ "evictionRunIntervalMillis": 0,
59
+ "min": 1,
60
+ "max": 1
61
+ }
62
+ }
63
+ },
64
+ "db": "sql"
65
+ }
66
+ },
67
+ "license": "Apache-2.0"
68
+ }
@@ -15,6 +15,20 @@ might use a deprecated flag only for a limited period of time.
15
15
 
16
16
 
17
17
 
18
+ ## Version 7.0.2 - 2026-07-14
19
+
20
+ ### Bug Fixes
21
+
22
+ - **compiler:**
23
+ + Avoid dump for a new association in a projection with backlink comparison while reporting a missing backlink target element.
24
+ + Soften incompatibility concerning backlink comparisons: missing target elements which are referred to in the foreign keys or ON-condition of the backlink
25
+ association now only lead to a warning.
26
+ + Soften incompatibility concerning structure values for annotations: a structure property like `list` won't be rejected; we avoid an ambiguity with an
27
+ _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 `=`,
28
+ though.
29
+
30
+
31
+
18
32
  ## Version 7.0.1 - 2026-06-18
19
33
 
20
34
  ### ⚠ 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
  }
@@ -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;
@@ -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 ) {
@@ -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.2",
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)",
@@ -109,6 +109,8 @@ module.exports = {
109
109
 
110
110
  hebrew: "iso88598",
111
111
  hebrew8: "iso88598",
112
+ iso88598i: "iso88598",
113
+ iso88598e: "iso88598",
112
114
 
113
115
  turkish: "iso88599",
114
116
  turkish8: "iso88599",
@@ -31,7 +31,9 @@ function Utf32Encoder (options, codec) {
31
31
 
32
32
  Utf32Encoder.prototype.write = function (str) {
33
33
  var src = Buffer.from(str, "ucs2")
34
- var dst = Buffer.alloc(src.length * 2)
34
+ // src.length * 2 covers this chunk's code units (4 bytes each); the extra 4 bytes leave room for a
35
+ // high surrogate held over from a previous chunk, which is flushed ahead of this chunk's units.
36
+ var dst = Buffer.alloc(src.length * 2 + 4)
35
37
  var write32 = this.isLE ? dst.writeUInt32LE : dst.writeUInt32BE
36
38
  var offset = 0
37
39
 
@@ -113,9 +115,9 @@ Utf32Decoder.prototype.write = function (src) {
113
115
  // NOTE: codepoint is a signed int32 and can be negative.
114
116
  // NOTE: We copied this block from below to help V8 optimize it (it works with array, not buffer).
115
117
  if (isLE) {
116
- codepoint = overflow[i] | (overflow[i + 1] << 8) | (overflow[i + 2] << 16) | (overflow[i + 3] << 24)
118
+ codepoint = overflow[0] | (overflow[1] << 8) | (overflow[2] << 16) | (overflow[3] << 24)
117
119
  } else {
118
- codepoint = overflow[i + 3] | (overflow[i + 2] << 8) | (overflow[i + 1] << 16) | (overflow[i] << 24)
120
+ codepoint = overflow[3] | (overflow[2] << 8) | (overflow[1] << 16) | (overflow[0] << 24)
119
121
  }
120
122
  overflow.length = 0
121
123
 
@@ -169,7 +171,12 @@ function _writeCodepoint (dst, offset, codepoint, badChar) {
169
171
  };
170
172
 
171
173
  Utf32Decoder.prototype.end = function () {
174
+ if (this.overflow.length === 0) { return }
175
+
176
+ // A leftover, incomplete 4-byte code unit at the end of the input is ill-formed. Substitute a
177
+ // single U+FFFD (Unicode Standard conformance clause C10) instead of silently dropping the bytes.
172
178
  this.overflow.length = 0
179
+ return String.fromCharCode(this.badChar)
173
180
  }
174
181
 
175
182
  // == UTF-32 Auto codec =============================================================
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "iconv-lite",
3
3
  "description": "Convert character encodings in pure javascript.",
4
- "version": "0.7.2",
4
+ "version": "0.7.3",
5
5
  "license": "MIT",
6
6
  "keywords": [
7
7
  "iconv",
@@ -46,7 +46,7 @@
46
46
  "stream": false
47
47
  },
48
48
  "devDependencies": {
49
- "@arethetypeswrong/cli": "^0.17.4",
49
+ "@arethetypeswrong/cli": "^0.18.4",
50
50
  "@stylistic/eslint-plugin": "^5.1.0",
51
51
  "@stylistic/eslint-plugin-js": "^4.1.0",
52
52
  "@types/node": "^24.0.12",
@@ -276,6 +276,8 @@ export type Encoding =
276
276
  | "iso88596"
277
277
  | "iso88597"
278
278
  | "iso88598"
279
+ | "iso88598e"
280
+ | "iso88598i"
279
281
  | "iso88599"
280
282
  | "isoceltic"
281
283
  | "isoir100"
@@ -77,6 +77,7 @@ class Receiver extends Writable {
77
77
 
78
78
  this._totalPayloadLength = 0;
79
79
  this._messageLength = 0;
80
+ this._numFragments = 0;
80
81
  this._fragments = [];
81
82
 
82
83
  this._errored = false;
@@ -500,6 +501,19 @@ class Receiver extends Writable {
500
501
  return;
501
502
  }
502
503
 
504
+ if (this._maxFragments > 0 && ++this._numFragments > this._maxFragments) {
505
+ const error = this.createError(
506
+ RangeError,
507
+ 'Too many message fragments',
508
+ false,
509
+ 1008,
510
+ 'WS_ERR_TOO_MANY_BUFFERED_PARTS'
511
+ );
512
+
513
+ cb(error);
514
+ return;
515
+ }
516
+
503
517
  if (this._compressed) {
504
518
  this._state = INFLATING;
505
519
  this.decompress(data, cb);
@@ -507,22 +521,6 @@ class Receiver extends Writable {
507
521
  }
508
522
 
509
523
  if (data.length) {
510
- if (
511
- this._maxFragments > 0 &&
512
- this._fragments.length >= this._maxFragments
513
- ) {
514
- const error = this.createError(
515
- RangeError,
516
- 'Too many message fragments',
517
- false,
518
- 1008,
519
- 'WS_ERR_TOO_MANY_BUFFERED_PARTS'
520
- );
521
-
522
- cb(error);
523
- return;
524
- }
525
-
526
524
  //
527
525
  // This message is not compressed so its length is the sum of the payload
528
526
  // length of all fragments.
@@ -562,22 +560,6 @@ class Receiver extends Writable {
562
560
  return;
563
561
  }
564
562
 
565
- if (
566
- this._maxFragments > 0 &&
567
- this._fragments.length >= this._maxFragments
568
- ) {
569
- const error = this.createError(
570
- RangeError,
571
- 'Too many message fragments',
572
- false,
573
- 1008,
574
- 'WS_ERR_TOO_MANY_BUFFERED_PARTS'
575
- );
576
-
577
- cb(error);
578
- return;
579
- }
580
-
581
563
  this._fragments.push(buf);
582
564
  }
583
565
 
@@ -604,6 +586,7 @@ class Receiver extends Writable {
604
586
  this._totalPayloadLength = 0;
605
587
  this._messageLength = 0;
606
588
  this._fragmented = 0;
589
+ this._numFragments = 0;
607
590
  this._fragments = [];
608
591
 
609
592
  if (this._opcode === 2) {