@sap/cds 9.8.1 → 9.8.3
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 +12 -0
- package/eslint.config.mjs +0 -3
- package/lib/compile/etc/properties.js +1 -0
- package/lib/env/cds-env.js +1 -1
- package/lib/i18n/index.js +2 -0
- package/lib/ql/cds.ql-infer.js +2 -0
- package/lib/req/response.js +2 -0
- package/lib/srv/middlewares/auth/mocked-users.js +1 -1
- package/lib/srv/protocols/hcql.js +1 -1
- package/lib/utils/cds-utils.js +1 -1
- package/libx/_runtime/messaging/redis-messaging.js +1 -1
- package/libx/odata/middleware/batch.js +21 -20
- package/libx/odata/parse/afterburner.js +0 -1
- package/libx/queue/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@
|
|
|
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.8.3 - 2026-03-12
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- OData batch parallel processing: Drain remaining queue when aborting
|
|
12
|
+
|
|
13
|
+
## Version 9.8.2 - 2026-03-10
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Compatibility with `@eslint/js^10`
|
|
18
|
+
|
|
7
19
|
## Version 9.8.1 - 2026-03-09
|
|
8
20
|
|
|
9
21
|
### Fixed
|
package/eslint.config.mjs
CHANGED
|
@@ -9,6 +9,7 @@ exports.read = function read (res, ext = '.properties', options) {
|
|
|
9
9
|
const src = fs.readFileSync(path.resolve(res),'utf-8')
|
|
10
10
|
return Object.defineProperty (exports.parse(src,options), '_source', {value:res})
|
|
11
11
|
} catch (e) {
|
|
12
|
+
// eslint-disable-next-line preserve-caught-error
|
|
12
13
|
if (e.code !== 'ENOENT') throw new Error (`Corrupt ${ext} file: ${res+ext}`)
|
|
13
14
|
}
|
|
14
15
|
}
|
package/lib/env/cds-env.js
CHANGED
|
@@ -295,7 +295,7 @@ class Config {
|
|
|
295
295
|
const any = this._add_vcap_services_to (vcaps)
|
|
296
296
|
if (any) this._sources.push ('process.env.VCAP_SERVICES')
|
|
297
297
|
} catch(e) {
|
|
298
|
-
throw new Error ('[cds.env] - failed to parse VCAP_SERVICES:\n '+ e.message)
|
|
298
|
+
throw new Error ('[cds.env] - failed to parse VCAP_SERVICES:\n '+ e.message, {cause: e})
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
301
|
|
package/lib/i18n/index.js
CHANGED
package/lib/ql/cds.ql-infer.js
CHANGED
package/lib/req/response.js
CHANGED
|
@@ -39,7 +39,7 @@ const _configured = (u,x) => {
|
|
|
39
39
|
u.attr = { ...u.attr, ...x }
|
|
40
40
|
}
|
|
41
41
|
if (u.jwt) {
|
|
42
|
-
if ((
|
|
42
|
+
if (( _deprecated (u.jwt.zid, 'jwt.zid','tenant'))) {
|
|
43
43
|
u.tenant = u.jwt.zid
|
|
44
44
|
}
|
|
45
45
|
if ((x = _deprecated (u.jwt.attributes, 'jwt.attributes','attr'))) {
|
|
@@ -47,7 +47,7 @@ class HCQLAdapter extends require('./http') {
|
|
|
47
47
|
|
|
48
48
|
// Error formatting
|
|
49
49
|
router.use ((err, req, res, next) => {
|
|
50
|
-
err.$response = e => ({ errors: [ { ...e, message: e.message } ] })
|
|
50
|
+
if (typeof err === 'object') err.$response = e => ({ errors: [ { ...e, message: e.message } ] })
|
|
51
51
|
next(err)
|
|
52
52
|
})
|
|
53
53
|
|
package/lib/utils/cds-utils.js
CHANGED
|
@@ -189,7 +189,7 @@ exports.read = async function read (file, _encoding) {
|
|
|
189
189
|
if (_encoding === 'json' || !_encoding && f.endsWith('.json')) try {
|
|
190
190
|
return JSON.parse(src)
|
|
191
191
|
} catch(e) {
|
|
192
|
-
throw new Error (`Failed to parse JSON in ${f}: ${e.message}
|
|
192
|
+
throw new Error (`Failed to parse JSON in ${f}: ${e.message}`, { cause: e })
|
|
193
193
|
}
|
|
194
194
|
else return process.platform === 'win32' ? src?.replace(/\r\n/g, '\n') : src
|
|
195
195
|
}
|
|
@@ -36,7 +36,7 @@ class RedisMessaging extends cds.MessagingService {
|
|
|
36
36
|
try {
|
|
37
37
|
await this.client.connect()
|
|
38
38
|
} catch (e) {
|
|
39
|
-
throw new Error('Connection to Redis could not be established: ' + e)
|
|
39
|
+
throw new Error('Connection to Redis could not be established: ' + e, { cause: e })
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
this._ready = true
|
|
@@ -487,42 +487,43 @@ const _processBatch = async (srv, router, req, res, next, body, ct, boundary) =>
|
|
|
487
487
|
|
|
488
488
|
// ensure all subrequests run in this tx
|
|
489
489
|
// (if first subrequest fails without really opening the tx, the rest are executed in a "dangling tx")
|
|
490
|
-
return Promise.allSettled(subrequests)
|
|
491
|
-
.
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
// throw first error and call srv.on('error') for the others
|
|
501
|
-
const first = failed.shift()
|
|
502
|
-
if (srv.handlers._error?.length)
|
|
503
|
-
for (const other of failed)
|
|
504
|
-
for (const each of srv.handlers._error) each.handler.call(srv, other.reason, cds.context)
|
|
505
|
-
throw first.reason
|
|
506
|
-
})
|
|
490
|
+
return Promise.allSettled(subrequests).then(ress => {
|
|
491
|
+
const failed = ress.filter(({ status }) => status === 'rejected')
|
|
492
|
+
if (!failed.length) return
|
|
493
|
+
// throw first error and call srv.on('error') for the others
|
|
494
|
+
const first = failed.shift()
|
|
495
|
+
if (srv.handlers._error?.length)
|
|
496
|
+
for (const other of failed)
|
|
497
|
+
for (const each of srv.handlers._error) each.handler.call(srv, other.reason, cds.context)
|
|
498
|
+
throw first.reason
|
|
499
|
+
})
|
|
507
500
|
})
|
|
508
501
|
.catch(err => {
|
|
509
502
|
responses._has_failure = true
|
|
510
503
|
|
|
511
504
|
// abort batch on first failure with odata.continue-on-error: false
|
|
512
|
-
if (!continue_on_error)
|
|
505
|
+
if (!continue_on_error) {
|
|
506
|
+
_continue = false
|
|
507
|
+
while (queue.length) queue.shift()()
|
|
508
|
+
}
|
|
513
509
|
|
|
514
510
|
if (!responses.some(r => r.status === 'fail')) {
|
|
515
511
|
// here, the commit was rejected even though all requests were successful (e.g., by custom handler or db consistency check)
|
|
516
512
|
_replaceResponsesWithCommitErrors(err, responses, ids)
|
|
517
513
|
}
|
|
518
514
|
})
|
|
519
|
-
.finally(() => {
|
|
515
|
+
.finally(async () => {
|
|
520
516
|
// trigger next in queue
|
|
521
517
|
if (queue.length) queue.shift()()
|
|
522
518
|
|
|
523
519
|
// late error serialization
|
|
524
520
|
if (responses._has_failure) _serializeErrors(responses)
|
|
525
521
|
|
|
522
|
+
// wait for all previous atomicity groups (ignoring errors via allSettled) for odata v2
|
|
523
|
+
const prevs = []
|
|
524
|
+
for (let i = 0; i < agIndex; i++) prevs.push(promises[i])
|
|
525
|
+
await Promise.allSettled(prevs)
|
|
526
|
+
|
|
526
527
|
if (isJson) _writeResponseJson(responses, res)
|
|
527
528
|
else _writeResponseMultipart(responses, res, boundary)
|
|
528
529
|
})
|
|
@@ -540,7 +541,7 @@ const _processBatch = async (srv, router, req, res, next, body, ct, boundary) =>
|
|
|
540
541
|
// trigger first max_parallel in queue
|
|
541
542
|
for (let i = 0; i < max_parallel; i++) if (queue.length) queue.shift()()
|
|
542
543
|
|
|
543
|
-
await Promise.
|
|
544
|
+
await Promise.allSettled(promises)
|
|
544
545
|
|
|
545
546
|
res.write(isJson ? ']}' : `--${boundary}--${CRLF}`)
|
|
546
547
|
res.end()
|
|
@@ -463,7 +463,6 @@ function _processSegments(from, model, namespace, cqn, protocol) {
|
|
|
463
463
|
|
|
464
464
|
// > navigation
|
|
465
465
|
one = !!(current.is2one || ref[i].where)
|
|
466
|
-
incompleteKeys = one || i === ref.length - 1 ? false : true
|
|
467
466
|
current = model.definitions[current.target]
|
|
468
467
|
target = current
|
|
469
468
|
|
package/libx/queue/index.js
CHANGED
|
@@ -259,7 +259,7 @@ const _processTasks = (target, tenant, _opts = {}) => {
|
|
|
259
259
|
LOG.error(`${service.name}: Programming error detected:`, e)
|
|
260
260
|
task.updateData = { attempts: opts.maxAttempts }
|
|
261
261
|
toBeUpdated.push(task)
|
|
262
|
-
throw new Error(`${service.name}: Programming error detected
|
|
262
|
+
throw new Error(`${service.name}: Programming error detected.`, { cause: e })
|
|
263
263
|
}
|
|
264
264
|
if (e.unrecoverable) {
|
|
265
265
|
LOG.error(`${service.name}: Unrecoverable error:`, e)
|