@sap/cds-dk 10.1.1 → 10.1.2
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/lib/build/plugins/internal.js +0 -26
- package/lib/build/plugins/nodejs/index.js +8 -3
- package/lib/cds.js +8 -2
- package/lib/init/merge.js +21 -17
- package/lib/init/template/cloud-logging/index.js +3 -3
- package/lib/init/template/destination/index.js +3 -3
- package/lib/init/template/enterprise-messaging/index.js +4 -1
- package/lib/init/template/event-mesh/index.js +3 -0
- package/lib/init/template/multitenancy/index.js +2 -2
- package/lib/init/template/workzone-standard/index.js +1 -1
- package/node_modules/@cap-js/db-service/CHANGELOG.md +13 -0
- package/node_modules/@cap-js/db-service/lib/cqn4sql.js +9 -5
- package/node_modules/@cap-js/db-service/package.json +1 -1
- package/node_modules/@cap-js/sqlite/CHANGELOG.md +9 -0
- package/node_modules/@cap-js/sqlite/package.json +2 -2
- package/node_modules/@sap/cds/CHANGELOG.md +19 -0
- package/node_modules/@sap/cds/common.cds +3 -3
- package/node_modules/@sap/cds/lib/compile/minify.js +3 -1
- package/node_modules/@sap/cds/lib/env/cds-env.js +1 -1
- package/node_modules/@sap/cds/lib/env/cds-requires.js +1 -0
- package/node_modules/@sap/cds/libx/_runtime/common/generic/crud.js +7 -1
- package/node_modules/@sap/cds/libx/_runtime/common/utils/cqn.js +1 -1
- package/node_modules/@sap/cds/libx/_runtime/fiori/lean-draft.js +1 -1
- package/node_modules/@sap/cds/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +1 -1
- package/node_modules/@sap/cds/libx/_runtime/messaging/kafka.js +1 -1
- package/node_modules/@sap/cds/libx/http/location.js +10 -11
- package/node_modules/@sap/cds/libx/odata/middleware/read.js +1 -1
- package/node_modules/@sap/cds/libx/queue/SchedulingService.js +4 -0
- package/node_modules/@sap/cds/libx/queue/index.js +44 -10
- package/node_modules/@sap/cds/libx/queue/processing.js +47 -16
- package/node_modules/@sap/cds/libx/queue/utils.js +3 -0
- package/node_modules/@sap/cds/package.json +1 -1
- package/node_modules/@sap/cds/tasks/enterprise-messaging-deploy.js +13 -5
- package/node_modules/proxy-addr/HISTORY.md +60 -54
- package/node_modules/proxy-addr/README.md +1 -1
- package/node_modules/proxy-addr/index.js +32 -0
- package/node_modules/proxy-addr/package.json +8 -4
- package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/browser/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/package.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,18 @@ 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.1.2 - 2026-09-17
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- `cds` no longer crashes with an `ENOENT` (`uv_cwd`) stack trace when the shell's current directory has been deleted, e.g. during tab-completion.
|
|
14
|
+
- `cds build --for java` no longer writes Node.js runtime messages.
|
|
15
|
+
- `cds build` preserves line breaks in merged `messages.properties` bundles.
|
|
16
|
+
- `cds add xsuaa` no longer generates an invalid `mta.yaml` when added after `workzone`.
|
|
17
|
+
- `cds add cloud-logging` now binds existing approuter and multitenancy modules when `mta` is added later.
|
|
18
|
+
- `cds add enterprise-messaging` and `cds add event-mesh` now contribute to an MTA descriptor added later.
|
|
19
|
+
- `cds add destination` now binds existing approuter and multitenancy modules when `mta` is added later.
|
|
20
|
+
|
|
9
21
|
## Version 10.1.1 - 2026-09-09
|
|
10
22
|
|
|
11
23
|
### Added
|
|
@@ -132,36 +132,10 @@ module.exports = class InternalBuildPlugin extends require('./plugin') {
|
|
|
132
132
|
// bundleDest might be null
|
|
133
133
|
if (bundleDest && Object.keys(bundles).length > 0) {
|
|
134
134
|
await this.write(bundles).to(file)
|
|
135
|
-
// also collect and write messages from plugins
|
|
136
|
-
await this.collectMessageBundles(bundleDest, model)
|
|
137
135
|
return { file, bundles }
|
|
138
136
|
}
|
|
139
137
|
return null
|
|
140
138
|
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Collect and write message bundles from CAP plugins into messages.properties files.
|
|
144
|
-
* @param {string} bundleDest
|
|
145
|
-
* @param {Object} model - passed for signature consistency with subclass overrides; not used here
|
|
146
|
-
* as Java does not require neighborhood resolution of plugin i18n files.
|
|
147
|
-
*/
|
|
148
|
-
// eslint-disable-next-line no-unused-vars
|
|
149
|
-
async collectMessageBundles(bundleDest, model) {
|
|
150
|
-
const messageBundle = cds.i18n.bundle4('messages')
|
|
151
|
-
const messagesData = messageBundle.translations4?.(cds.env.i18n.languages) ?? {}
|
|
152
|
-
|
|
153
|
-
// Write messages.properties files for each locale
|
|
154
|
-
for (const [locale, messages] of Object.entries(messagesData)) {
|
|
155
|
-
if (Object.keys(messages).length > 0) {
|
|
156
|
-
const fileName = locale ? `messages_${locale}.properties` : 'messages.properties'
|
|
157
|
-
const filePath = path.join(bundleDest, fileName)
|
|
158
|
-
const content = Object.entries(messages)
|
|
159
|
-
.map(([key, value]) => `${key}=${value}`)
|
|
160
|
-
.join('\n') + '\n'
|
|
161
|
-
await this.write(content).to(filePath)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
139
|
}
|
|
166
140
|
|
|
167
141
|
// Emits the 'compile.for.runtime' event, as the produced CSN is for runtime usage.
|
|
@@ -10,6 +10,7 @@ const DEBUG = cds.debug('cli|build')
|
|
|
10
10
|
const NODE_ENGINE_VERSION = '^24'
|
|
11
11
|
const { exec } = require('node:child_process')
|
|
12
12
|
const pexec = require('node:util').promisify(exec)
|
|
13
|
+
const escapeLineBreaks = value => value.replace(/\r/g, '\\u000D').replace(/\n/g, '\\u000A')
|
|
13
14
|
|
|
14
15
|
module.exports = class NodejsBuildPlugin extends require('../edmx') {
|
|
15
16
|
|
|
@@ -353,15 +354,19 @@ module.exports = class NodejsBuildPlugin extends require('../edmx') {
|
|
|
353
354
|
return Array.isArray(parsed) ? parsed : Object.values(parsed)
|
|
354
355
|
}
|
|
355
356
|
|
|
356
|
-
|
|
357
|
-
|
|
357
|
+
async collectLanguageBundles(model, bundleDest) {
|
|
358
|
+
const result = await super.collectLanguageBundles(model, bundleDest)
|
|
359
|
+
if (result) await this.collectMessageBundles(bundleDest, model)
|
|
360
|
+
return result
|
|
361
|
+
}
|
|
362
|
+
|
|
358
363
|
async collectMessageBundles(bundleDest, model) {
|
|
359
364
|
const messageBundle = cds.i18n.bundle4('messages', { model })
|
|
360
365
|
const messagesData = messageBundle.translations4?.(cds.env.i18n.languages) ?? {}
|
|
361
366
|
for (const [locale, messages] of Object.entries(messagesData)) {
|
|
362
367
|
if (Object.keys(messages).length > 0) {
|
|
363
368
|
const fileName = locale ? `messages_${locale}.properties` : 'messages.properties'
|
|
364
|
-
const content = Object.entries(messages).map(([k, v]) => `${k}=${v}`).join('\n') + '\n'
|
|
369
|
+
const content = Object.entries(messages).map(([k, v]) => `${k}=${escapeLineBreaks(v)}`).join('\n') + '\n'
|
|
365
370
|
await this.write(content).to(path.join(bundleDest, fileName))
|
|
366
371
|
}
|
|
367
372
|
}
|
package/lib/cds.js
CHANGED
|
@@ -11,10 +11,16 @@ module.exports = Object.defineProperties (cds, {
|
|
|
11
11
|
|
|
12
12
|
function _local (id, ...morePaths) {
|
|
13
13
|
return require(require.resolve (id, {paths:[
|
|
14
|
-
|
|
14
|
+
_cwd(), // project-local module is preferred
|
|
15
15
|
__dirname, // otherwise from our own dependencies
|
|
16
16
|
...morePaths
|
|
17
|
-
]}))
|
|
17
|
+
].filter(Boolean)})) // drop empty paths (e.g. _cwd()/_npmRoot() fallbacks); require.resolve expects real paths
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function _cwd() {
|
|
21
|
+
// process.cwd() throws ENOENT (uv_cwd) when the cwd was deleted
|
|
22
|
+
try { return process.cwd() }
|
|
23
|
+
catch (e) { if (e.code === 'ENOENT') return '' }
|
|
18
24
|
}
|
|
19
25
|
|
|
20
26
|
function _npmRoot() {
|
package/lib/init/merge.js
CHANGED
|
@@ -165,8 +165,28 @@ async function _mergeYAML(into, from, project, semantics = {}) {
|
|
|
165
165
|
t.value = s.value
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
+
const deleteEntries = entries => {
|
|
169
|
+
for (const { item, key, relationships: refs } of entries) {
|
|
170
|
+
const seq = locate(target, item.in)
|
|
171
|
+
const node = locate(target, item)
|
|
172
|
+
if (!YAML.isSeq(seq) || !node) continue
|
|
173
|
+
const removed = node.toJSON()
|
|
174
|
+
key ? node.deleteIn(path(key)) : seq.delete(seq.items.indexOf(node))
|
|
175
|
+
for (const { removeProperty, allWithin } of refs ?? []) {
|
|
176
|
+
const [parentPath, childPath, prop] = allWithin
|
|
177
|
+
for (const parent of locate(target, parentPath)?.items ?? []) {
|
|
178
|
+
const list = parent.getIn(path(childPath))
|
|
179
|
+
const i = list?.items?.findIndex(n => n.get?.(prop) === removed[removeProperty]) ?? -1
|
|
180
|
+
if (i > -1) list.delete(i)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
168
185
|
mergeNode(target.contents, source.contents)
|
|
169
186
|
|
|
187
|
+
// Deletions: remove the matched node (or a sub-key), then cascade-remove references to it.
|
|
188
|
+
deleteEntries(deletions.filter(({ key }) => !key))
|
|
189
|
+
|
|
170
190
|
// Placement: add matched (and relationship-referenced) template entries not already present.
|
|
171
191
|
const isEntry = e => e && typeof e === 'object' && !Array.isArray(e) && e.in !== undefined
|
|
172
192
|
const insertNode = ins => Array.isArray(ins) ? (ins.length === 1 ? ins[0] : undefined) : ins
|
|
@@ -208,22 +228,7 @@ async function _mergeYAML(into, from, project, semantics = {}) {
|
|
|
208
228
|
}
|
|
209
229
|
}
|
|
210
230
|
|
|
211
|
-
|
|
212
|
-
for (const { item, key, relationships: refs } of deletions) {
|
|
213
|
-
const seq = locate(target, item.in)
|
|
214
|
-
const node = locate(target, item)
|
|
215
|
-
if (!YAML.isSeq(seq) || !node) continue
|
|
216
|
-
const removed = node.toJSON()
|
|
217
|
-
key ? node.deleteIn(path(key)) : seq.delete(seq.items.indexOf(node))
|
|
218
|
-
for (const { removeProperty, allWithin } of refs ?? []) {
|
|
219
|
-
const [parentPath, childPath, prop] = allWithin
|
|
220
|
-
for (const parent of locate(target, parentPath)?.items ?? []) {
|
|
221
|
-
const list = parent.getIn(path(childPath))
|
|
222
|
-
const i = list?.items?.findIndex(n => n.get?.(prop) === removed[removeProperty]) ?? -1
|
|
223
|
-
if (i > -1) list.delete(i)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
231
|
+
deleteEntries(deletions.filter(({ key }) => key))
|
|
227
232
|
|
|
228
233
|
typeof into === 'string' && await writeYAML(into, target)
|
|
229
234
|
return target
|
|
@@ -349,4 +354,3 @@ module.exports.merge = (...src) => {
|
|
|
349
354
|
* @property {Deletion[]} [deletions] - matched nodes (or sub-keys) to delete
|
|
350
355
|
* @property {Relationship[]} [relationships] - relationships to wire into target sequences
|
|
351
356
|
*/
|
|
352
|
-
|
|
@@ -40,7 +40,7 @@ module.exports = class extends require('../../plugin') {
|
|
|
40
40
|
|
|
41
41
|
async combine(facet) {
|
|
42
42
|
const project = readProject(facet)
|
|
43
|
-
const { hasMta, addMta, addKyma, addApprouter, isJava, addMultitenancy, srv } = project
|
|
43
|
+
const { hasMta, addMta, addKyma, addApprouter, hasApprouter, isJava, addMultitenancy, hasMultitenancy, srv } = project
|
|
44
44
|
|
|
45
45
|
if (addMta || (addMultitenancy || addApprouter) && hasMta) {
|
|
46
46
|
const cloudLogging = {
|
|
@@ -58,14 +58,14 @@ module.exports = class extends require('../../plugin') {
|
|
|
58
58
|
into: [srvModule, 'requires', 'name']
|
|
59
59
|
})
|
|
60
60
|
}
|
|
61
|
-
if (
|
|
61
|
+
if (hasMultitenancy) {
|
|
62
62
|
const mtxSidecar = mtxSidecar4(isJava ? 'mtx/sidecar' : 'gen/mtx/sidecar')
|
|
63
63
|
relationships.push({
|
|
64
64
|
insert: [cloudLogging, 'name'],
|
|
65
65
|
into: [mtxSidecar, 'requires', 'name']
|
|
66
66
|
})
|
|
67
67
|
}
|
|
68
|
-
if (
|
|
68
|
+
if (hasApprouter) {
|
|
69
69
|
relationships.push({
|
|
70
70
|
insert: [cloudLogging, 'name'],
|
|
71
71
|
into: [approuter, 'requires', 'name']
|
|
@@ -25,7 +25,7 @@ module.exports = class DestinationTemplate extends require('../../plugin') {
|
|
|
25
25
|
|
|
26
26
|
async combine(facet) {
|
|
27
27
|
const project = readProject(facet)
|
|
28
|
-
const { addApprouter, addMultitenancy, addKyma, addMta, hasMta, srv, isJava } = project
|
|
28
|
+
const { addApprouter, hasApprouter, addMultitenancy, hasMultitenancy, addKyma, addMta, hasMta, srv, isJava } = project
|
|
29
29
|
|
|
30
30
|
if (addMta || (addApprouter || addMultitenancy) && hasMta) {
|
|
31
31
|
const srvModule = srv4(srv.path)
|
|
@@ -34,13 +34,13 @@ module.exports = class DestinationTemplate extends require('../../plugin') {
|
|
|
34
34
|
insert: [destination, 'name'],
|
|
35
35
|
into: [srvModule, 'requires', 'name']
|
|
36
36
|
}]
|
|
37
|
-
if (
|
|
37
|
+
if (hasApprouter) {
|
|
38
38
|
relationships.push({
|
|
39
39
|
insert: [destination, 'name'],
|
|
40
40
|
into: [approuter, 'requires', 'name']
|
|
41
41
|
})
|
|
42
42
|
}
|
|
43
|
-
if (
|
|
43
|
+
if (hasMultitenancy) {
|
|
44
44
|
relationships.push({
|
|
45
45
|
insert: [destination, 'name'],
|
|
46
46
|
into: [mtxSidecar4(isJava ? 'mtx/sidecar' : 'gen/mtx/sidecar'), 'requires', 'name']
|
|
@@ -6,6 +6,9 @@ const { srv4, enterpriseMessaging, xsuaa } = require('../../registries/mta')
|
|
|
6
6
|
const KymaTemplate = require('../kyma')
|
|
7
7
|
|
|
8
8
|
module.exports = class EnterpriseMessagingTemplate extends require('../../plugin') {
|
|
9
|
+
static get kind() { return 'enterprise-messaging-http' }
|
|
10
|
+
static get vcap() { return 'enterprise-messaging' }
|
|
11
|
+
|
|
9
12
|
constructor(...args) {
|
|
10
13
|
super(...args)
|
|
11
14
|
this._name = 'enterprise-messaging'
|
|
@@ -37,7 +40,7 @@ module.exports = class EnterpriseMessagingTemplate extends require('../../plugin
|
|
|
37
40
|
|
|
38
41
|
static hasInProduction(env) {
|
|
39
42
|
const { kind = 'dummy', vcap } = env.requires?.messaging ?? {}
|
|
40
|
-
return kind === this.
|
|
43
|
+
return kind === this.kind && vcap?.label === this.vcap
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
async run() {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
const { eventMesh } = require('../../registries/mta')
|
|
2
2
|
|
|
3
3
|
module.exports = class EventMeshTemplate extends require('../enterprise-messaging') {
|
|
4
|
+
static get kind() { return 'enterprise-messaging-http' }
|
|
5
|
+
static get vcap() { return 'event-mesh-message-client' }
|
|
6
|
+
|
|
4
7
|
constructor(...args) {
|
|
5
8
|
super(...args)
|
|
6
9
|
this._name = 'event-mesh'
|
|
@@ -44,11 +44,11 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
|
|
|
44
44
|
const project = readProject(facet)
|
|
45
45
|
const {
|
|
46
46
|
isNodejs, isJava, addMta, hasMta,
|
|
47
|
-
addXsuaa, hasXsuaa, addIas, hasIas, hasHana, hasHanaServerless, addKyma, hasApprouter,
|
|
47
|
+
addXsuaa, hasXsuaa, addIas, hasIas, addMultitenancy, hasHana, hasHanaServerless, addKyma, hasApprouter,
|
|
48
48
|
srv, appName
|
|
49
49
|
} = project
|
|
50
50
|
|
|
51
|
-
if (addMta || (addIas || addXsuaa) && hasMta) {
|
|
51
|
+
if (addMta || (addMultitenancy || addIas || addXsuaa) && hasMta) {
|
|
52
52
|
const srvModule = srv4(srv.path)
|
|
53
53
|
const mtxSidecar = mtxSidecar4(isJava ? 'mtx/sidecar' : 'gen/mtx/sidecar')
|
|
54
54
|
const modules = [srvModule, mtxSidecar]
|
|
@@ -4,6 +4,19 @@
|
|
|
4
4
|
- The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
5
5
|
- This project adheres to [Semantic Versioning](http://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [3.1.1](https://github.com/cap-js/cds-dbs/compare/db-service-v3.1.0...db-service-v3.1.1) (2026-09-15)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
* **cqn4sql:** correctly expand `not exists <assoc>` alongside a null-check branch ([#1736](https://github.com/cap-js/cds-dbs/issues/1736)) ([bfb0a01](https://github.com/cap-js/cds-dbs/commit/bfb0a010cce2cea089f4fabf92a3ecea43486a09))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Performance Improvements
|
|
16
|
+
|
|
17
|
+
* do not use ranked search for static values ([#1727](https://github.com/cap-js/cds-dbs/issues/1727)) ([33fb36b](https://github.com/cap-js/cds-dbs/commit/33fb36bbf0f86291e5fc0b9e2ff5b7244b49d19e))
|
|
18
|
+
* if explicit order by is specified, do not add ranked search as a secondary clause ([33fb36b](https://github.com/cap-js/cds-dbs/commit/33fb36bbf0f86291e5fc0b9e2ff5b7244b49d19e))
|
|
19
|
+
|
|
7
20
|
## [3.1.0](https://github.com/cap-js/cds-dbs/compare/db-service-v3.0.1...db-service-v3.1.0) (2026-09-07)
|
|
8
21
|
|
|
9
22
|
|
|
@@ -338,9 +338,11 @@ function _cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
338
338
|
// it would sort by a constant boolean.
|
|
339
339
|
const ranksSearch =
|
|
340
340
|
cds.db?.kind === 'hana' && cds.env.hana?.fuzzy !== false && cds.env.hana?.fuzzy?.ranked_search !== false
|
|
341
|
-
// count queries do not need ranked search
|
|
342
|
-
const isCountQuery = columns?.length === 1 && columns[0].func === 'count'
|
|
343
|
-
|
|
341
|
+
// count queries or static values do not need ranked search
|
|
342
|
+
const isCountQuery = columns?.length === 1 && typeof columns[0] === 'object' && (columns[0].func === 'count' || 'val' in columns[0])
|
|
343
|
+
// a user-provided ORDER BY takes precedence over the search rank — don't inject the ranking then
|
|
344
|
+
const hasExplicitOrderBy = (orderBy || []).some(o => !o.implicit)
|
|
345
|
+
const searchRank = ranksSearch && !isCountQuery && !hasExplicitOrderBy && inferred.$searchRank && buildSearchRankOrderBy(inferred.$searchRank)
|
|
344
346
|
if (searchRank) {
|
|
345
347
|
// precedence: user ordering, then rank, then the runtime's implicit key ordering
|
|
346
348
|
const implicitAt = (orderBy || []).findIndex(o => o.implicit)
|
|
@@ -1776,8 +1778,10 @@ function _cqn4sql(originalQuery, model, useTechnicalAlias = true) {
|
|
|
1776
1778
|
}
|
|
1777
1779
|
|
|
1778
1780
|
const whereExists = { SELECT: whereExistsSubqueries(whereExistsSubSelects) }
|
|
1779
|
-
|
|
1780
|
-
//
|
|
1781
|
+
// append, don't index by `i`: a preceding branch (e.g. flattening `<assoc> is null`)
|
|
1782
|
+
// may have changed the transformed stream's length, so `[i + 1]` would leave holes
|
|
1783
|
+
transformedTokenStream.push(whereExists)
|
|
1784
|
+
// skip the association ref which we just turned into the subquery
|
|
1781
1785
|
i += 1
|
|
1782
1786
|
} else if (token !== null && typeof token === 'object' && '#' in token) {
|
|
1783
1787
|
// Enum token: resolve to its value
|
|
@@ -4,6 +4,15 @@
|
|
|
4
4
|
- The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
5
5
|
- This project adheres to [Semantic Versioning](http://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [3.1.1](https://github.com/cap-js/cds-dbs/compare/sqlite-v3.1.0...sqlite-v3.1.1) (2026-09-15)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Dependencies
|
|
11
|
+
|
|
12
|
+
* The following workspace dependencies were updated
|
|
13
|
+
* dependencies
|
|
14
|
+
* @cap-js/db-service bumped from ^3.1.0 to ^3.1.1
|
|
15
|
+
|
|
7
16
|
## [3.1.0](https://github.com/cap-js/cds-dbs/compare/sqlite-v3.0.2...sqlite-v3.1.0) (2026-09-07)
|
|
8
17
|
|
|
9
18
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js/sqlite",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "CDS database service for SQLite",
|
|
5
5
|
"homepage": "https://github.com/cap-js/cds-dbs/tree/main/sqlite#cds-database-service-for-sqlite",
|
|
6
6
|
"repository": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"test": "cds-test"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@cap-js/db-service": "^3.1.
|
|
29
|
+
"@cap-js/db-service": "^3.1.1"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"@sap/cds": "^10",
|
|
@@ -4,6 +4,25 @@
|
|
|
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 10.1.1 - 2026-09-17
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Messaging consumers (Event Mesh AMQP, Kafka, Redis) no longer crash with `TypeError: Cannot use 'in' operator to search for 'data' in null` when a message with a `null` payload is received
|
|
12
|
+
- `cds.env` now correctly replaces an array default with an object project override instead of assigning object properties onto the array
|
|
13
|
+
- SQLite now uses its default pool config (single, non-evicting connection) even when `@cap-js/sqlite` is installed globally rather than as a local dependency
|
|
14
|
+
- Stripping of `IsActiveEntity` in programmatic deep updates to draft instances
|
|
15
|
+
- Server crash when string keys contain control characters in OData `Location` response header
|
|
16
|
+
- Binary keys in OData `Location` response header are now formatted as binary literals (`binary'...'`)
|
|
17
|
+
- Skip periodic message processing when no provider tenant is configured
|
|
18
|
+
- The parent-readable check during CREATE/navigation no longer swallows system errors (e.g. database failures); such errors are now propagated instead of being masked as a `404`
|
|
19
|
+
- Support X.509 in `enterprise-messaging`'s deploy task
|
|
20
|
+
- Incompatibility of global config `cds.sql.lock_acquire_timeout` and event-queue implementation
|
|
21
|
+
- Outbox messages written before `appid` was introduced are now processed after an upgrade instead of being skipped
|
|
22
|
+
- A queued outbox message whose target service cannot be resolved is now dead-lettered instead of crashing the application
|
|
23
|
+
- Recurring scheduled tasks (`.every()`) are no longer resurrected when unscheduled while running, and re-submitting one while it runs no longer discards the updated schedule and payload
|
|
24
|
+
- Convenience handling of `/$count` requests for custom handlers simply returning arrays
|
|
25
|
+
|
|
7
26
|
## Version 10.1.0 - 2026-09-02
|
|
8
27
|
|
|
9
28
|
### Added
|
|
@@ -166,9 +166,9 @@ annotate managed with {
|
|
|
166
166
|
createdBy @Core.Immutable;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
annotate sap.common.
|
|
170
|
-
annotate sap.common.
|
|
171
|
-
annotate sap.common.Languages with
|
|
169
|
+
annotate sap.common.Currencies :code with @Common.Text:name;
|
|
170
|
+
annotate sap.common.Countries :code with @Common.Text:name;
|
|
171
|
+
annotate sap.common.Languages :code with @Common.Text:name;
|
|
172
172
|
|
|
173
173
|
|
|
174
174
|
//---------------------------------------------------------------------------
|
|
@@ -93,7 +93,8 @@ class Minifier {
|
|
|
93
93
|
let r = q.from.ref[0]; if (r.id) r = r.id; if (r in this.kept) return
|
|
94
94
|
let e = this.defs[r]; if (!e) return
|
|
95
95
|
let all = e.elements, kept = {}
|
|
96
|
-
if (
|
|
96
|
+
if (e['@cds.minify'] === 'unused-elements') return this.keep (r,e) // avoids propagation
|
|
97
|
+
if (q.columns) _keep (q.columns); else return this.keep (r,e)
|
|
97
98
|
if (q.where) _keep (q.where)
|
|
98
99
|
e.elements = kept
|
|
99
100
|
return this.keep (r,e)
|
|
@@ -101,6 +102,7 @@ class Minifier {
|
|
|
101
102
|
if (x.func) { _keep (x.args); continue }
|
|
102
103
|
if (x.xpr) { _keep (x.xpr); continue }
|
|
103
104
|
if (x.ref) { let [r] = x.ref; if (r in all) kept[r] = all[r] }
|
|
105
|
+
if (x === '*') return kept = all
|
|
104
106
|
}}
|
|
105
107
|
}
|
|
106
108
|
|
|
@@ -515,7 +515,7 @@ function _merge (dst, src, _profiles) {
|
|
|
515
515
|
const v = pd.value
|
|
516
516
|
if (typeof v === 'object' && !Array.isArray(v) && v != null) {
|
|
517
517
|
if (!dst[p]) dst[p] = {}
|
|
518
|
-
if (typeof dst[p] !== 'object') dst[p] = v
|
|
518
|
+
if (typeof dst[p] !== 'object' || Array.isArray(dst[p])) dst[p] = v
|
|
519
519
|
else _merge (dst[p], v, _profiles)
|
|
520
520
|
continue
|
|
521
521
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
|
+
const LOG = cds.log('app')
|
|
2
3
|
const { SELECT } = cds.ql
|
|
3
4
|
|
|
4
5
|
module.exports = exports = cds.service.impl(function () {
|
|
@@ -18,7 +19,7 @@ module.exports = exports = cds.service.impl(function () {
|
|
|
18
19
|
? SELECT.one`1 as $parent`.from({ ref: ref.slice(0, -1) })
|
|
19
20
|
: null
|
|
20
21
|
const parent_readable_check = parent_exists_query
|
|
21
|
-
? async () => (parent_skip_auth ? cds.db : this).run(parent_exists_query).catch(
|
|
22
|
+
? async () => (parent_skip_auth ? cds.db : this).run(parent_exists_query).catch(parent_unreadable_error)
|
|
22
23
|
: null
|
|
23
24
|
|
|
24
25
|
if (req.event === 'CREATE' && parent_readable_check != null) (await parent_readable_check()) || req.reject(404)
|
|
@@ -84,6 +85,11 @@ async function legacy_behavior(req, result, parent) {
|
|
|
84
85
|
}
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
function parent_unreadable_error(e) {
|
|
89
|
+
if (cds.error.isSystemError(e)) throw e
|
|
90
|
+
LOG._debug && LOG.debug('Parent readable check failed:', e)
|
|
91
|
+
}
|
|
92
|
+
|
|
87
93
|
exports._handle_result = legacy_behavior
|
|
88
94
|
exports._always_return_arrays = on_off => {
|
|
89
95
|
if (on_off === undefined) return exports._handle_result === new_behavior
|
|
@@ -4,7 +4,7 @@ const { setEntityContained } = require('./csn')
|
|
|
4
4
|
|
|
5
5
|
// scope: simple wheres à la "[{ ref: ['foo'] }, '=', { val: 'bar' }, 'and', ... ]"
|
|
6
6
|
function where2obj(where, target = null, data = {}) {
|
|
7
|
-
for (let i = 0; i < where.length;) {
|
|
7
|
+
for (let i = 0; i < where.length; ) {
|
|
8
8
|
const a = where[i++]
|
|
9
9
|
if (a.xpr) {
|
|
10
10
|
where2obj(a.xpr, target, data)
|
|
@@ -1090,7 +1090,7 @@ const draftHandle = async function (req) {
|
|
|
1090
1090
|
for (const assoc in target.associations) {
|
|
1091
1091
|
const val = data[assoc]
|
|
1092
1092
|
if (val && typeof val === 'object') {
|
|
1093
|
-
const _target =
|
|
1093
|
+
const _target = target.associations[assoc]._target
|
|
1094
1094
|
if (Array.isArray(val)) {
|
|
1095
1095
|
val.forEach(v => _rmIsActiveEntity(v, _target))
|
|
1096
1096
|
} else {
|
package/node_modules/@sap/cds/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js
CHANGED
|
@@ -11,7 +11,7 @@ const _JSONorString = string => {
|
|
|
11
11
|
const normalizeIncomingMessage = message => {
|
|
12
12
|
const _payload = typeof message === 'object' ? message : _JSONorString(message)
|
|
13
13
|
let data, headers
|
|
14
|
-
if (typeof _payload === 'object' && 'data' in _payload) {
|
|
14
|
+
if (_payload !== null && typeof _payload === 'object' && 'data' in _payload) {
|
|
15
15
|
data = _payload.data
|
|
16
16
|
headers = { ..._payload }
|
|
17
17
|
delete headers.data
|
|
@@ -161,7 +161,7 @@ function _JSONorString(string) {
|
|
|
161
161
|
function _normalizeIncomingMessage(message) {
|
|
162
162
|
const _payload = _JSONorString(message)
|
|
163
163
|
let data, headers
|
|
164
|
-
if (typeof _payload === 'object' && 'data' in _payload) {
|
|
164
|
+
if (_payload !== null && typeof _payload === 'object' && 'data' in _payload) {
|
|
165
165
|
data = _payload.data
|
|
166
166
|
headers = { ..._payload }
|
|
167
167
|
delete headers.data
|
|
@@ -8,14 +8,16 @@ module.exports = (target, srv, result, keys_as_segments) => {
|
|
|
8
8
|
|
|
9
9
|
const keyValuePairs = filteredKeys.reduce((acc, key) => {
|
|
10
10
|
const value = result[key]
|
|
11
|
-
if (result[key] === undefined) return acc
|
|
12
11
|
|
|
13
12
|
if (Buffer.isBuffer(value)) {
|
|
14
|
-
|
|
13
|
+
const base64 = value.toString('base64')
|
|
14
|
+
acc[key] = keys_as_segments ? base64 : `binary'${base64}'`
|
|
15
|
+
} else if (keys_as_segments) {
|
|
16
|
+
acc[key] = value
|
|
15
17
|
} else {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
const el = target.elements[key]
|
|
19
|
+
const needsQuotes = typeof value === 'string' && (el._type !== 'cds.UUID' || el['@odata.Type'] === 'Edm.String')
|
|
20
|
+
acc[key] = needsQuotes ? `'${value}'` : value
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
return acc
|
|
@@ -24,19 +26,16 @@ module.exports = (target, srv, result, keys_as_segments) => {
|
|
|
24
26
|
if (keys_as_segments) {
|
|
25
27
|
let location = targetName
|
|
26
28
|
for (const k in keyValuePairs) location += `/${keyValuePairs[k]}`
|
|
27
|
-
return location
|
|
29
|
+
return encodeURI(location)
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
let keys
|
|
31
33
|
const entries = Object.entries(keyValuePairs)
|
|
32
34
|
if (entries.length === 1) {
|
|
33
35
|
keys = entries[0][1]
|
|
34
|
-
if (target.elements[entries[0][0]]['@odata.Type'] === 'Edm.String') keys = `'${keys}'`
|
|
35
36
|
} else {
|
|
36
|
-
keys = entries
|
|
37
|
-
.map(([key, value]) => `${key}=${target.elements[key]['@odata.Type'] === 'Edm.String' ? `'${value}'` : value}`)
|
|
38
|
-
.join(',')
|
|
37
|
+
keys = entries.map(([key, value]) => `${key}=${value}`).join(',')
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
return `${targetName}(${keys})`
|
|
40
|
+
return encodeURI(`${targetName}(${keys})`)
|
|
42
41
|
}
|
|
@@ -126,7 +126,7 @@ const _isNullableSingleton = query => query._target._isSingleton && query._targe
|
|
|
126
126
|
const _isToOneAssoc = query =>
|
|
127
127
|
query.SELECT.from.ref.length > 1 && typeof query.SELECT.from.ref.slice(-1)[0] === 'string'
|
|
128
128
|
|
|
129
|
-
const _getCountFromResult = result => (Array.isArray(result) ? (result[0]
|
|
129
|
+
const _getCountFromResult = result => (Array.isArray(result) ? (result[0]?.$count ?? 0) : (result?.$count ?? 0))
|
|
130
130
|
|
|
131
131
|
const _getCountFromResultForArrayOfQueries = result => {
|
|
132
132
|
if (!Array.isArray(result)) return result.$count ?? 0
|
|
@@ -24,6 +24,8 @@ const _setLongTimeout = (cb, delay) => {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
let T0
|
|
27
|
+
const MISSING_T0 =
|
|
28
|
+
'Skipping periodic message processing: no provider tenant is configured via `cds.requires.multitenancy.t0`. This is unexpected outside the `mtx-sidecar` profile.'
|
|
27
29
|
|
|
28
30
|
module.exports = class SchedulingService extends cds.Service {
|
|
29
31
|
constructor(...args) {
|
|
@@ -101,6 +103,8 @@ module.exports = class SchedulingService extends cds.Service {
|
|
|
101
103
|
|
|
102
104
|
// NOTE: in the future, we also need to trigger task processing in sidecar for mtx
|
|
103
105
|
if (cds.env.profile !== 'mtx-sidecar') {
|
|
106
|
+
if (!T0) return LOG.warn(MISSING_T0)
|
|
107
|
+
|
|
104
108
|
const periodic_flush = setInterval(() => _flush(T0), flush_interval_ms).unref()
|
|
105
109
|
cds.on('shutdown', () => clearInterval(periodic_flush))
|
|
106
110
|
_flush(T0)
|