@sap/cds-dk 8.0.3 → 8.1.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 (31) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/bin/bind.js +3 -3
  3. package/bin/cds.js +11 -15
  4. package/bin/init.js +3 -6
  5. package/bin/version.js +5 -3
  6. package/lib/build/provider/hana/index.js +5 -6
  7. package/lib/build/provider/mtx-sidecar/index.js +10 -3
  8. package/lib/compile/to-mermaid.js +27 -12
  9. package/lib/deploy/to-hana/hana.js +10 -8
  10. package/lib/deploy/to-hana/hdiDeployUtil.js +9 -1
  11. package/lib/init/constants.js +9 -3
  12. package/lib/init/index.js +6 -4
  13. package/lib/init/projectReader.js +3 -3
  14. package/lib/init/template/audit-logging/files/package.sidecar.json +7 -0
  15. package/lib/init/template/audit-logging/index.js +1 -1
  16. package/lib/init/template/cf-manifest/index.js +4 -0
  17. package/lib/init/template/cov2ap/files/dependencies.package.json +5 -0
  18. package/lib/init/template/cov2ap/index.js +31 -0
  19. package/lib/init/template/event-queue/files/dependencies.package.json +5 -0
  20. package/lib/init/template/event-queue/index.js +35 -0
  21. package/lib/init/template/http/index.js +37 -28
  22. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +0 -1
  23. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +0 -1
  24. package/lib/init/template/sample/files/nodejs/admin-service.js +13 -8
  25. package/lib/init/template/sample/files/nodejs/cat-service.js +27 -15
  26. package/lib/init/template/typescript/files/package.json +3 -3
  27. package/lib/init/template/websocket/files/dependencies.package.json +5 -0
  28. package/lib/init/template/websocket/index.js +35 -0
  29. package/lib/init/template/workzone/index.js +0 -3
  30. package/npm-shrinkwrap.json +14 -11
  31. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,33 @@ This project adheres to [Semantic Versioning](http://semver.org/).
7
7
 
8
8
  The format is based on [Keep a Changelog](http://keepachangelog.com/).
9
9
 
10
+ ## Version 8.1.0 - 2024-07-26
11
+
12
+ ### Added
13
+
14
+ - `cds compile --to mermaid` can be configured with a layout direction like `LR`.
15
+ - `cds deploy --to hana` now shows the `hdi-deploy` version when running.
16
+
17
+ ### Changed
18
+
19
+ - Better generation of http files with `cds add http`, especially for draft requests.
20
+
21
+ ### Fixed
22
+
23
+ - `cds add` recognizes `hasCfManifest` as inactive unless a `manifest.yml` file exists.
24
+ - `cds add` help and suggestions are only shown when the `help` method is implemented.
25
+ - `cds add sample` does not include the deprecated `synchronizationMode` property in `manifest.json` files any more.
26
+ - `cds compile --to mermaid` no longer produces empty `namespace` blocks that would lead to rendering errors.
27
+ - `cds compile --to mermaid` no longer fails for complex queries like joins.
28
+ - `cds build --for mtx-sidecar` now correctly supports different `srv` folder name.
29
+ - `cds add audit-log` in combination with `cds add multitenancy` correctly adds the audit-log dependency to the MTX sidecar.
30
+ - `cds build` adds the correct hdbtabledata file paths to the hana result set.
31
+ - `cds add` plugins can now correctly interpret `cds.cli` properties.
32
+ - `cds deploy --to hana --dry` now shows correct files with their content.
33
+ - `cds version` now reports the correct version of `@sap/cds` if this one is installed locally along with a local `@sap/cds-dk` installation.
34
+ - Shell completion now works correctly for `cds init --add`.
35
+ - `cds build --for mtx-sidecar` no longer fails if the severity of a compilation error has been downgraded from `Error` to `Warning`.
36
+
10
37
  ## Version 8.0.3 - 2024-07-20
11
38
 
12
39
  ### Fixed
@@ -142,7 +169,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
142
169
 
143
170
  - `cds add notebook` is removed. CAP Jupyter Notebooks have been replaced by custom CAP Notebooks for VS Code which are now part of the CDS Editor. See https://cap.cloud.sap/docs/tools/#add-cds-editor for more.
144
171
 
145
-
146
172
  ## Version 7.9.6 - 2024-07-19
147
173
 
148
174
  ### Fixed
package/bin/bind.js CHANGED
@@ -46,7 +46,7 @@ module.exports = Object.assign(cds_bind, {
46
46
 
47
47
  *-a | --to-app-services* <app>
48
48
 
49
- Bind to a given application (Cloud Foundry only).
49
+ Bind services of a given application (Cloud Foundry only).
50
50
 
51
51
  *--no-create-service-key*
52
52
 
@@ -164,11 +164,11 @@ async function cds_bind(args, options) {
164
164
  options.targets = options.to.split(/,/g);
165
165
 
166
166
  if (options.targets.length >= 2 && options.serviceArg) {
167
- throw `Service argument cannot be specified together with multiple targets (${term.bold('--to')}) services. Use one service per call or omit the service argument.`;
167
+ throw `Service argument cannot be specified together with multiple target (${term.bold('--to')}) services. Use one service per call or omit the service argument.`;
168
168
  }
169
169
 
170
170
  if (options.targets.length >= 2 && options.kind) {
171
- throw `The option '--kind' cannot be specified together with multiple targets (${term.bold('--to')}) services. Use one service per call or omit the ${term.bold('--kind')} option.`;
171
+ throw `The option '--kind' cannot be specified together with multiple target (${term.bold('--to')}) services. Use one service per call or omit the ${term.bold('--kind')} option.`;
172
172
  }
173
173
 
174
174
  const services = await bind({ ...options, out });
package/bin/cds.js CHANGED
@@ -55,8 +55,15 @@ const cli = {
55
55
  argv = argv.slice(0, endOfOptions)
56
56
  }
57
57
 
58
- const unknown = new Set
59
- const args = this.args(task, argv, unknown)
58
+ // REVISIT: Have to load plugins to allow for plugin-contributed flags!
59
+ if (cmd === 'add') {
60
+ cds.cli = { command: 'add' }
61
+ await cds.plugins
62
+ for (const [, Plugin] of cds.add.registered) {
63
+ task.options.push(...Object.keys((new Plugin).options()).map(key => '--'+key))
64
+ }
65
+ }
66
+ const args = this.args(task, argv)
60
67
  args[0].push(...appendArgs)
61
68
  cds.cli = {
62
69
  command: cmd,
@@ -64,17 +71,6 @@ const cli = {
64
71
  options: args[1],
65
72
  }
66
73
 
67
- if (cmd === 'add') { // cds add plugins allow additional custom options
68
- await cds.plugins
69
- for (const [, Plugin] of cds.add.registered) {
70
- const options = Object.keys((new Plugin).options()).map(key => '--'+key)
71
- task.options.push(options)
72
- options.forEach(o => unknown.delete(o))
73
- }
74
- }
75
- if (unknown.size === 1) throw 'Invalid option: ' + Array.from(unknown)[0]
76
- if (unknown.size > 1) throw 'Invalid options: ' + Array.from(unknown).join(', ')
77
-
78
74
  if (args[1]?.['resolve-bindings']) await _resolveBindings({ silent: cmd === 'env' })
79
75
 
80
76
  // cds.root may for some subcommands not point to the correct directory (e.g. cds w where users can pass a project directory)
@@ -106,7 +102,7 @@ const cli = {
106
102
  },
107
103
 
108
104
  // TODO replace w/ common arg parser from node
109
- args (task, argv, unknown) {
105
+ args (task, argv) {
110
106
 
111
107
  const { options:o=[], flags:f=[], shortcuts:s=[] } = task
112
108
  const _global = /^--(profile|production|sql|odata|add-.*|build-.*|cdsc-.*|odata-.*|folders-.*)$/
@@ -120,7 +116,7 @@ const cli = {
120
116
  else if ((k = o.indexOf(a)) >= 0) add(o[k],argv[++i])
121
117
  else if ((k = f.indexOf(a)) >= 0) add(f[k])
122
118
  else if (_global.test(a)) add_global(a, _flags[a] || argv[++i])
123
- else unknown.add(a)
119
+ else throw 'Invalid option: '+ a
124
120
  }
125
121
  // consistent production setting for NODE_ENV and CDS_ENV
126
122
  // if (process.env.NODE_ENV !== 'production') process.env.NODE_ENV = process.env.CDS_ENV?.split(',').find(p => p === 'production') || process.env.NODE_ENV
package/bin/init.js CHANGED
@@ -61,18 +61,15 @@ async function handleCompletion(currentWord, previousWord, argv, util) {
61
61
  return allOptionsFlags;
62
62
  }
63
63
 
64
- if (previousWord === '--add') {
65
- return util.completionFs.readTemplates(argv);
66
- }
67
-
68
64
  if (previousWord === '--java:package') {
69
65
  const excludes = ['_merging'];
70
66
  return (await util.completionFs.readdir(currentWord, { files: false }))
71
67
  .filter(e => !excludes.includes(e));
72
68
  }
73
69
 
74
- if (!currentWord && init.options?.includes(previousWord)) {
75
- return [];
70
+ const templates = await util.completionFs.readTemplates(argv);
71
+ if (previousWord === '--add' || argv.includes('--add') && previousWord.endsWith(',')) {
72
+ return templates;
76
73
  }
77
74
 
78
75
  return allOptionsFlags;
package/bin/version.js CHANGED
@@ -61,7 +61,8 @@ function list_versions(args, options) {
61
61
  }
62
62
 
63
63
  function info({ root:cwd=process.cwd() }) {
64
- const env = require('../lib').env.for('cds', cwd)
64
+ const cds = require('../lib')
65
+ const env = cds.env.for('cds', cwd)
65
66
  env.cli ??= {}; env.cli.version ??= {}
66
67
  const javaInfo = !env.cli.version.skipjava && env['project-nature'] === 'java'
67
68
  if (javaInfo && process.stderr.isTTY) {
@@ -74,10 +75,11 @@ function info({ root:cwd=process.cwd() }) {
74
75
  ..._versions_from(_pkg_path('@sap/cds-dk', npmRoot), '@sap/cds-dk (global)'), // ... or installed globally
75
76
  ..._versions_from(_pkg_path('@sap/cds-dk', cwd)), // local dk, important for Java projects
76
77
  ..._versions_from(_pkg_path('@sap/cds', __dirname)), // @sap/cds from here
77
- ..._versions_from(cwd), // ... or from local app dependencies
78
+ ..._versions_from(_pkg_path('@sap/cds', cds.home)), // ... or local @sap/cds
79
+ ..._versions_from(cwd), // ... more from local app dependencies
78
80
  ..._versions_from(_pkg_path('@sap/cds-compiler', cwd, __dirname)), // compiler is no direct dependency, so list it explicitly
79
81
  'Node.js': process.version,
80
- 'home': require('../lib/cds').home, // effective (local) cds
82
+ 'home': cds.home, // effective cds home
81
83
  }
82
84
  // sort Node entries
83
85
  versions = Object.keys(versions).sort().reduce((all, key) => { all[key] = versions[key]; return all }, {})
@@ -26,7 +26,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
26
26
  if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
27
27
  this._result = {
28
28
  dest: this.task.dest,
29
- hana: []
29
+ hana: new Set()
30
30
  }
31
31
  }
32
32
  this.task.options.compileDest = path.resolve(this.task.dest, this.task.options.compileDest || DEFAULT_COMPILE_DEST_FOLDER)
@@ -228,7 +228,6 @@ class HanaBuildPlugin extends InternalBuildPlugin {
228
228
  }
229
229
 
230
230
  const promises = []
231
- const relDest = path.relative(this.task.dest, this.task.options.compileDest)
232
231
  const options = { ...this.options(), messages: this.messages, dirs: csvDirs, baseDir: this.task.options.compileDest }
233
232
 
234
233
  const toHdbtabledata = cds.compile.to.hdbtabledata ?? require(path.join(cds.home, 'bin/build/provider/hana/2tabledata')) // cds@6 compatibility
@@ -240,7 +239,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
240
239
  let tableDataPath = csvFolder.startsWith(this.task.options.compileDest) ? csvFolder : this.task.options.compileDest
241
240
  tableDataPath = path.join(tableDataPath, file)
242
241
  if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
243
- this._result.hana.push(path.join(relDest, file))
242
+ this._result.hana.add(path.relative(this.task.dest, tableDataPath))
244
243
  }
245
244
  promises.push(this.write(tableData).to(tableDataPath))
246
245
  }
@@ -296,7 +295,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
296
295
 
297
296
  hdiPlugins.add(suffix)
298
297
  if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
299
- this._result.hana.push(path.join(relDest, file))
298
+ this._result.hana.add(path.join(relDest, file))
300
299
  }
301
300
  promises.push(this.write(content).to(path.join(this.task.options.compileDest, file)))
302
301
  if (suffix === FILE_EXT_HDBTABLE) {
@@ -345,7 +344,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
345
344
  migrationTableFiles.push(path.join(relDbSrcDir, fileName))
346
345
  if (changed) {
347
346
  if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
348
- this._result.hana.push(path.join("src", fileName))
347
+ this._result.hana.add(path.join("src", fileName))
349
348
  }
350
349
  promises.push(this.write(content).to(path.join(dbSrcDir, fileName)))
351
350
  } else {
@@ -354,7 +353,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
354
353
  }
355
354
  } else {
356
355
  if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
357
- this._result.hana.push(path.join(relDestDir, fileName))
356
+ this._result.hana.add(path.join(relDestDir, fileName))
358
357
  }
359
358
  promises.push(this.write(content).to(path.join(this.task.options.compileDest, fileName)))
360
359
  if (suffix === FILE_EXT_HDBTABLE) {
@@ -45,7 +45,7 @@ class MtxSidecarBuildPlugin extends NodejsBuildPlugin {
45
45
  */
46
46
  async _buildNodeApp(sidecarEnv) {
47
47
  const destSidecar = this.task.dest
48
- const destSidecarSrc = path.join(destSidecar, cds.env.folders.srv)
48
+ const destSidecarSrc = path.join(destSidecar, sidecarEnv.folders.srv)
49
49
  const model = this._compileSidecarSync(sidecarEnv)
50
50
  await this.compileToJson(model, path.join(destSidecarSrc, DEFAULT_CSN_FILE_NAME))
51
51
  await this.collectLanguageBundles(model, path.join(destSidecarSrc, getI18nDefaultFolder()))
@@ -129,8 +129,15 @@ class MtxSidecarBuildPlugin extends NodejsBuildPlugin {
129
129
  this.pushMessage(`CDS models [${unresolved.join(', ')}] required by MTX sidecar cannot be resolved. Make sure up-to-date versions of the missing npm modules are installed.`, ERROR)
130
130
  }
131
131
 
132
- // synchronous compilation
133
- return cds.load(modelFilePaths, { sync: true, ...this.options() })
132
+ // using synchronous compilation as cds.root and cds.env have been switched to mtx/sidecar
133
+ // using different messages array ensures that cds compilation doesn't fail because of
134
+ // existing compilation error messages that have been downgraded to warnings
135
+ // see cap/issues/issues/16401#issuecomment-7205397
136
+ const messages = []
137
+ const model = cds.load(modelFilePaths, { sync: true, messages })
138
+ messages.forEach(msg => this.messages.push(msg))
139
+
140
+ return model
134
141
  } finally {
135
142
  // restore project scope
136
143
  cds.root = root
@@ -2,12 +2,13 @@ const cds = require('../cds')
2
2
 
3
3
  module.exports = function to_mermaid (model, options={}) {
4
4
  options = Object.assign({
5
- assocnames: false, // include association names
6
- elements : false, // include all elements { falsy | 'all'|true | 'keys' }
7
- min : false, // minify the model, i.e. remove unused definitions, available in CLI
8
- namespaces: true, // group entities by namespace/service
9
- queries : false, // include relations for queries
10
- service : undefined // filter service by name, available in CLI
5
+ assocnames: false, // include association names
6
+ elements : false, // include all elements { falsy | 'all'|true | 'keys' }
7
+ min : false, // minify the model, i.e. remove unused definitions, available in CLI
8
+ namespaces: true, // group entities by namespace/service
9
+ queries : false, // include relations for queries
10
+ service : undefined, // filter service by name, available in CLI
11
+ direction : undefined // direction of the graph
11
12
  }, cds.env.mermaid ?? {}, options)
12
13
 
13
14
  const elemOpts = options.elements === 'none' ? false : options.elements
@@ -27,6 +28,7 @@ module.exports = function to_mermaid (model, options={}) {
27
28
  const entityFilter = e => !e.name.startsWith('cds.') && !e.name.endsWith('.texts')
28
29
 
29
30
  let diag = ''
31
+ const direction = options.direction ? ` direction ${options.direction}\n` : ''
30
32
 
31
33
  // if requested, filter by one service
32
34
  model.all('service')
@@ -56,8 +58,10 @@ module.exports = function to_mermaid (model, options={}) {
56
58
  })
57
59
 
58
60
  // create namespace groups and classes
59
- Object.keys(namespaces).forEach(ns => {
60
- if (!diag.length) diag = `classDiagram\n`
61
+ Object.keys(namespaces)
62
+ .filter (ns => namespaces[ns].length) // empty namespaces can cause rendering errors, so skip them
63
+ .forEach(ns => {
64
+ if (!diag.length) diag = `classDiagram\n${direction}`
61
65
  diag += ` namespace ${ns.replaceAll('.','_')} {\n`
62
66
  namespaces[ns].forEach (e => {
63
67
  const simpleName = e.name.slice(e.name.lastIndexOf('.')+1)
@@ -65,7 +69,7 @@ module.exports = function to_mermaid (model, options={}) {
65
69
  diag += `["${simpleName}"]` // simple name as label
66
70
  if (elemOpts) {
67
71
  diag += ` {\n`
68
- e.elements.forEach (el => {
72
+ e.elements?.forEach (el => {
69
73
  if ((elemOpts === 'all' || elemOpts === true) || (el.key && elemOpts == 'keys')) {
70
74
  diag += ` +${el._type?.replace('cds.','')} ${el.name}`
71
75
  diag += `\n`
@@ -85,8 +89,8 @@ module.exports = function to_mermaid (model, options={}) {
85
89
  ? Array.from(seenEntities).map(name => model.definitions[name])
86
90
  : model.all('entity').filter (entityFilter) // if no grouping and no service filter is on
87
91
  entities.forEach(e => {
88
- if (!diag.length) diag = `classDiagram\n`
89
- e.elements.forEach (el => {
92
+ if (!diag.length) diag = `classDiagram\n${direction}`
93
+ e.elements?.forEach (el => {
90
94
  if (el instanceof cds.Association) {
91
95
  if (el.target.endsWith('.texts')) return
92
96
  const type = el.type === 'cds.Composition' ? '*-->' : '-->'
@@ -100,9 +104,20 @@ module.exports = function to_mermaid (model, options={}) {
100
104
  })
101
105
  // add relations for queries
102
106
  if (e.query && options.queries) {
103
- diag += ` \`${e.name}\` ..> \`${e.query.target.name}\`\n`
107
+ resolveTargets(e.query).forEach(t => {
108
+ diag += ` \`${e.name}\` ..> \`${t}\`\n`
109
+ })
104
110
  }
105
111
  })
106
112
 
107
113
  return diag.trim()
108
114
  }
115
+
116
+ function resolveTargets (query) {
117
+ if (query.target) return [ query.target.name ] // simple query, like projection on
118
+
119
+ // SELECT: { from: { args: [{ ref: ['t1'] }, { ref: ['t2'] }], join: ..., on: ... } }
120
+ const targets = query.SELECT?.from?.args?.filter(arg => arg.ref?.length).map (arg => arg.ref[0])
121
+
122
+ return targets
123
+ }
@@ -6,7 +6,6 @@ const cds = require('../../../lib/cds');
6
6
  const { exists, read } = cds.utils;
7
7
 
8
8
  const { build } = require('../../build');
9
- const buildConstants = require('../../build/constants');
10
9
 
11
10
  const hdi = require('./hdiDeployUtil');
12
11
  const cfUtil = require('../../util/cf');
@@ -16,6 +15,7 @@ const mtaUtil = require('./mtaUtil');
16
15
  const { bold, info, warn } = require('../../util/term');
17
16
 
18
17
  const { bind, storeBindings } = require('../../bind');
18
+ const { BUILD_TASK_HANA, OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM, OUTPUT_MODE_RESULT } = require('../../build/constants');
19
19
 
20
20
  const DEBUG = cds.debug('cli');
21
21
 
@@ -73,13 +73,13 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
73
73
  vcapEnv.TARGET_CONTAINER = process.env.TARGET_CONTAINER;
74
74
  }
75
75
 
76
- for (const { task } of buildResults) {
76
+ for (const { task, result } of buildResults) {
77
77
  if (dry) {
78
- const model = await cds.load(task.src);
79
- const data = cds.compile.to.hdbtable(model);
80
- for (const [src, { file }] of data) {
81
- console.log(`-- ${file}`);
82
- console.log(src);
78
+ const sortedResult = Array.from(result.hana).sort();
79
+ for (const file of sortedResult) {
80
+ const srcPath = path.join(result.dest, file);
81
+ console.log(`-- ${path.relative(cds.root, srcPath)}`);
82
+ console.log(await read(srcPath));
83
83
  console.log();
84
84
  }
85
85
  } else {
@@ -221,7 +221,9 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
221
221
 
222
222
  async _build() {
223
223
  const buildTaskOptions = {
224
- for: buildConstants.BUILD_TASK_HANA
224
+ for: BUILD_TASK_HANA,
225
+ src: cds.env.folders.db,
226
+ [OUTPUT_MODE]: OUTPUT_MODE_FILESYSTEM | OUTPUT_MODE_RESULT
225
227
  }
226
228
 
227
229
  console.log(`Running build`);
@@ -59,7 +59,15 @@ module.exports = new class HdiDeployUtil {
59
59
  Add it either to ${bold('devDependencies')} using 'npm install -D ${HDI_DEPLOY_LIB}' or install it globally using 'npm install -g ${HDI_DEPLOY_LIB}'.`);
60
60
  }
61
61
 
62
- console.log(`Using HDI deployer from ${libPath}`)
62
+ console.log(`HDI deployer path: ${libPath}`)
63
+
64
+ const libRoot = path.dirname(libPath);
65
+ try {
66
+ const libPackageJson = require(path.join(libRoot, 'package.json'));
67
+ console.log(`HDI deployer version: ${libPackageJson.version}`);
68
+ } catch {
69
+ // ignore
70
+ }
63
71
 
64
72
  // let any error go through and abort deploy
65
73
  return require(libPath);
@@ -5,7 +5,7 @@ module.exports = {
5
5
  COMMAND_ADD: 'add',
6
6
 
7
7
  JAVA_LTS_VERSIONS: [17, 21],
8
- MAVEN_ARCHETYPE_VERSION: '3.0.0',
8
+ MAVEN_ARCHETYPE_VERSION: '3.0.1',
9
9
 
10
10
  OPTIONS: Object.freeze({
11
11
  COMPLETION: 'completion',
@@ -57,7 +57,10 @@ module.exports = {
57
57
  LINT_DEV: 'lint:dev',
58
58
  PIPELINE: 'pipeline',
59
59
  TYPER: 'typer',
60
- TYPESCRIPT: 'typescript'
60
+ TYPESCRIPT: 'typescript',
61
+ EVENT_QUEUE: 'event-queue',
62
+ COV2AP: 'cov2ap',
63
+ WEBSOCKET: 'websocket'
61
64
  }),
62
65
 
63
66
  REGEX_JAVA_PACKAGE: /^[a-z_][a-z0-9_]*(\.[a-z_][a-z0-9_]*)*$/g,
@@ -69,7 +72,10 @@ module.exports = {
69
72
  SAMPLES: 'https://github.com/SAP-samples/cloud-cap-samples',
70
73
  CAPIRE: 'https://cap.cloud.sap',
71
74
  MAVEN_ARCHETYPE_HELP: `https://cap.cloud.sap/docs/java/developing-applications/building#the-maven-archetype`,
72
- MAVEN_INSTALL_HELP: `https://maven.apache.org/install.html`
75
+ MAVEN_INSTALL_HELP: `https://maven.apache.org/install.html`,
76
+ CAP_JS_COMMUNITY_ODATA_V2: 'https://github.com/cap-js-community/odata-v2-adapter',
77
+ CAP_JS_COMMUNITY_EVENT_QUEUE: 'https://github.com/cap-js-community/event-queue',
78
+ CAP_JS_COMMUNITY_WEBSOCKET: 'https://github.com/cap-js-community/websocket'
73
79
  }),
74
80
 
75
81
  PROJECT_FILES: ['package.json', 'pom.xml', '.cdsrc.json']
package/lib/init/index.js CHANGED
@@ -22,14 +22,16 @@ module.exports = class CDSGenerator {
22
22
  }
23
23
 
24
24
  static help({ exclude = [] } = {}) {
25
+ if (!cds.add) return // use case shell completion: cds.add is undefined and not needed
26
+
25
27
  const plugins = this.readPlugins(exclude)
26
28
  const nameFixedLength = Math.max(...plugins.map(plugin => plugin.name.length))
27
29
  return plugins
30
+ .filter(({module}) => module.help())
28
31
  .map(({name, module}) => {
29
- const help = module.help?.() ?? ''
30
- return ` *${name}*${' '.repeat(nameFixedLength - name.length)} - ${help}`
32
+ return ` *${name}*${' '.repeat(nameFixedLength - name.length)} - ${module.help()}`
31
33
  })
32
- .join('\n\n')
34
+ .join('\n')
33
35
  }
34
36
 
35
37
  /**
@@ -176,7 +178,7 @@ module.exports = class CDSGenerator {
176
178
  const fuzzySearch = require('../../bin/util/fuzzySearch')
177
179
  const [bestMatch] = fuzzySearch(plugin, entries.map(e => e.name))
178
180
  const max = entries.reduce((max, {name}) => Math.max(max, name.length), 0)
179
- const allFacetsText = entries.map(({name, module}) => {
181
+ const allFacetsText = entries.filter(({module})=> module.help()).map(({name, module}) => {
180
182
  const help = module.help?.() ?? ''
181
183
  return `${term.bold(name) + ' '.repeat(max - name.length)} ${term.dim(help)}`
182
184
  }).join('\n')
@@ -4,6 +4,7 @@ const { fs, exists, isdir, path } = cds.utils
4
4
  const { parseXml } = require('./xml')
5
5
  const { REGEX_JAVA_VERSION, JAVA_LTS_VERSIONS } = require('./constants')
6
6
  const DEBUG = cds.debug('cli')
7
+ const TRACE = cds.debug('trace')
7
8
 
8
9
  module.exports = new class ProjectReader {
9
10
 
@@ -88,7 +89,7 @@ module.exports = new class ProjectReader {
88
89
  * @property {boolean} hasAttachments Indicates if the project uses SAP BTP Object Store Service.
89
90
  * @property {boolean} hasApprouter Indicates if the project uses the SAP Application Router.
90
91
  * @property {boolean} hasHtml5Repo Indicates if the project uses the SAP BTP HTML5 Application Repository.
91
- * @property {boolean} hasPortal Indicates if the project uses the SAP Cloud Portal serivce.
92
+ * @property {boolean} hasPortal Indicates if the project uses the SAP Cloud Portal service.
92
93
  * @property {boolean} hasDestination Indicates if the project uses the SAP BTP Destination service.
93
94
  * @property {boolean} hasMTXRoute Indicates if the approuter should have a multitenant route.
94
95
  * @property {boolean} hasMTXRouteJava Indicates if the approuter should have a multitenant subscription route (java).
@@ -106,7 +107,7 @@ module.exports = new class ProjectReader {
106
107
  if (!this.appName) this.#setPackageInfo()
107
108
  const env = this.env4('production')
108
109
  const { appVersion, appName, appId, appDescription, jdkVersion } = this
109
- DEBUG?.({ env })
110
+ TRACE?.({ env })
110
111
 
111
112
  const _inProd = plugin => require(`./template/${plugin}`).hasInProduction(env)
112
113
  const _ui5 = () => exists('package.json') && !!JSON.parse(fs.readFileSync(path.resolve(cds.root, 'package.json'))).sapux
@@ -187,7 +188,6 @@ module.exports = new class ProjectReader {
187
188
  })
188
189
  })()
189
190
 
190
- DEBUG?.({ project })
191
191
  return project
192
192
  }
193
193
  }
@@ -0,0 +1,7 @@
1
+ {
2
+ "cds": {
3
+ "requires": {
4
+ "audit-log": true
5
+ }
6
+ }
7
+ }
@@ -36,7 +36,7 @@ module.exports = class AuditLoggingTemplate extends require('../../plugin') {
36
36
  const { hasMta, hasHelm, hasMultitenancy, srvPath } = project
37
37
  const { plan = 'premium' } = cds.cli.options
38
38
  project.servicePlan = plan
39
-
39
+
40
40
  if (hasMultitenancy) {
41
41
  await merge(__dirname, 'files/package.sidecar.json').into('mtx/sidecar/package.json')
42
42
  }
@@ -12,6 +12,10 @@ module.exports = class CfManifestTemplate extends require('../../plugin') {
12
12
  return 'Cloud Foundry deployment using manifest files'
13
13
  }
14
14
 
15
+ static hasInProduction() {
16
+ return exists(MANIFEST_FILE)
17
+ }
18
+
15
19
  async canRun() {
16
20
  if (cds.cli.options.force) {
17
21
  return true
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "@cap-js-community/odata-v2-adapter": "^1"
4
+ }
5
+ }
@@ -0,0 +1,31 @@
1
+ const { readProject } = require('../../projectReader')
2
+ const { merge, sort } = require('../../merge')
3
+ const { link } = require('../../../util/term')
4
+ const { CAP_JS_COMMUNITY_ODATA_V2 } = require('../../constants').URLS
5
+
6
+ module.exports = class Cov2apTemplate extends require('../../plugin') {
7
+
8
+ static help() {
9
+ return 'cap-js-community: expose CDS services via the OData V2 protocol'
10
+ }
11
+
12
+ static hasInProduction() {
13
+ const { dependencies } = readProject()
14
+ return !!dependencies?.['@cap-js-community/odata-v2-adapter']
15
+ }
16
+
17
+ async canRun() {
18
+ const { isJava } = readProject()
19
+ if (isJava) throw `'cds add cov2ap' is not available for Java`
20
+ return true
21
+ }
22
+
23
+ async run() {
24
+ await merge(__dirname, 'files/dependencies.package.json').into('package.json')
25
+ await sort('package.json', 'dependencies')
26
+ }
27
+
28
+ async finalize() {
29
+ console.log(`Find plugin documentation on ${link(CAP_JS_COMMUNITY_ODATA_V2)}`)
30
+ }
31
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "@cap-js-community/event-queue": "^1"
4
+ }
5
+ }
@@ -0,0 +1,35 @@
1
+ const { readProject } = require('../../projectReader')
2
+ const { merge, sort } = require('../../merge')
3
+ const { link } = require('../../../util/term')
4
+ const { CAP_JS_COMMUNITY_EVENT_QUEUE } = require('../../constants').URLS
5
+
6
+ module.exports = class EventQueueTemplate extends require('../../plugin') {
7
+
8
+ static help() {
9
+ return 'cap-js-community: transactional event processing with automatic load distribution'
10
+ }
11
+
12
+ async canRun() {
13
+ const { isJava } = readProject()
14
+ if (isJava) throw `'cds add event-queue' is not available for Java`
15
+ return true
16
+ }
17
+
18
+ static hasInProduction() {
19
+ const { dependencies } = readProject()
20
+ return !!dependencies?.['@cap-js-community/event-queue']
21
+ }
22
+
23
+ requires() {
24
+ return ['redis']
25
+ }
26
+
27
+ async run() {
28
+ await merge(__dirname, 'files/dependencies.package.json').into('package.json')
29
+ await sort('package.json', 'dependencies')
30
+ }
31
+
32
+ async finalize() {
33
+ console.log(`Find plugin documentation on ${link(CAP_JS_COMMUNITY_EVENT_QUEUE)}`)
34
+ }
35
+ }
@@ -149,6 +149,7 @@ module.exports = class HttpTemplate extends require('../../plugin') {
149
149
  }
150
150
  varsDefined = true
151
151
  const headers = auth ? `Content-Type: application/json\n{{auth}}` : `Content-Type: application/json`
152
+ const headerAuth = auth ? '{{auth}}' : ''
152
153
 
153
154
  service.urlPaths.forEach((urlPath) => {
154
155
  if (service.urlPaths.length > 1) {
@@ -156,36 +157,41 @@ module.exports = class HttpTemplate extends require('../../plugin') {
156
157
  }
157
158
 
158
159
  service.entities.forEach((e) => {
159
- const entityNameSimple = e.name.slice(e.name.lastIndexOf('.') + 1)
160
+ const entityNameSimple = e.name.slice(serviceName.length + 1)
161
+ const nameSlug = slug(entityNameSimple)
160
162
  const url = `{{server}}/${urlPath}${entityNameSimple}`
161
- const requestTitle = `\n### ${e.name}`
162
- requests.push(`${requestTitle}\nGET ${url}\n${headers}\n`)
163
+ const requestTitle = `\n### ${entityNameSimple}`
164
+ requests.push(`${requestTitle}\n# @name ${nameSlug}_GET\nGET ${url}\n${headerAuth}\n`)
163
165
  if (e.readOnly) return
164
166
 
165
167
  const entityData = payload[`${e.name}`] ? payload[`${e.name}`][0] : null
166
168
  if (!entityData) return
167
- const postBody = JSON.stringify(entityData, null, 2)
168
-
169
- let id
170
- const compositeKey = Object.keys(e.keys).length > 1
171
- if (compositeKey) {
172
- id = e.keys.map(k => k.type === 'cds.String' ? `${k.name}='${entityData[k.name]}'` : `${k.name}=${entityData[k.name]}`).join(',')
173
- } else {
174
- id = entityData[Object.keys(e.keys)[0]]
175
- }
176
169
 
177
170
  if (e.isDraft) {
178
- generateDraftRequests(requests, requestTitle, url, headers, postBody, id)
179
- } else {
171
+ const key = Object.keys(e.keys)[0] // draft keys are always single
172
+ delete entityData[key] // draft keys should always by auto generated (either UUID or through custom logic), so remove them from the payload
173
+ const postBody = JSON.stringify(entityData, null, 2)
174
+ generateDraftRequests(requests, requestTitle, url, headers, headerAuth, postBody, key)
175
+ }
176
+ else {
177
+ const postBody = JSON.stringify(entityData, null, 2)
178
+ let id
179
+ const compositeKey = Object.keys(e.keys).length > 1
180
+ if (compositeKey) {
181
+ id = e.keys.map(k => k.type === 'cds.String' ? `${k.name}='${entityData[k.name]}'` : `${k.name}=${entityData[k.name]}`).join(',')
182
+ } else {
183
+ id = entityData[Object.keys(e.keys)[0]]
184
+ }
180
185
  compositeKey ? id = `(${id})` : id = `/${id}`
181
- requests.push(`${requestTitle}\nPOST ${url}\n${headers}\n\n${postBody}\n`)
182
- requests.push(`${requestTitle}\nPATCH ${url}${id}\n${headers}\n\n${postBody}\n`)
183
- requests.push(`${requestTitle}\nDELETE ${url}${id}\n${headers}\n`)
186
+ requests.push(`${requestTitle}\n# @name ${nameSlug}_POST\nPOST ${url}\n${headers}\n\n${postBody}\n`)
187
+ requests.push(`${requestTitle}\n# @name ${nameSlug}_PATCH\nPATCH ${url}${id}\n${headers}\n\n${postBody}\n`)
188
+ requests.push(`${requestTitle}\n# @name ${nameSlug}_DELETE\nDELETE ${url}${id}\n${headers}\n`)
184
189
  }
185
190
  })
186
191
 
187
192
  service.actions.forEach((a) => {
188
- const url = `{{server}}/${urlPath}${a.name.split(".")[1]}`
193
+ const simpleNameAction = a.name.slice(serviceName.length + 1)
194
+ const url = `{{server}}/${urlPath}${simpleNameAction}`
189
195
 
190
196
  let actionPayload = {}
191
197
  if (a.params) {
@@ -197,20 +203,25 @@ module.exports = class HttpTemplate extends require('../../plugin') {
197
203
  : null
198
204
  }
199
205
  }
200
- requests.push(`\n### ${a.name}\nPOST ${url}\n${headers}\n\n${JSON.stringify(actionPayload, null, 2)}\n`)
206
+ requests.push(`\n### ${simpleNameAction}\n# @name ${simpleNameAction}_POST\nPOST ${url}\n${headers}\n\n${JSON.stringify(actionPayload, null, 2)}\n`)
201
207
  })
202
208
 
203
209
  })
204
210
  this.#write(requests, serviceName)
205
211
  }
206
212
 
207
- function generateDraftRequests(requests, requestTitle, url, headers, postBody, id) {
208
- requests.push(`${requestTitle} Drafts \nGET ${url}?$filter=(IsActiveEntity eq false)\n${headers}\n`) // unencoded URL for better readability (client can handle this)
209
- requests.push(`${requestTitle} Drafts \nPOST ${url}\n${headers}\n\n${postBody}\n`)
210
- requests.push(`\n### Please paste the server-generated draft id here e.g. ID=101 or ID=101,secodKey='stringValue' \n@draftID = ${id}\n`)
211
- requests.push(`${requestTitle} Patch Draft \nPATCH ${url}({{draftID}},IsActiveEntity=false)\n${headers}\n\n${postBody}\n`)
212
- requests.push(`${requestTitle} Prepare Draft \nPOST ${url}({{draftID}},IsActiveEntity=false)/AdminService.draftPrepare\n${headers}\n\n{}\n`)
213
- requests.push(`${requestTitle} Activate Draft \nPOST ${url}({{draftID}},IsActiveEntity=false)/AdminService.draftActivate\n${headers}\n\n{}\n`)
213
+ function generateDraftRequests(requests, title, url, headers, headerAuth, postBody, key) {
214
+ const nameDraftsCreate = slug(title+'_Draft_POST')
215
+ requests.push(`${title} Drafts GET\n# @name ${slug(title+'_Drafts_GET')}\nGET ${url}?$filter=(IsActiveEntity eq false)\n${headerAuth}\n`) // unencoded URL for better readability (client can handle this)
216
+ requests.push(`${title} Draft POST\n# @name ${nameDraftsCreate}\nPOST ${url}\n${headers}\n\n${postBody}\n`)
217
+ requests.push(`\n### Result from POST request above\n@draftID = {{${nameDraftsCreate}.response.body.$.${key}}}\n`)
218
+ requests.push(`${title} Draft PATCH\n# @name ${slug(title+'_Draft_Patch')}\nPATCH ${url}(${key}={{draftID}},IsActiveEntity=false)\n${headers}\n\n${postBody}\n`)
219
+ requests.push(`${title} Draft Prepare\n# @name ${slug(title+'_Draft_Prepare')}\nPOST ${url}(${key}={{draftID}},IsActiveEntity=false)/AdminService.draftPrepare\n${headers}\n\n{}\n`)
220
+ requests.push(`${title} Draft Activate\n# @name ${slug(title+'_Draft_Activate')}\nPOST ${url}(${key}={{draftID}},IsActiveEntity=false)/AdminService.draftActivate\n${headers}\n\n{}\n`)
221
+ }
222
+
223
+ function slug(str) {
224
+ return str.trim().replace(/[#\\. ]/g, '')
214
225
  }
215
226
  }
216
227
 
@@ -248,5 +259,3 @@ module.exports = class HttpTemplate extends require('../../plugin') {
248
259
  return `${outputDir}/${serviceName}.http`
249
260
  }
250
261
  }
251
-
252
-
@@ -55,7 +55,6 @@
55
55
  "": {
56
56
  "dataSource": "AdminService",
57
57
  "settings": {
58
- "synchronizationMode": "None",
59
58
  "operationMode": "Server",
60
59
  "autoExpandSelect": true,
61
60
  "earlyRequests": true,
@@ -71,7 +71,6 @@
71
71
  "": {
72
72
  "dataSource": "CatalogService",
73
73
  "settings": {
74
- "synchronizationMode": "None",
75
74
  "operationMode": "Server",
76
75
  "autoExpandSelect": true,
77
76
  "earlyRequests": true,
@@ -1,12 +1,17 @@
1
- const cds = require('@sap/cds/lib')
1
+ const cds = require('@sap/cds')
2
2
 
3
3
  module.exports = class AdminService extends cds.ApplicationService { init(){
4
- this.before ('NEW','Books.drafts', genid)
4
+
5
+ const { Books } = this.entities
6
+
7
+ /**
8
+ * Generate IDs for new Books drafts
9
+ */
10
+ this.before ('NEW', Books.drafts, async (req) => {
11
+ if (req.data.ID) return
12
+ const { ID:id1 } = await SELECT.one.from(Books).columns('max(ID) as ID')
13
+ const { ID:id2 } = await SELECT.one.from(Books.drafts).columns('max(ID) as ID')
14
+ req.data.ID = Math.max(id1||0, id2||0) + 1
15
+ })
5
16
  return super.init()
6
17
  }}
7
-
8
- /** Generate primary keys for target entity in request */
9
- async function genid (req) {
10
- const {ID} = await cds.tx(req).run (SELECT.one.from(req.target.actives).columns('max(ID) as ID'))
11
- req.data.ID = ID - ID % 100 + 100 + 1
12
- }
@@ -1,26 +1,38 @@
1
1
  const cds = require('@sap/cds')
2
- const { Books } = cds.entities ('sap.capire.bookshop')
3
2
 
4
- class CatalogService extends cds.ApplicationService { init(){
3
+ class CatalogService extends cds.ApplicationService { init() {
5
4
 
6
- // Reduce stock of ordered books if available stock suffices
7
- this.on ('submitOrder', async req => {
8
- const {book,quantity} = req.data
9
- let {stock} = await SELECT `stock` .from (Books,book)
10
- if (stock >= quantity) {
11
- await UPDATE (Books,book) .with (`stock -=`, quantity)
12
- await this.emit ('OrderedBook', { book, quantity, buyer:req.user.id })
13
- return { stock }
14
- }
15
- else return req.error (409,`${quantity} exceeds stock for book #${book}`)
16
- })
5
+ const { Books } = cds.entities('sap.capire.bookshop')
6
+ const { ListOfBooks } = this.entities
17
7
 
18
8
  // Add some discount for overstocked books
19
- this.after ('each','ListOfBooks', book => {
9
+ this.after('each', ListOfBooks, book => {
20
10
  if (book.stock > 111) book.title += ` -- 11% discount!`
21
11
  })
22
12
 
13
+ // Reduce stock of ordered books if available stock suffices
14
+ this.on('submitOrder', async req => {
15
+ let { book:id, quantity } = req.data
16
+ let book = await SELECT.from (Books, id, b => b.stock)
17
+
18
+ // Validate input data
19
+ if (!book) return req.error (404, `Book #${id} doesn't exist`)
20
+ if (quantity < 1) return req.error (400, `quantity has to be 1 or more`)
21
+ if (quantity > book.stock) return req.error (409, `${quantity} exceeds stock for book #${id}`)
22
+
23
+ // Reduce stock in database and return updated stock value
24
+ await UPDATE (Books, id) .with ({ stock: book.stock -= quantity })
25
+ return book
26
+ })
27
+
28
+ // Emit event when an order has been submitted
29
+ this.after('submitOrder', async (_,req) => {
30
+ let { book, quantity } = req.data
31
+ await this.emit('OrderedBook', { book, quantity, buyer: req.user.id })
32
+ })
33
+
34
+ // Delegate requests to the underlying generic service
23
35
  return super.init()
24
36
  }}
25
37
 
26
- module.exports = { CatalogService }
38
+ module.exports = CatalogService
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "devDependencies": {
3
- "typescript": "^5",
4
- "@cap-js/cds-types": "<1",
5
- "@types/node": "^20"
3
+ "@cap-js/cds-types": "^0.6",
4
+ "@types/node": "^20",
5
+ "typescript": "^5"
6
6
  },
7
7
  "scripts": {
8
8
  "watch": "cds-ts w",
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "@cap-js-community/websocket": "^1"
4
+ }
5
+ }
@@ -0,0 +1,35 @@
1
+ const { readProject } = require('../../projectReader')
2
+ const { merge, sort } = require('../../merge')
3
+ const { link } = require('../../../util/term')
4
+ const { CAP_JS_COMMUNITY_WEBSOCKET } = require('../../constants').URLS
5
+
6
+ module.exports = class WebsocketTemplate extends require('../../plugin') {
7
+
8
+ static help() {
9
+ return 'cap-js-community: expose CDS services via the Websocket protocol'
10
+ }
11
+
12
+ async canRun() {
13
+ const { isJava } = readProject()
14
+ if (isJava) throw `'cds add websocket' is not available for Java`
15
+ return true
16
+ }
17
+
18
+ requires() {
19
+ return ['redis']
20
+ }
21
+
22
+ static hasInProduction() {
23
+ const { dependencies } = readProject()
24
+ return !!dependencies?.['@cap-js-community/websocket']
25
+ }
26
+
27
+ async run() {
28
+ await merge(__dirname, 'files/dependencies.package.json').into('package.json')
29
+ await sort('package.json', 'dependencies')
30
+ }
31
+
32
+ async finalize() {
33
+ console.log(`Find plugin documentation on ${link(CAP_JS_COMMUNITY_WEBSOCKET)}`)
34
+ }
35
+ }
@@ -1,7 +1,4 @@
1
1
  module.exports = class extends require('../../plugin') {
2
- help() {
3
- return 'SAP BTP Work Zone'
4
- }
5
2
 
6
3
  requires() {
7
4
  return ['workzone-standard']
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "8.0.3",
3
+ "version": "8.1.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@sap/cds-dk",
9
- "version": "8.0.3",
9
+ "version": "8.1.0",
10
10
  "license": "SEE LICENSE IN LICENSE",
11
11
  "dependencies": {
12
12
  "@cap-js/asyncapi": "^1.0.0",
@@ -42,9 +42,12 @@
42
42
  }
43
43
  },
44
44
  "node_modules/@cap-js/db-service": {
45
- "version": "1.11.0",
45
+ "version": "1.12.0",
46
46
  "license": "SEE LICENSE",
47
47
  "optional": true,
48
+ "dependencies": {
49
+ "generic-pool": "^3.9.0"
50
+ },
48
51
  "peerDependencies": {
49
52
  "@sap/cds": ">=7.9"
50
53
  }
@@ -102,7 +105,7 @@
102
105
  }
103
106
  },
104
107
  "node_modules/@eslint/config-array": {
105
- "version": "0.17.0",
108
+ "version": "0.17.1",
106
109
  "license": "Apache-2.0",
107
110
  "dependencies": {
108
111
  "@eslint/object-schema": "^2.1.4",
@@ -135,7 +138,7 @@
135
138
  }
136
139
  },
137
140
  "node_modules/@eslint/js": {
138
- "version": "9.7.0",
141
+ "version": "9.8.0",
139
142
  "license": "MIT",
140
143
  "engines": {
141
144
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -200,7 +203,7 @@
200
203
  }
201
204
  },
202
205
  "node_modules/@sap/cds": {
203
- "version": "8.0.4",
206
+ "version": "8.1.0",
204
207
  "license": "SEE LICENSE IN LICENSE",
205
208
  "dependencies": {
206
209
  "@sap/cds-compiler": ">=5",
@@ -226,7 +229,7 @@
226
229
  }
227
230
  },
228
231
  "node_modules/@sap/cds-compiler": {
229
- "version": "5.0.6",
232
+ "version": "5.1.0",
230
233
  "license": "SEE LICENSE IN LICENSE",
231
234
  "dependencies": {
232
235
  "antlr4": "4.9.3"
@@ -866,14 +869,14 @@
866
869
  }
867
870
  },
868
871
  "node_modules/eslint": {
869
- "version": "9.7.0",
872
+ "version": "9.8.0",
870
873
  "license": "MIT",
871
874
  "dependencies": {
872
875
  "@eslint-community/eslint-utils": "^4.2.0",
873
876
  "@eslint-community/regexpp": "^4.11.0",
874
- "@eslint/config-array": "^0.17.0",
877
+ "@eslint/config-array": "^0.17.1",
875
878
  "@eslint/eslintrc": "^3.1.0",
876
- "@eslint/js": "9.7.0",
879
+ "@eslint/js": "9.8.0",
877
880
  "@humanwhocodes/module-importer": "^1.0.1",
878
881
  "@humanwhocodes/retry": "^0.3.0",
879
882
  "@nodelib/fs.walk": "^1.2.8",
@@ -2422,7 +2425,7 @@
2422
2425
  }
2423
2426
  },
2424
2427
  "node_modules/yaml": {
2425
- "version": "2.4.5",
2428
+ "version": "2.5.0",
2426
2429
  "license": "ISC",
2427
2430
  "bin": {
2428
2431
  "yaml": "bin.mjs"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "8.0.3",
3
+ "version": "8.1.0",
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": [