@sap/cds 9.4.3 → 9.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@
4
4
  - The format is based on [Keep a Changelog](https://keepachangelog.com/).
5
5
  - This project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## Version 9.4.4 - 2025-10-23
8
+
9
+ ### Fixed
10
+
11
+ - Input validation of action parameters in action calls on draft state of draft enabled entities
12
+ - Input validation on `NEW` event of draft choreography
13
+ - Ignore outbox model on Windows
14
+ - `enterprise-messaging-shared`: preserve error listener during reconnect
15
+
7
16
  ## Version 9.4.3 - 2025-10-10
8
17
 
9
18
  ### Fixed
@@ -62,7 +71,6 @@
62
71
  - Broken link `cds.auth`
63
72
  - Persist original error message in draft validation messages
64
73
  - Escaping of `\t` and `\f` in edmx during localization
65
- - Escaping of JSON escape sequences other than `\"` during localization
66
74
 
67
75
  ## Version 9.3.1 - 2025-09-03
68
76
 
@@ -40,5 +40,4 @@ function string4 (raw) {
40
40
  return raw
41
41
  .replace(/''/g,"'") .replace(/^"(.*)"$/,"$1") .replace(/^'(.*)'$/,"$1")
42
42
  .replace(/\\u[\dA-F]{4}/gi, (match) => String.fromCharCode(parseInt(match.replace(/\\u/g, ''), 16)))
43
- .replace(/\\(.)/g, (_, seq) => ({ n: '\n', r: '\r', t: '\t', f: '\f' }[seq] ?? seq))
44
43
  }
@@ -13,7 +13,7 @@ module.exports = exports = function load (files, options) {
13
13
  // REVISIT: bandaid for grow as you go scenario with task queues enabled by default
14
14
  let locations
15
15
  if (cds.watched) {
16
- const _is_outbox = p => cds.utils.path.posix.normalize(p).match(/\/cds\/srv\/outbox(\.cds)?$/)
16
+ const _is_outbox = p => cds.utils.path.posix.normalize(p).match(/((\/cds\/srv\/outbox)|(\\cds\\srv\\outbox))(\.cds)?$/)
17
17
  const _outbox_only = any?.length === 1 && _is_outbox(any[0]) && (!Array.isArray(files) || !files.some(_is_outbox))
18
18
  if (_outbox_only) {
19
19
  any = undefined
@@ -85,7 +85,7 @@ exports.edmx = edmx => {
85
85
 
86
86
  exports.json = json => {
87
87
  if (typeof json === 'object') json = JSON.stringify(json)
88
- const _json_replacer = s => s && JSON.stringify(s).slice(1,-1)
88
+ const _json_replacer = s => s?.replace(/"/g, '\\"')
89
89
  return localize(json) .using (_json_replacer)
90
90
  }
91
91
 
@@ -429,7 +429,11 @@ validate_action._initial = true
429
429
  module.exports = cds.service.impl(function () {
430
430
  this.before(['CREATE', 'UPDATE', 'NEW'], '*', validate_input)
431
431
  for (const each of this.actions) this.before(each, validate_action)
432
- for (const entity of this.entities) for (let a in entity.actions) this.before(a, entity, validate_action)
432
+ for (const entity of this.entities)
433
+ for (let a in entity.actions) {
434
+ this.before(a, entity, validate_action)
435
+ if (entity.drafts) this.before(a, entity.drafts, validate_action)
436
+ }
433
437
  })
434
438
 
435
439
  // needed for testing
@@ -1915,13 +1915,11 @@ async function beforeNew(req) {
1915
1915
 
1916
1916
  // Also support deep insertions
1917
1917
  for (const key in data) {
1918
+ if (!target.elements[key]) return data
1918
1919
  if (data[key] && target.elements[key].isAssociation) delete data[key].IsActiveEntity
1919
- if (!target.elements[key]?.isComposition) continue
1920
- // do array trick
1920
+ if (!target.elements[key].isComposition) continue
1921
1921
  if (Array.isArray(data[key])) data[key] = data[key].map(v => _cleanseData(v, target.elements[key]._target))
1922
- else if (typeof data[key] === 'object') {
1923
- data[key] = _cleanseData(data[key], target.elements[key]._target)
1924
- }
1922
+ else if (typeof data[key] === 'object') data[key] = _cleanseData(data[key], target.elements[key]._target)
1925
1923
  }
1926
1924
 
1927
1925
  return data
@@ -6,54 +6,39 @@ const _rmHandlers = client => {
6
6
  client.removeAllListeners('disconnected')
7
7
  }
8
8
 
9
- const _connectUntilConnected = (client, LOG, x) => {
10
- if (client._reconnecting) return
11
- client._reconnecting = true
12
-
13
- const _waitingTime = waitingTime(x)
14
- setTimeout(() => {
15
- connect(client, LOG, true)
16
- .then(() => {
17
- client._reconnecting = false
18
- LOG._warn && LOG.warn('Reconnected to Enterprise Messaging Client')
19
- })
20
- .catch(e => {
21
- _rmHandlers(client)
22
- LOG.error(e)
23
-
24
- LOG._warn &&
25
- LOG.warn(
26
- `Connection to Enterprise Messaging Client lost: Reconnecting in ${Math.round(_waitingTime / 1000)} s`
27
- )
28
- client._reconnecting = false
29
- _connectUntilConnected(client, LOG, x + 1)
30
- })
31
- }, _waitingTime)
32
- }
33
-
34
9
  const connect = (client, LOG, keepAlive) => {
35
10
  return new Promise((resolve, reject) => {
36
11
  _rmHandlers(client)
37
12
 
38
13
  client
39
14
  .once('connected', function () {
40
- const handleReconnection = err => {
41
- if (client._reconnecting) return
15
+ _rmHandlers(client)
42
16
 
43
- if (err && LOG._error) {
44
- err.message = 'Client error: ' + err.message
45
- LOG.error(err)
46
- }
47
- if (keepAlive) {
48
- _rmHandlers(client)
49
- _connectUntilConnected(client, LOG, 0)
50
- }
51
- }
17
+ // It's important to _always_ keep an error listener,
18
+ // otherwise it will throw and crash the server
19
+ client.on('error', err => LOG(err?.message))
52
20
 
53
- _rmHandlers(client)
54
- client.once('error', handleReconnection)
55
21
  if (keepAlive) {
56
- client.once('disconnected', handleReconnection)
22
+ client.on('disconnected', () => {
23
+ let connected = false
24
+ client.once('connected', () => {
25
+ LOG('Reconnected')
26
+ connected = true
27
+ })
28
+ let x = 1
29
+ const _untilConnected = async () => {
30
+ if (!connected) {
31
+ LOG('Reconnecting')
32
+ try {
33
+ client.connect()
34
+ } catch {
35
+ // we try again...
36
+ }
37
+ setTimeout(_untilConnected, waitingTime(x++))
38
+ }
39
+ }
40
+ _untilConnected()
41
+ })
57
42
  }
58
43
 
59
44
  resolve(client)
@@ -65,7 +50,11 @@ const connect = (client, LOG, keepAlive) => {
65
50
  reject(e)
66
51
  })
67
52
 
68
- client.connect()
53
+ try {
54
+ client.connect()
55
+ } catch (e) {
56
+ reject(e)
57
+ }
69
58
  })
70
59
  }
71
60
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds",
3
- "version": "9.4.3",
3
+ "version": "9.4.4",
4
4
  "description": "SAP Cloud Application Programming Model - CDS for Node.js",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [