@sap/cds 9.0.4 → 9.2.0

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 (77) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/bin/deploy.js +29 -0
  3. package/bin/serve.js +1 -5
  4. package/lib/compile/etc/csv.js +11 -6
  5. package/lib/compile/for/lean_drafts.js +29 -7
  6. package/lib/compile/load.js +8 -5
  7. package/lib/compile/to/hdbtabledata.js +1 -1
  8. package/lib/dbs/cds-deploy.js +5 -34
  9. package/lib/env/cds-env.js +2 -1
  10. package/lib/env/cds-requires.js +4 -1
  11. package/lib/env/defaults.js +0 -11
  12. package/lib/env/schemas/cds-rc.js +218 -6
  13. package/lib/index.js +38 -38
  14. package/lib/log/cds-error.js +12 -11
  15. package/lib/log/format/json.js +1 -1
  16. package/lib/ql/SELECT.js +31 -0
  17. package/lib/ql/resolve.js +1 -1
  18. package/lib/req/context.js +1 -1
  19. package/lib/req/request.js +1 -1
  20. package/lib/req/validate.js +17 -19
  21. package/lib/srv/cds.Service.js +18 -28
  22. package/lib/srv/middlewares/auth/ias-auth.js +29 -2
  23. package/lib/srv/middlewares/auth/jwt-auth.js +11 -1
  24. package/lib/srv/middlewares/auth/xssec.js +1 -1
  25. package/lib/srv/srv-models.js +1 -1
  26. package/lib/srv/srv-tx.js +2 -2
  27. package/lib/utils/cds-utils.js +35 -2
  28. package/lib/utils/csv-reader.js +1 -1
  29. package/lib/utils/inflect.js +2 -2
  30. package/lib/utils/tar.js +60 -23
  31. package/lib/utils/version.js +18 -0
  32. package/libx/_runtime/cds.js +1 -1
  33. package/libx/_runtime/common/aspects/any.js +1 -23
  34. package/libx/_runtime/common/generic/crud.js +1 -3
  35. package/libx/_runtime/common/generic/input.js +113 -52
  36. package/libx/_runtime/common/generic/sorting.js +1 -1
  37. package/libx/_runtime/common/generic/temporal.js +0 -6
  38. package/libx/_runtime/common/utils/draft.js +1 -1
  39. package/libx/_runtime/common/utils/entityFromCqn.js +1 -1
  40. package/libx/_runtime/common/utils/propagateForeignKeys.js +1 -1
  41. package/libx/_runtime/common/utils/resolveView.js +2 -2
  42. package/libx/_runtime/common/utils/structured.js +2 -2
  43. package/libx/_runtime/common/utils/templateProcessor.js +0 -5
  44. package/libx/_runtime/common/utils/vcap.js +1 -1
  45. package/libx/_runtime/fiori/lean-draft.js +529 -143
  46. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +3 -2
  47. package/libx/_runtime/messaging/service.js +1 -1
  48. package/libx/_runtime/remote/utils/client.js +2 -1
  49. package/libx/common/assert/utils.js +2 -12
  50. package/libx/common/utils/streaming.js +4 -9
  51. package/libx/http/location.js +1 -0
  52. package/libx/odata/ODataAdapter.js +47 -43
  53. package/libx/odata/index.js +1 -1
  54. package/libx/odata/middleware/batch.js +6 -2
  55. package/libx/odata/middleware/create.js +1 -1
  56. package/libx/odata/middleware/error.js +27 -17
  57. package/libx/odata/middleware/operation.js +15 -21
  58. package/libx/odata/middleware/stream.js +1 -1
  59. package/libx/odata/parse/afterburner.js +22 -8
  60. package/libx/odata/parse/cqn2odata.js +16 -10
  61. package/libx/odata/parse/grammar.peggy +185 -134
  62. package/libx/odata/parse/parser.js +1 -1
  63. package/libx/odata/utils/index.js +1 -36
  64. package/libx/odata/utils/metadata.js +34 -1
  65. package/libx/odata/utils/odataBind.js +2 -1
  66. package/libx/odata/utils/result.js +22 -20
  67. package/libx/queue/index.js +7 -4
  68. package/libx/rest/RestAdapter.js +1 -2
  69. package/libx/rest/middleware/create.js +5 -2
  70. package/package.json +2 -2
  71. package/server.js +1 -1
  72. package/bin/deploy/to-hana.js +0 -1
  73. package/lib/utils/check-version.js +0 -9
  74. package/lib/utils/unit.js +0 -19
  75. package/libx/_runtime/cds-services/util/assert.js +0 -181
  76. package/libx/_runtime/types/api.js +0 -129
  77. package/libx/common/assert/validation.js +0 -109
package/CHANGELOG.md CHANGED
@@ -4,6 +4,64 @@
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.2.0 - 2025-07-29
8
+
9
+ ### Added
10
+
11
+ - `srv.schedule` allows to specify the time in a more readable way, e.g. `srv.schedule(...).after('1min')`
12
+ - Support for `jwt`/`xsuaa`-auth on XSA
13
+ - Enable `@sap/xssec`'s caching mechanisms (requires `@sap/xssec^4.8`)
14
+ + The signature cache can be configured via `cds.requires.auth.config`, which is passed to `@sap/xssec`'s authentication services
15
+ + The token decode cache can be configured programmatically via `require('@sap/xssec').Token.enableDecodeCache(config?)` and deactivated via `require('@sap/xssec').Token.decodeCache = false`
16
+ - `cds.requires` correctly resolve service credentials on Kyma when its merged env configuration is only `true` and the service is found via its property name.
17
+ - `ias`-auth: Support for fallback XSUAA-based authentication meant to ease migration to IAS
18
+ + The fallback is automatically enabled if XSUAA credentials are available. To enable the credentials look-up, simply add `cds.requires.xsuaa = true` to your env.
19
+ + In case you need a custom config for the fallback (passed through to `@sap/xssec` as is!), configure it via `cds.requires.xsuaa = { config: { ... } }`
20
+ - Better error message if `cds.xt.Extensions` table is missing in extensibility scenarios.
21
+
22
+ ### Changed
23
+
24
+ - Upgrade to Peggy 5 version
25
+ - Enabled conversion of `not exists where not` to OData `all`, integrating the inverse of the policy applied by the OData parser.
26
+ - Numeric values in `.csv` files are now returned as numbers instead of strings, e.g. `1` instead of `'1'`;
27
+ when pre-padded with zeros, e.g., `0123`, they are returned as strings, e.g. `'0123'` instead of `123`.
28
+
29
+ ### Fixed
30
+
31
+ - Runtime error in transaction handling in messaging services when used with outbox
32
+ - Always use `cds.context` middleware for `enterprise-messaging` endpoints
33
+ - Crash during Location header generation caused by custom response not matching the entity definition.
34
+ - Support for logging of correct error locations with `cds watch` and `cds run`.
35
+ - Double-unescaping of values in double quotes during OData URL parsing
36
+ - Throw explicit error if the result of a media data query is not an instance of `Readable`, rather than responding with `No Content`
37
+ - When loading `.csv` files quoted strings containing the separator (comma or semicolon) where erroneously
38
+ parsed as two separate values instead of one.
39
+
40
+ ## Version 9.1.0 - 2025-06-30
41
+
42
+ ### Added
43
+
44
+ - CDS config schema validations for `cds.requires.auth.tenants`, `cds.cdsc`, `cds.query`, `cds.log`, `cds.server`
45
+ - Queue option `targetPrefix` to prefix `target` value of `cds.outbox.Messages` entries for microservice isolation
46
+ - Basic support for CRUD for hierarchy entities
47
+
48
+ ### Changed
49
+
50
+ - Reduced the amount of SELECT nesting the OData adapter does for `$apply` queries.
51
+ - Better error messages for unresolved parent associations in hierarchy requests
52
+ - Enabled updated behavior of `draftActivate` to move updates to fields of draft enabled entities with type `cds.LargeBinary` from draft to active table on the database level, with feature flag `cds.env.fiori.move_media_data_in_db`.
53
+
54
+ ### Fixed
55
+
56
+ - Copies of `cds.context` with `locale`
57
+ - Support for relative paths in `@odata.bind`
58
+ - `cds build` on Windows OS - fixed cli tar usage for resources.tgz
59
+ - Actions and functions with scalar return types use same `@odata.context` calculation as other return types, fixing e.g. `cds.odata.contextAbsoluteUrl` not being respected
60
+ - Improve content-type and content-length handling in OData adapter
61
+ - Parsing incorrect function parameters
62
+ - `cds deploy --dry` no longer tries to load a DB adapter, so that it works w/o one installed.
63
+ - Fix `@mandatory` for actions and functions
64
+
7
65
  ## Version 9.0.4 - 2025-06-18
8
66
 
9
67
  ### Fixed
@@ -38,6 +96,7 @@
38
96
  ### Changed
39
97
 
40
98
  - Lean draft handler is registered in a service only if a draft-enabled service entity exists
99
+ - Add back in server version on CAP server launch info log record
41
100
 
42
101
  ### Fixed
43
102
 
@@ -135,6 +194,15 @@
135
194
  - Deprecated stripping of unnecessary topic prefix `topic:` in messaging
136
195
  - Deprecated messaging `Outbox` class. Please use config or `cds.outboxed(srv)` to outbox your service.
137
196
 
197
+ ## Version 8.9.5 - 2025-07-25
198
+
199
+ ### Fixed
200
+
201
+ - `req.diff` in case of draft entities using associations to joins/unions
202
+ - Locale detection does not enforce `<http-req>.query` to be present. Some protocol adapters do not set it.
203
+ - View metadata for requests with $apply
204
+ - Handling of bad timestamps in URL ($filter and temporals)
205
+
138
206
  ## Version 8.9.4 - 2025-05-16
139
207
 
140
208
  ### Fixed
package/bin/deploy.js ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable no-console */
3
+
4
+ const cds = require('../lib');
5
+ const argv = process.argv.slice(2);
6
+ const o = {};
7
+ cds.cli = { command: 'deploy', argv, options: o }
8
+
9
+ Promise.resolve(cds.plugins).then(async () => {
10
+ let db = cds.requires.db || {}
11
+ for (let i = 0; i < argv.length; ++i) {
12
+ let k = argv[i], v = argv[++i]
13
+ if (!k.startsWith('--')) return error `Invalid argument: ${k}. Expected format --key value`
14
+ if (v?.startsWith('--')) { --i; v = true } // allow --key without value
15
+ o[k = k.slice(2)] = v ?? true // allow last --key without value
16
+ if (k === 'to') db = { kind: v, dialect: v }
17
+ else (db.credentials ??= {})[k] = v
18
+ }
19
+ console.debug('Deploying with options:', db, o)
20
+ cds.db = await cds.connect.to(db)
21
+ return await cds.deploy('*',o).to(db)
22
+ })
23
+ .catch (e => { console.error(e); process.exitCode = 1 })
24
+ .finally (() => cds.db?.disconnect?.())
25
+
26
+ const error = (...args) => {
27
+ console.error (String.raw(...args))
28
+ process.exitCode = 1
29
+ }
package/bin/serve.js CHANGED
@@ -275,10 +275,6 @@ async function _local_server_js() {
275
275
  function _prepare_logging () { // NOSONAR
276
276
 
277
277
  const LOG = cds.log('cds.serve|server',{label:'cds'}); if (!LOG._info) return; else log = LOG.info
278
- const _timer = process.env.NODE_ENV === 'production'
279
- ? `[cds] - server launched at ${new Date().toLocaleString()}, version: ${cds.version}, in`
280
- : '[cds] - server launched in'
281
- console.time (_timer)
282
278
 
283
279
  // print information when model is loaded
284
280
  cds.on ('loaded', ({$sources:srcs})=>{
@@ -313,7 +309,7 @@ function _prepare_logging () { // NOSONAR
313
309
  cds.once ('listening', ({url})=>{
314
310
  console.log()
315
311
  LOG.info ('server listening on',{url})
316
- _timer && console.timeEnd (_timer)
312
+ LOG.info ('server', 'v'+cds.version, 'launched in', performance.now().toFixed(0),'ms')
317
313
  if (process.stdin.isTTY) LOG.info (`[ terminate with ^C ]\n`)
318
314
  })
319
315
  }
@@ -34,6 +34,10 @@ function parse (csv) {
34
34
  if (headers.includes(currCol)) values.push (_value4(val, quoted)) // skip value if column was skipped
35
35
  val = undefined, quoted = false //> start new val
36
36
  }
37
+ else if (c === ' ' && val === undefined) {
38
+ // ignore leading spaces
39
+ continue
40
+ }
37
41
  else if (c === '"' && val === undefined) { // start quoted string
38
42
  val = ''
39
43
  inString = true
@@ -43,7 +47,7 @@ function parse (csv) {
43
47
  else inString = false, quoted = true // stop string
44
48
  }
45
49
  else { // normal char
46
- if (val === undefined) val = ''
50
+ val ??= ''
47
51
  val += c
48
52
  }
49
53
  }
@@ -59,12 +63,13 @@ function parse (csv) {
59
63
  return rows
60
64
  }
61
65
 
62
- function _value4 (val, quoted = false) {
66
+ const globals = { null:null, true:true, false:false }
67
+ function _value4 (val, quoted) {
63
68
  if (quoted) return val
64
- if (val) val = val.trim()
65
- if (val === 'true') return true
66
- else if (val === 'false') return false
67
- else return val
69
+ if (val) val = val.trim(); else return undefined
70
+ if (val in globals) return globals[val] //> null, true, false
71
+ let n = Number(val)
72
+ return n.toString() == val ? n : val
68
73
  }
69
74
 
70
75
  function _ignoreLine(line) {
@@ -41,16 +41,14 @@ const { Draft } = cds.linked(`
41
41
  }
42
42
  `).definitions
43
43
 
44
-
45
- function DraftEntity4 (active, name = active.name+'.drafts') {
46
-
47
- const draft = Object.create (active, {
44
+ function DraftEntity4(active, name = active.name + '.drafts') {
45
+ const draft = Object.create(active, {
48
46
  name: { value: name }, // REVISIT: lots of things break if we do that!
49
47
  elements: { value: { ...active.elements, ...Draft.elements }, enumerable: true },
50
48
  actives: { value: active },
51
49
  query: { value: undefined }, // to not inherit that from active
52
50
  // drafts: { value: undefined }, // to not inherit that from active -> doesn't work yet as the coding in lean-draft.js uses .drafts to identify both active and draft entities
53
- isDraft: { value: true },
51
+ isDraft: { value: true }
54
52
  })
55
53
 
56
54
  // for quoted names, we need to overwrite the cds.persistence.name of the derived, draft entity
@@ -60,7 +58,6 @@ function DraftEntity4 (active, name = active.name+'.drafts') {
60
58
  return draft
61
59
  }
62
60
 
63
-
64
61
  module.exports = function cds_compile_for_lean_drafts(csn) {
65
62
  function _redirect(assoc, target) {
66
63
  assoc.target = target.name
@@ -153,7 +150,8 @@ module.exports = function cds_compile_for_lean_drafts(csn) {
153
150
  key === '@mandatory' ||
154
151
  key === '@Common.FieldControl' && newEl[key]?.['#'] === 'Mandatory' ||
155
152
  // key === '@Core.Immutable': Not allowed via UI anyway -> okay to cleanse them in PATCH
156
- key.startsWith('@assert') ||
153
+ // REVISIT: Remove feature flag dependency: If active, validation errors will be degraded to messages and stored in draft admin data
154
+ (!active._service?.entities.DraftAdministrativeData.elements.DraftMessages && key.startsWith('@assert')) ||
157
155
  key.startsWith('@PersonalData')
158
156
  )
159
157
  newEl[key] = undefined
@@ -162,6 +160,23 @@ module.exports = function cds_compile_for_lean_drafts(csn) {
162
160
  draft.elements[each] = newEl
163
161
  }
164
162
 
163
+ // For list-report hierarchies, there must not be deep deletes w.r.t. recursive compositions
164
+ // Therefore, they are degraded to associations.
165
+ // Note: For object-page hiearchies, deep delete on draft recursive compositions is still needed.
166
+ if (draft.elements.LimitedDescendantCount && draft['@Common.DraftRoot.ActivationAction']) {
167
+ for (const c in draft.compositions) {
168
+ const comp = draft.compositions[c]
169
+ if (comp.target === draft.name) {
170
+ // modify comp to assoc
171
+ comp.type = 'cds.Association'
172
+ comp.isComposition = false
173
+ comp.is = function(kind) { return kind === 'Association' }
174
+ delete draft.compositions[c]
175
+ draft.associations[c] = comp
176
+ }
177
+ }
178
+ }
179
+
165
180
  return draft
166
181
  }
167
182
 
@@ -173,6 +188,13 @@ module.exports = function cds_compile_for_lean_drafts(csn) {
173
188
  def.elements.HasActiveEntity.virtual = true
174
189
  if (def.elements.DraftAdministrativeData_DraftUUID) def.elements.DraftAdministrativeData_DraftUUID.virtual = true
175
190
  def.elements.DraftAdministrativeData.virtual = true
191
+ if (def.elements.LimitedDescendantCount) {
192
+ // for hierarchies: make sure recursive compositions are not part of the draft tree
193
+ for (const c in def.compositions) {
194
+ const comp = def.compositions[c]
195
+ if (comp.target === def.name) comp['@odata.draft.ignore'] = true
196
+ }
197
+ }
176
198
  // will insert drafts entities, so that others can use `.drafts` even without incoming draft requests
177
199
  addDraftEntity(def, csn)
178
200
  }
@@ -9,13 +9,16 @@ if (TRACE) {
9
9
  module.exports = exports = function load (files, options) {
10
10
  let any = cds.resolve(files,options)
11
11
 
12
+ // REVISIT: we need to find a better way to handle this -> doing that in cds.load is by far too central
12
13
  // REVISIT: bandaid for grow as you go scenario with task queues enabled by default
13
14
  let locations
14
- const _is_outbox = p => cds.utils.path.posix.normalize(p).match(/\/cds\/srv\/outbox(\.cds)?$/)
15
- const _outbox_only = any?.length === 1 && _is_outbox(any[0]) && (!Array.isArray(files) || !files.some(_is_outbox))
16
- if (_outbox_only) {
17
- any = undefined
18
- locations = cds.resolve(files, false).filter(f => !_is_outbox(f))
15
+ if (cds.watched) {
16
+ const _is_outbox = p => cds.utils.path.posix.normalize(p).match(/\/cds\/srv\/outbox(\.cds)?$/)
17
+ const _outbox_only = any?.length === 1 && _is_outbox(any[0]) && (!Array.isArray(files) || !files.some(_is_outbox))
18
+ if (_outbox_only) {
19
+ any = undefined
20
+ locations = cds.resolve(files, false).filter(f => !_is_outbox(f))
21
+ }
19
22
  }
20
23
 
21
24
  if (!any) return Promise.reject (new cds.error ({
@@ -1,4 +1,4 @@
1
- const cds = require('../../../lib')
1
+ const cds = require('../..')
2
2
  const { getElementCdsPersistenceName, getArtifactCdsPersistenceName } = require('@sap/cds-compiler')
3
3
  const { fs, path, isdir, csv } = cds.utils
4
4
  const { readdir } = fs.promises
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  const cds = require('../index'), { local, path } = cds.utils
3
2
  const DEBUG = cds.debug('deploy')
4
3
  const TRACE = cds.debug('trace')
@@ -20,6 +19,9 @@ const deploy = module.exports = function cds_deploy (model, options, csvs) {
20
19
  if (o.mocked) deploy.include_external_entities_in(model)
21
20
  else deploy.exclude_external_entities_in(model)
22
21
 
22
+ // dry deployment (output schema only)
23
+ if (o.dry) return await deploy.schema ({model, options:{}}, model, o)
24
+
23
25
  // prepare db
24
26
  if (!db.run) db = await cds.connect.to(db)
25
27
  if (!cds.db) cds.db = cds.services.db = db
@@ -34,10 +36,9 @@ const deploy = module.exports = function cds_deploy (model, options, csvs) {
34
36
 
35
37
  // deploy schema and initial data...
36
38
  try {
37
- const _run = fn => o.dry ? fn(db) : db.run(fn)
38
- await _run (async tx => {
39
+ await db.run (async tx => {
39
40
  let any = await deploy.schema (tx, model, o)
40
- if ((any || csvs) && !o.dry) await deploy.data (tx, model, o, csvs, file => LOG?.(GREY, ' > init from', local(file), RESET))
41
+ if (any || csvs) await deploy.data (tx, model, o, csvs, file => LOG?.(GREY, ' > init from', local(file), RESET))
41
42
  })
42
43
  LOG?.('/> successfully deployed to', descr, '\n')
43
44
  } catch (e) {
@@ -385,33 +386,3 @@ const _entity4 = (file, csn) => {
385
386
  }
386
387
  return entity.name ? entity : { name, __proto__:entity }
387
388
  }
388
-
389
- /** CLI used as via cds-deploy as deployer for PostgreSQL */
390
- if (!module.parent) (async function CLI () {
391
- cds.cli = { command: 'deploy', argv: process.argv.slice(2), options: {} }
392
- await cds.plugins // IMPORTANT: that has to go before any call to cds.env, like through cds.deploy or cds.requires below
393
- let db = cds.requires.db
394
- try {
395
- let o={}, recent
396
- for (let each of process.argv.slice(2)) {
397
- if (each.startsWith('--')) o[(recent = each.slice(2))] = true
398
- else o[recent] = each
399
- }
400
- if (o.to) {
401
- db = { kind: o.to, dialect: o.to }
402
- if (o.url) (db.credentials ??= {}).url = o.url
403
- if (o.host) (db.credentials ??= {}).host = o.host
404
- if (o.port) (db.credentials ??= {}).port = o.port
405
- if (o.username) (db.credentials ??= {}).username = o.username
406
- if (o.password) (db.credentials ??= {}).password = o.password
407
- }
408
- cds.cli.options = o
409
- db = await cds.connect.to(db);
410
- db = await cds.deploy('*',o).to(db)
411
- } finally {
412
- await db?.disconnect?.()
413
- }
414
- })().catch((e) => {
415
- console.error(e)
416
- process.exitCode = 1
417
- })
@@ -408,7 +408,8 @@ class Config {
408
408
  const { credentials } = this._find_credentials_for_required_service(service, conf, vcaps) || {}
409
409
  if (credentials) {
410
410
  // Merge `credentials`. Needed because some app-defined things like `credentials.destination` must survive.
411
- any = conf.credentials = Object.assign ({}, conf.credentials, credentials)
411
+ if (conf === true) any = this.requires[service] = { credentials }
412
+ else any = conf.credentials = { ...conf.credentials, ...credentials }
412
413
  }
413
414
  }
414
415
  return !!any
@@ -85,6 +85,9 @@ for (let each of Object.values(_authentication_strategies)) {
85
85
  }})
86
86
  }
87
87
 
88
+ // enable credentials lookup for xsuaa fallback
89
+ _authentication_strategies.xsuaa = { vcap: { label: 'xsuaa' } }
90
+
88
91
  const _services = {
89
92
 
90
93
  "app-service": {
@@ -157,7 +160,7 @@ const _queue = {
157
160
  storeLastError: true,
158
161
  timeout: '1h',
159
162
  legacyLocking: true,
160
- ignoredContext: ['user', 'http', 'model', 'timestamp']
163
+ ignoredContext: ['user', 'http', 'model', 'timestamp', '_locale', '_features']
161
164
  },
162
165
  // legacy
163
166
  "in-memory-outbox": "in-memory-queue",
@@ -2,17 +2,6 @@ const production = process.env.NODE_ENV === 'production'
2
2
 
3
3
  module.exports = {
4
4
 
5
- /**
6
- * For our own tests to replace hard-coded checks for CDS_ENV === 'better-sqlite'
7
- * which don't work anymore with cds8 where that is the default.
8
- */
9
- get _better_sqlite() {
10
- if (process.env.CDS_ENV === 'better-sqlite') return true
11
- let conf = this.requires.db || this.requires.kinds.sql
12
- if (conf?.impl === '@cap-js/sqlite') return true
13
- else return false
14
- },
15
-
16
5
  production,
17
6
 
18
7
  requires: require('./cds-requires'),
@@ -81,23 +81,23 @@ module.exports = {
81
81
  folders: {
82
82
  type: 'object',
83
83
  default: {},
84
- description: 'Only set folders if you don\'t want to use the defaults \'app/\', \'db/\', \'srv/\'.',
84
+ description: 'Overrides for default folders. Only override if you don\'t want to use the defaults \'app/\', \'db/\', \'srv/\'.',
85
85
  additionalProperties: true,
86
86
  properties: {
87
87
  app: {
88
88
  type: 'string',
89
89
  format: 'uri-reference',
90
- description: 'Add a custom path for the app property, which becomes \'cds.roots\'.'
90
+ description: 'Applications, e.g. UI5 or Fiori apps'
91
91
  },
92
92
  db: {
93
93
  type: 'string',
94
94
  format: 'uri-reference',
95
- description: 'Add a custom path for the db property, which becomes \'cds.roots\'.'
95
+ description: 'Database models and migrations'
96
96
  },
97
97
  srv: {
98
98
  type: 'string',
99
99
  format: 'uri-reference',
100
- description: 'Add a custom path for the srv property, which becomes \'cds.roots\'.'
100
+ description: 'Services'
101
101
  }
102
102
  },
103
103
  patternProperties: {
@@ -168,7 +168,7 @@ module.exports = {
168
168
  properties: {
169
169
  kind: {
170
170
  type: 'string',
171
- description: 'Define the kind of strategy.',
171
+ description: 'Authentication kind.',
172
172
  anyOf: [
173
173
  {
174
174
  $ref: '#/$defs/authType'
@@ -181,6 +181,27 @@ module.exports = {
181
181
  users: {
182
182
  $ref: '#/$defs/mockUsers'
183
183
  },
184
+ tenants: {
185
+ type: 'object',
186
+ description: 'List of tenants with their respective features.',
187
+ additionalProperties: true,
188
+ patternProperties: {
189
+ '.+': {
190
+ type: 'object',
191
+ additionalProperties: true,
192
+ properties: {
193
+ features: {
194
+ type: 'array',
195
+ description: 'List of feature toggles for this tenant.',
196
+ uniqueItems: true,
197
+ items: {
198
+ type: 'string'
199
+ }
200
+ }
201
+ }
202
+ }
203
+ }
204
+ },
184
205
  credentials: {
185
206
  type: 'object',
186
207
  description: 'You can explicitly configure credentials, but this is overruled by VCAP_SERVICES if a matching entry is found therein.',
@@ -198,6 +219,10 @@ module.exports = {
198
219
  },
199
220
  db: {
200
221
  oneOf: [
222
+ {
223
+ type: 'boolean',
224
+ description: 'Shortcut to enable primary database.'
225
+ },
201
226
  {
202
227
  type: 'string',
203
228
  description: 'Settings for the primary database (shortcut).',
@@ -645,6 +670,193 @@ module.exports = {
645
670
  ]
646
671
  }
647
672
  }
673
+ },
674
+ cdsc: {
675
+ type: 'object',
676
+ default: {},
677
+ description: 'CDS compiler configuration options.',
678
+ additionalProperties: true,
679
+ properties: {
680
+ moduleLookupDirectories: {
681
+ type: 'array',
682
+ description: 'List of directories to search for modules.',
683
+ uniqueItems: true,
684
+ items: {
685
+ type: 'string',
686
+ format: 'uri-reference'
687
+ }
688
+ }
689
+ }
690
+ },
691
+ query: {
692
+ type: 'object',
693
+ default: {},
694
+ description: 'Query configuration options.',
695
+ additionalProperties: true,
696
+ properties: {
697
+ limit: {
698
+ type: 'object',
699
+ description: 'Default limit for queries.',
700
+ additionalProperties: true,
701
+ properties: {
702
+ default: {
703
+ type: 'integer',
704
+ description: 'Default number of results returned by a query.',
705
+ minimum: 1
706
+ },
707
+ max: {
708
+ type: 'integer',
709
+ description: 'Maximum number of results returned by a query.',
710
+ minimum: 1
711
+ },
712
+ reliablePaging: {
713
+ type: 'boolean',
714
+ description: 'Enable reliable paging for queries.'
715
+ }
716
+ }
717
+ }
718
+ }
719
+ },
720
+ log: {
721
+ type: 'object',
722
+ default: {},
723
+ description: 'Logging configuration options.',
724
+ additionalProperties: true,
725
+ properties: {
726
+ format: {
727
+ type: 'string',
728
+ description: 'Log format, either \'plain\' or \'json\'.',
729
+ enum: [
730
+ 'plain',
731
+ 'json'
732
+ ]
733
+ },
734
+ levels: {
735
+ type: 'object',
736
+ description: 'Log levels for different components.',
737
+ additionalProperties: {
738
+ type: 'string'
739
+ },
740
+ properties: {
741
+ cds: {
742
+ type: 'string',
743
+ description: 'Server and common output.'
744
+ },
745
+ cli: {
746
+ type: 'string',
747
+ description: 'CLI output.'
748
+ },
749
+ build: {
750
+ type: 'string',
751
+ description: 'CDS build output.'
752
+ },
753
+ app: {
754
+ type: 'string',
755
+ description: 'Application Service.'
756
+ },
757
+ db: {
758
+ type: 'string',
759
+ description: 'Databases.'
760
+ },
761
+ sql: {
762
+ type: 'string',
763
+ description: 'SQL output.'
764
+ },
765
+ messaging: {
766
+ type: 'string',
767
+ description: 'Messaging Service.'
768
+ },
769
+ remote: {
770
+ type: 'string',
771
+ description: 'Remote Service.'
772
+ },
773
+ 'audit-log': {
774
+ type: 'string',
775
+ description: 'AuditLog Service.'
776
+ },
777
+ odata: {
778
+ type: 'string',
779
+ description: 'OData Protocol Adapter.'
780
+ },
781
+ rest: {
782
+ type: 'string',
783
+ description: 'REST Protocol Adapter.'
784
+ },
785
+ graphql: {
786
+ type: 'string',
787
+ description: 'GraphQL Protocol Adapter.'
788
+ },
789
+ auth: {
790
+ type: 'string',
791
+ description: 'Authentication.'
792
+ },
793
+ deploy: {
794
+ type: 'string',
795
+ description: 'Database Deployment.'
796
+ },
797
+ mtx: {
798
+ type: 'string',
799
+ description: 'Multitenancy and Extensibility.'
800
+ }
801
+ }
802
+ },
803
+ mask_headers: {
804
+ type: 'array',
805
+ description: 'List of header patterns to mask in logs.',
806
+ uniqueItems: true,
807
+ items: {
808
+ type: 'string'
809
+ },
810
+ default: [
811
+ '/authorization/i',
812
+ '/cookie/i',
813
+ '/cert/i',
814
+ '/ssl/i'
815
+ ]
816
+ },
817
+ als_custom_fields: {
818
+ type: 'object',
819
+ description:
820
+ `Custom fields for Application Logging Service (ALS) in Kibana's error rendering.
821
+ Key is the index in the log message array.`,
822
+ additionalProperties: {
823
+ type: 'integer'
824
+ }
825
+ },
826
+ cls_custom_fields: {
827
+ type: 'array',
828
+ description:
829
+ `Custom fields for CLS in Kibana's error rendering.
830
+ Each entry is a field name.`,
831
+ uniqueItems: true,
832
+ items: {
833
+ type: 'string'
834
+ }
835
+ }
836
+ }
837
+ },
838
+ server: {
839
+ type: 'object',
840
+ default: {},
841
+ description: 'Server configuration options.',
842
+ additionalProperties: true,
843
+ properties: {
844
+ port: {
845
+ type: 'integer',
846
+ description: 'Port number for the server to listen on.',
847
+ minimum: 1,
848
+ maximum: 65535
849
+ },
850
+ cors: {
851
+ type: 'boolean',
852
+ description: 'Enable CORS support.',
853
+ default: true
854
+ },
855
+ index: {
856
+ type: 'boolean',
857
+ description: 'Serve the default index page.'
858
+ }
859
+ }
648
860
  }
649
861
  }
650
862
  },
@@ -909,4 +1121,4 @@ module.exports = {
909
1121
  }
910
1122
  }
911
1123
  }
912
- }
1124
+ }