@sap/cds-dk 10.0.4 → 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 (43) hide show
  1. package/CHANGELOG.md +22 -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/@sap/cds-compiler/CHANGELOG.md +14 -0
  22. package/node_modules/@sap/cds-compiler/bin/cdsse.js +1 -1
  23. package/node_modules/@sap/cds-compiler/bin/cdsv2m.js +1 -1
  24. package/node_modules/@sap/cds-compiler/doc/IncompatibleChanges_v7.md +20 -13
  25. package/node_modules/@sap/cds-compiler/lib/base/builtins.js +4 -1
  26. package/node_modules/@sap/cds-compiler/lib/base/message-registry.js +15 -2
  27. package/node_modules/@sap/cds-compiler/lib/checks/defaultValues.js +0 -1
  28. package/node_modules/@sap/cds-compiler/lib/compiler/resolve.js +6 -5
  29. package/node_modules/@sap/cds-compiler/lib/compiler/tweak-assocs.js +27 -6
  30. package/node_modules/@sap/cds-compiler/lib/edm/annotations/edmJson.js +5 -0
  31. package/node_modules/@sap/cds-compiler/lib/json/from-csn.js +3 -3
  32. package/node_modules/@sap/cds-compiler/lib/json/to-csn.js +5 -4
  33. package/node_modules/@sap/cds-compiler/lib/parsers/AstBuildingParser.js +14 -3
  34. package/node_modules/@sap/cds-compiler/lib/tool-lib/enrichCsn.js +1 -1
  35. package/node_modules/@sap/cds-compiler/lib/transform/db/cdsPersistence.js +3 -2
  36. package/node_modules/@sap/cds-compiler/lib/transform/db/views.js +2 -2
  37. package/node_modules/@sap/cds-compiler/lib/transform/transformUtils.js +1 -2
  38. package/node_modules/@sap/cds-compiler/package.json +1 -1
  39. package/node_modules/ws/lib/receiver.js +15 -32
  40. package/node_modules/ws/lib/websocket-server.js +4 -4
  41. package/node_modules/ws/lib/websocket.js +4 -4
  42. package/node_modules/ws/package.json +5 -1
  43. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,28 @@ This project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  The format is based on [Keep a Changelog](https://keepachangelog.com/).
8
8
 
9
+ ## Version 10.0.5 - 2026-07-16
10
+
11
+ ### Fixed
12
+
13
+ - `cds add typer` now generates a `jsconfig.json` file that works with VS Code.
14
+ - `cds debug <app> --force` on Kyma now preserves existing `NODE_OPTIONS` flags (e.g. `--max-old-space-size`) instead of overwriting them when enabling the inspector.
15
+ - `cds add sample` now selects `stock` in the Books list and object pages, so the overstock discount shows up in the UI.
16
+ - `cds add data` now generates correct foreign-key columns for nested compositions of aspects, e.g. `up__up__ID,up__ID,ID,name` instead of a broken `up__up_` header.
17
+ - `cds build` no longer fails with `npm ls -ws: unknown option` on npm 12, which dropped the short `-ws` flag.
18
+ - `cds build` now packages transitive npm workspace dependencies again on npm 12, which changed `npm pack --json` to emit an object keyed by package name instead of an array.
19
+ - `cds up` uses `helm --rollback-on-failure` instead of the deprecated `--atomic`.
20
+ - `cds add portal` on Java + IAS disables JWT proof-of-possession on `srv` (`SAP_SPRING_SECURITY_IDENTITY_PROOFTOKEN=false`) so Portal-proxied tile OData works.
21
+ - `cds add ias` forwards client certificates from the approuter to `srv-api` (`backendDestinations.srv-api.forwardAuthCertificates`).
22
+ - `cds add ias` always enables `xsuaa-cross-consumption` in the identity service parameters.
23
+ - `cds add ias` no longer sets an mTLS `expose.host` on the approuter, restoring a plain approuter host for the browser flow.
24
+ - `cds add portal` on Java multitenant now binds the Portal service instance to `srv` so CAP Java's `getDependencies` reports Portal to the SaaS Registry.
25
+ - `cds add kyma` uses a tenant-agnostic (tcpSocket) startup probe for the multitenant approuter, avoiding 400s from `TENANT_HOST_PATTERN` on kubelet probes.
26
+ - `cds add kyma` no longer emits the dead `Authentication: OAuth2UserTokenExchange` key on `backendDestinations.srv-api`.
27
+ - `cds add github-actions` uses updated versions.
28
+ - `cds watch` now works in ESM scenarios where only a global `cds-dk` is installed, where it would formerly fail with `ERR_MODULE_NOT_FOUND` for `@sap/cds`.
29
+ - `cds watch`no longer shows a `'server.hmr.protocol/host/port/path/clientPort/timeout/server' is deprecated` warning when running a Vite application.
30
+
9
31
  ## Version 10.0.4 - 2026-07-08
10
32
 
11
33
  ### Fixed
package/bin/debug.js CHANGED
@@ -101,7 +101,11 @@ async function debug() {
101
101
 
102
102
  if (force) {
103
103
  console.log(`\nEnabling Node inspector on deployment/${appName} at 0.0.0.0:${portLocal} ...`)
104
- _execSyncSafe(`kubectl ${ns} set env deployment/${appName} NODE_OPTIONS="--inspect=0.0.0.0:${portLocal}"`, { stdio: 'inherit' })
104
+ const existingOpts = _execSyncSafe(`kubectl ${ns} get deploy ${appName} -o jsonpath="{.spec.template.spec.containers[0].env[?(@.name=='NODE_OPTIONS')].value}"`)
105
+ const inspectFlag = `--inspect=0.0.0.0:${portLocal}`
106
+ const stripped = existingOpts ? existingOpts.replace(/--inspect(-brk)?(=\S+)?\s*/g, '').trim() : ''
107
+ const newOpts = stripped ? `${stripped} ${inspectFlag}` : inspectFlag
108
+ _execSyncSafe(`kubectl ${ns} set env deployment/${appName} NODE_OPTIONS="${newOpts.replace(/"/g, '\\"')}"`, { stdio: 'inherit' })
105
109
  _execSyncSafe(`kubectl ${ns} rollout restart deployment/${appName}`, { stdio: 'inherit' })
106
110
  k(`rollout status deployment/${appName} --timeout=180s`, { stdio: 'inherit' })
107
111
  } else {
@@ -281,7 +285,7 @@ async function debug() {
281
285
  return _execSync(...args)
282
286
  } catch { /* ignore */ }
283
287
  }
284
- function _execSync(cmd, options={ encoding: 'utf-8', shell: true }) {
288
+ function _execSync(cmd, options={ encoding: 'utf-8', shell: true, maxBuffer: 10 * 1024 * 1024 }) {
285
289
  try {
286
290
  return execSync(cmd, options)?.trim()
287
291
  } catch (err) {
package/bin/up.js CHANGED
@@ -346,7 +346,7 @@ async function up() {
346
346
  }
347
347
  })
348
348
 
349
- let helmUpgrade = `helm upgrade --install ${appName} ./gen/chart ${nsHelm} --atomic --wait --wait-for-jobs --timeout=10m`
349
+ let helmUpgrade = `helm upgrade --install ${appName} ./gen/chart ${nsHelm} --rollback-on-failure --wait --wait-for-jobs --timeout=20m`
350
350
  if (hasXsuaa) helmArgs.push(' --set-file xsuaa.jsonParameters=xs-security.json')
351
351
  if (helmArgs.length > 0) helmUpgrade += ` ${helmArgs.join(' ')}`
352
352
  run(helmUpgrade)
package/bin/watch.js CHANGED
@@ -148,6 +148,7 @@ async function watch ([cwd], {
148
148
  // if sap/cds is not installed locally, run with cds-dk's deps. (sap/cds, sqlite etc)
149
149
  if (!installed('@sap/cds', cwd)) {
150
150
  env.NODE_PATH = [...paths(cwd), ...paths(__dirname)].join(delimiter)
151
+ env.NODE_OPTIONS = [env.NODE_OPTIONS, `--import ${require.resolve('../lib/watch/esm-resolve-registration.js')}`].filter(Boolean).join(' ')
151
152
  }
152
153
 
153
154
  const delay = parseInt(process.env.CDS_WATCH_DELAY) || 200
@@ -88,6 +88,8 @@ Backend Destinations
88
88
  {{- end -}}
89
89
  {{- $currentDestination := dict $nameKey $destination.name $urlKey (print $protocol "://" $destinationHost ) -}}
90
90
  {{- $currentDestination := merge $currentDestination $.defaultParameters -}}
91
+ {{- $extras := omit $destination "name" "service" "external" -}}
92
+ {{- $currentDestination := merge $extras $currentDestination -}}
91
93
  {{- $destinations = (append $destinations $currentDestination) -}}
92
94
  {{- end -}}
93
95
  {{- $destinations | toJson }}
@@ -296,14 +296,17 @@ module.exports = class NodejsBuildPlugin extends require('../edmx') {
296
296
  * @param {import('fs').PathLike} dest - destination folder
297
297
  * @param {import('fs').PathLike} wsRoot - workspace root folder
298
298
  * @param {string[]} workspaces - list of workspace names
299
- * @returns {Promise<JSON>} - returns a promise that resolves to the result of the command
299
+ * @returns {Promise<object[]>} - array of pack descriptors ({ name, filename, ... })
300
300
  * @private
301
301
  */
302
302
  static async _execNpmPack(dest, wsRoot, workspaces) {
303
- const args = ['pack', '-ws', '--json', `--pack-destination=${dest}`]
303
+ const args = ['pack', '--ws', '--json', `--pack-destination=${dest}`]
304
304
  workspaces.forEach(w => args.push(`-w=${w}`))
305
305
  const result = await cmd.spawnCommand('npm', args, { cwd: wsRoot }, true, !!LOG._debug)
306
306
  DEBUG?.(result)
307
- return JSON.parse(result)
307
+ const parsed = JSON.parse(result)
308
+ // `npm pack --json` returns an array up to npm 11, but an object keyed by
309
+ // package name as of npm 12 — normalize to an array of descriptors.
310
+ return Array.isArray(parsed) ? parsed : Object.values(parsed)
308
311
  }
309
312
  }
package/lib/build/util.js CHANGED
@@ -300,7 +300,7 @@ async function _execNpmWs(wsRoot) {
300
300
  /** @type {string[]} */
301
301
  let result = []
302
302
  try {
303
- const cmdLine = 'npm ls -ws --json'
303
+ const cmdLine = 'npm ls --ws --json'
304
304
  DEBUG?.(`execute ${cmdLine}`)
305
305
  // @ts-expect-error - TODO: shell should be a string. But can probably be removed altogether?
306
306
  const { stdout } = await execAsync(cmdLine, { shell: IS_WIN, stdio: ['inherit', 'pipe', 'inherit'], cwd: wsRoot })
@@ -17,4 +17,5 @@ modules:
17
17
  {{/isJava}}
18
18
  env:
19
19
  BP_NODE_RUN_SCRIPTS: ""
20
+ BP_NODE_VERSION: "24"
20
21
 
@@ -16,11 +16,20 @@ approuter:
16
16
  ephemeral-storage: 1G
17
17
  cpu: 500m
18
18
  memory: 500M
19
+ {{^hasMultitenancy}}
19
20
  health:
20
21
  liveness:
21
22
  path: /
22
23
  readiness:
23
24
  path: /
25
+ {{/hasMultitenancy}}
26
+ {{#hasMultitenancy}}
27
+ health:
28
+ liveness:
29
+ path: ""
30
+ readiness:
31
+ path: ""
32
+ {{/hasMultitenancy}}
24
33
  {{=<% %>=}}
25
34
  envFrom:
26
35
  - configMapRef:
@@ -29,7 +38,6 @@ approuter:
29
38
  backendDestinations:
30
39
  srv-api:
31
40
  service: srv
32
- Authentication: OAuth2UserTokenExchange
33
41
  {{#hasMultitenancy}}
34
42
  mtx-api:
35
43
  service: {{#isJava}}srv{{/isJava}}{{^isJava}}sidecar{{/isJava}}
@@ -37,10 +37,12 @@ module.exports = async (data={}, csn, { headerOnly=false, separator=',' }={}) =>
37
37
  if (element.on) {
38
38
  continue // skip unmanaged associations with explicit on condition
39
39
  }
40
+ // flatten nested foreign keys (composite keys of nested aspects) into up__up__ID columns
41
+ const {headers: fkHeaders, records: fkRecords} = resolveStruct(value, name)
40
42
  if (i === 0) {
41
- header.push(...Object.keys(value).map(foreignKey => name + '_' + foreignKey))
43
+ header.push(...fkHeaders)
42
44
  }
43
- record.push(...Object.values(value))
45
+ record.push(...fkRecords)
44
46
  }
45
47
  else if (element.is_struct && element.type !== 'cds.Map') {
46
48
  const {headers: structHeaders, records: structRecords} = resolveStruct(value, name);
@@ -257,7 +257,10 @@ function dataForField(element, artifact, csn, record, data, parent, depth, refer
257
257
 
258
258
  const [ownKey, ownKeyStructured, targetKey] = findKeysInOnCondition(element, target)
259
259
  if (ownKey && targetKey) {
260
- const value = ownKeyStructured ? { [ownKey]: record[ownKey] } : record[ownKey]
260
+ // carry over all parent keys, not just the first, so composite/nested keys survive
261
+ const value = ownKeyStructured
262
+ ? Object.fromEntries(Object.keys(artifact.keys).map(k => [k, record[k]]))
263
+ : record[ownKey]
261
264
  targetRecords.forEach(targetRecord => {
262
265
  // Set parent in target as non-enumerable. Json serialization doesn't need it, but csv does.
263
266
  Object.defineProperty(targetRecord, targetKey, { value, enumerable: false })
@@ -44,13 +44,13 @@ jobs:
44
44
  echo "${PWD}/apache-maven-${MAVEN_VERSION}/bin" >> "$GITHUB_PATH"
45
45
  {{/sap}}
46
46
  {{/isJava}}
47
- - uses: cap-js/cf-setup@v1
47
+ - uses: cap-js/cf-setup@v2
48
48
  with:
49
- cf-api: {{#literal}}${{ vars.CF_API }}{{/literal}}
50
- cf-org: {{#literal}}${{ vars.CF_ORG }}{{/literal}}
51
- cf-space: {{#literal}}${{ vars.CF_SPACE }}{{/literal}}
52
- cf-username: {{#literal}}${{ vars.CF_USERNAME }}{{/literal}}
53
- cf-password: {{#literal}}${{ secrets.CF_PASSWORD }}{{/literal}}
49
+ api: {{#literal}}${{ vars.CF_API }}{{/literal}}
50
+ org: {{#literal}}${{ vars.CF_ORG }}{{/literal}}
51
+ space: {{#literal}}${{ vars.CF_SPACE }}{{/literal}}
52
+ username: {{#literal}}${{ vars.CF_USERNAME }}{{/literal}}
53
+ password: {{#literal}}${{ secrets.CF_PASSWORD }}{{/literal}}
54
54
  - run: npm install
55
55
  - run: npx cds up
56
56
 
@@ -13,14 +13,16 @@ sidecar:
13
13
  {{#hasApprouter}}
14
14
  approuter:
15
15
  expose:
16
- gateway: {{#literal}}"{{ .Release.Namespace }}/kyma-mtls-gateway"{{/literal}}
17
- host: {{#literal}}"{{ .Release.Name }}-approuter-{{ .Release.Namespace }}.cert"{{/literal}}
16
+ host: {{#literal}}"{{ .Release.Name }}-approuter-{{ .Release.Namespace }}"{{/literal}}
18
17
  bindings:
19
18
  auth:
20
19
  serviceInstanceName: identity
21
20
  parameters:
22
21
  credential-type: X509_GENERATED
23
22
  app-identifier: approuter
23
+ backendDestinations:
24
+ srv-api:
25
+ forwardAuthCertificates: true
24
26
  {{/hasApprouter}}
25
27
  srv:
26
28
  expose:
@@ -41,9 +43,7 @@ identity:
41
43
  {{#hasMultitenancy}}
42
44
  multi-tenant: true
43
45
  {{/hasMultitenancy}}
44
- {{#hasXsuaa}}
45
46
  xsuaa-cross-consumption: true
46
- {{/hasXsuaa}}
47
47
  provided-apis:
48
48
  - name: {{appName}}-ias-api
49
49
  description: API exposed by the application
@@ -6,9 +6,21 @@ approuter:
6
6
  portal:
7
7
  serviceOfferingName: portal
8
8
  servicePlanName: standard
9
+ {{#isJava}}{{#hasIas}}
10
+ srv:
11
+ env:
12
+ # Portal Service proxies tile OData without forwarding the client cert.
13
+ SAP_SPRING_SECURITY_IDENTITY_PROOFTOKEN: "false"
14
+ {{/hasIas}}{{/isJava}}
9
15
  {{#hasMultitenancy}}
10
16
  sidecar:
11
17
  bindings:
12
18
  portal:
13
19
  serviceInstanceName: portal
20
+ {{#isJava}}
21
+ srv:
22
+ bindings:
23
+ portal:
24
+ serviceInstanceName: portal
25
+ {{/isJava}}
14
26
  {{/hasMultitenancy}}
@@ -27,6 +27,7 @@ annotate CatalogService.Books with @(UI : {
27
27
  Value : currency.symbol,
28
28
  Label : '{i18n>Currency}'
29
29
  },
30
+ {Value : stock},
30
31
  ]},
31
32
  });
32
33
 
@@ -53,5 +54,6 @@ annotate CatalogService.Books with @(UI : {
53
54
  {Value : genre.name},
54
55
  {Value : price},
55
56
  {Value : currency.symbol},
57
+ {Value : stock},
56
58
  ]
57
59
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "moduleResolution": "nodenext",
3
+ "module": "NodeNext",
4
4
  "paths": {
5
5
  "#cds-models/*": ["./@cds-models/*"],
6
6
  "@sap/cds": ["./node_modules/@cap-js/cds-types"]
@@ -3,6 +3,6 @@
3
3
  "@cap-js/cds-types": "^0.18.0",
4
4
  "@types/node": "^24.0.0",
5
5
  "tsx": "^4",
6
- "typescript": "^6.0.0"
6
+ "typescript": "^7.0.0"
7
7
  }
8
8
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "10.6.0",
2
+ "version": "10.7.0",
3
3
  "help": "\n# SYNOPSIS\n\n *cds lint* [options] file.js [file.js] [dir]\n\n Runs environment checks and/or checks the specified models\n based on the ESLint framework\n\n# OPTIONS\n \n Basic configuration:\n --no-config-lookup Disable look up for eslint.config.js\n -c, --config path::String Use this configuration instead of eslint.config.js, eslint.config.mjs, or eslint.config.cjs\n --inspect-config Open the config inspector with the current configuration\n --ext [String] Specify additional file extensions to lint\n --global [String] Define global variables\n --parser String Specify the parser to be used\n --parser-options Object Specify parser options\n \n Specify Rules and Plugins:\n --plugin [String] Specify plugins\n --rule Object Specify rules\n \n Fix Problems:\n --fix Automatically fix problems\n --fix-dry-run Automatically fix problems without saving the changes to the file system\n --fix-type Array Specify the types of fixes to apply (directive, problem, suggestion, layout)\n \n Ignore Files:\n --no-ignore Disable use of ignore files and patterns\n --ignore-pattern [String] Patterns of files to ignore\n \n Use stdin:\n --stdin Lint code provided on <STDIN> - default: false\n --stdin-filename String Specify filename to process STDIN as\n \n Handle Warnings:\n --quiet Report errors only - default: false\n --max-warnings Int Number of warnings to trigger nonzero exit code - default: -1\n \n Output:\n -o, --output-file path::String Specify file to write report to\n -f, --format String Use a specific output format - default: stylish\n --color, --no-color Force enabling/disabling of color\n \n Inline configuration comments:\n --no-inline-config Prevent comments from changing config or rules\n --report-unused-disable-directives Adds reported errors for unused eslint-disable and eslint-enable directives\n --report-unused-disable-directives-severity String Chooses severity level for reporting unused eslint-disable and eslint-enable directives - either: off, warn, error, 0, 1, or 2\n --report-unused-inline-configs String Adds reported errors for unused eslint inline config comments - either: off, warn, error, 0, 1, or 2\n \n Caching:\n --cache Only check changed files - default: false\n --cache-file path::String Path to the cache file. Deprecated: use --cache-location - default: .eslintcache\n --cache-location path::String Path to the cache file or directory\n --cache-strategy String Strategy to use for detecting changed files in the cache - either: metadata or content - default: metadata\n \n Suppressing Violations:\n --suppress-all Suppress all violations - default: false\n --suppress-rule [String] Suppress specific rules\n --suppressions-location path::String Specify the location of the suppressions file\n --prune-suppressions Prune unused suppressions - default: false\n --pass-on-unpruned-suppressions Ignore unused suppressions - default: false\n \n Miscellaneous:\n --init Run config initialization wizard - default: false\n --env-info Output execution environment information - default: false\n --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched\n --exit-on-fatal-error Exit with exit code 2 in case of fatal error - default: false\n --no-warn-ignored Suppress warnings when the file list includes ignored files\n --pass-on-no-patterns Exit with exit code 0 in case no file patterns are passed\n --debug Output debugging information\n -h, --help Show help\n -v, --version Output the version number\n --print-config path::String Print the configuration for the given file\n --stats Add statistics to the lint report - default: false\n --flag [String] Enable a feature flag\n --mcp Start the ESLint MCP server\n --concurrency Int|String Number of linting threads, auto to choose automatically, off for no multithreading - default: off\n \n",
4
4
  "options": [
5
5
  "--config",
@@ -0,0 +1,27 @@
1
+ // we want to allow starting projects with having nothing but a global cds-dk installed in a watched context.
2
+ // In that case, we want to fall back to resolving internal packages from the global cds-dk's own node_modules.
3
+ // Enriching NODE_PATH can be used in CJS (require(...)), but not in ESM (import ... from and import(...)),
4
+ // which requires a custom loader instead.
5
+ // https://nodejs.org/api/esm.html#no-node_path
6
+ const { createRequire, registerHooks } = require('node:module')
7
+ const { pathToFileURL } = require('node:url')
8
+
9
+ const ourModules = new Set(['@sap/cds'])
10
+
11
+ const requireFromHere = createRequire(__filename)
12
+ const resolveFromHere = url => pathToFileURL(requireFromHere.resolve(url)).href
13
+
14
+ registerHooks({
15
+ resolve(specifier, context, nextResolve) {
16
+ try {
17
+ // use default Node resolver first.
18
+ // Note: registering more hooks is UB!
19
+ return nextResolve(specifier, context)
20
+ } catch (error) {
21
+ if (error.code === 'ERR_MODULE_NOT_FOUND' && ourModules.has(specifier)) {
22
+ return { url: resolveFromHere(specifier), shortCircuit: true }
23
+ }
24
+ throw error
25
+ }
26
+ }
27
+ })
@@ -79,7 +79,7 @@ cds.on('bootstrap', app => {
79
79
 
80
80
  for (const viteApp of viteApps) {
81
81
  const base = viteApp.root ? '/' : normalizeBase(viteApp.name)
82
- const server = await createServer({ base, root: viteApp.path, server: { middlewareMode: true, hmr: { port: hmrPort++ } }})
82
+ const server = await createServer({ base, root: viteApp.path, server: { middlewareMode: true, ws: { port: hmrPort++ } }})
83
83
  viteServers.push({ base, root: viteApp.root, middlewares: server.middlewares })
84
84
  }
85
85
 
@@ -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)",
@@ -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) {
@@ -43,9 +43,9 @@ class WebSocketServer extends EventEmitter {
43
43
  * called
44
44
  * @param {Function} [options.handleProtocols] A hook to handle protocols
45
45
  * @param {String} [options.host] The hostname where to bind the server
46
- * @param {Number} [options.maxBufferedChunks=1048576] The maximum number of
46
+ * @param {Number} [options.maxBufferedChunks=262144] The maximum number of
47
47
  * buffered data chunks
48
- * @param {Number} [options.maxFragments=131072] The maximum number of message
48
+ * @param {Number} [options.maxFragments=16384] The maximum number of message
49
49
  * fragments
50
50
  * @param {Number} [options.maxPayload=104857600] The maximum allowed message
51
51
  * size
@@ -69,8 +69,8 @@ class WebSocketServer extends EventEmitter {
69
69
  options = {
70
70
  allowSynchronousEvents: true,
71
71
  autoPong: true,
72
- maxBufferedChunks: 1024 * 1024,
73
- maxFragments: 128 * 1024,
72
+ maxBufferedChunks: 256 * 1024,
73
+ maxFragments: 16 * 1024,
74
74
  maxPayload: 100 * 1024 * 1024,
75
75
  skipUTF8Validation: false,
76
76
  perMessageDeflate: false,
@@ -646,9 +646,9 @@ module.exports = WebSocket;
646
646
  * masking key
647
647
  * @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the
648
648
  * handshake request
649
- * @param {Number} [options.maxBufferedChunks=1048576] The maximum number of
649
+ * @param {Number} [options.maxBufferedChunks=262144] The maximum number of
650
650
  * buffered data chunks
651
- * @param {Number} [options.maxFragments=131072] The maximum number of message
651
+ * @param {Number} [options.maxFragments=16384] The maximum number of message
652
652
  * fragments
653
653
  * @param {Number} [options.maxPayload=104857600] The maximum allowed message
654
654
  * size
@@ -670,8 +670,8 @@ function initAsClient(websocket, address, protocols, options) {
670
670
  autoPong: true,
671
671
  closeTimeout: CLOSE_TIMEOUT,
672
672
  protocolVersion: protocolVersions[1],
673
- maxBufferedChunks: 1024 * 1024,
674
- maxFragments: 128 * 1024,
673
+ maxBufferedChunks: 256 * 1024,
674
+ maxFragments: 16 * 1024,
675
675
  maxPayload: 100 * 1024 * 1024,
676
676
  skipUTF8Validation: false,
677
677
  perMessageDeflate: true,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ws",
3
- "version": "8.21.0",
3
+ "version": "8.21.1",
4
4
  "description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js",
5
5
  "keywords": [
6
6
  "HyBi",
@@ -66,5 +66,9 @@
66
66
  "nyc": "^15.0.0",
67
67
  "prettier": "^3.0.0",
68
68
  "utf-8-validate": "^6.0.0"
69
+ },
70
+ "allowScripts": {
71
+ "bufferutil": true,
72
+ "utf-8-validate": true
69
73
  }
70
74
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "10.0.4",
3
+ "version": "10.0.5",
4
4
  "description": "Command line client and development toolkit for the SAP Cloud Application Programming Model",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [