@sap/cds-dk 8.4.0 → 8.4.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 CHANGED
@@ -7,10 +7,44 @@ 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.4.2 - 2024-11-19
11
+
12
+ ### Changed
13
+
14
+ - `cds init` uses latest Maven Java archetype version 3.4.1 for creating Java projects.
15
+
16
+ ### Fixed
17
+
18
+ - `cds add -p/--package` correctly parse plugin-contributed options.
19
+ - `cds add side-by-side-extensibility` does not throw an error.
20
+ - fix method name in `cds bind` credential handling.
21
+ - `cds add --help` no longer has missing line breaks in help text.
22
+ - `cds` commands no longer fail with an error `cds.extend is not a function` with very old versions of `@sap/cds`.
23
+ - `cds add hana` adds the `requires.db` entry to `.cdsrc.json` for Java projects.
24
+ - `cds add data` creates decimal values with correct scale `0` if only precision is given, like in `Decimal(15)`.
25
+
26
+
27
+ ## Version 8.4.1 - 2024-11-08
28
+
29
+ ### Added
30
+
31
+ - `cds deploy --no-build` lets you skip the implicit `cds build`.
32
+ - `cds add data/http` supports new type `cds.Map`.
33
+
34
+ ### Fixed
35
+
36
+ - `cds compile --to <unknown>` shows a cleaner error message.
37
+ - `cds add data` handles composition of one correctly.
38
+ - `cds add http` creates payloads for unbound actions in services.
39
+ - `cds compile --to xsuaa` can now handle annotation expressions in `where` clauses.
40
+ - `cds add http` produces requests for all expected data and no longer relies on existing data on the side.
41
+ - `cds add typer` creates `tsconfig.json` that works with `cds-tsx`.
42
+
10
43
  ## Version 8.4.0 - 2024-10-29
11
44
 
12
45
  ### Added
13
46
 
47
+ - `cds add application-logging` for Java also executes the `cds:add` goal with feature `APPLICATION_LOGGING`.
14
48
  - Added support for `import` and `export` for auto-completion in `package.json` and `.cdsrc.json`.
15
49
  - `cds add side-by-side-extensibility` adds configuration for logic extensibility via extension points (Java).
16
50
  - `cds init --java` can be used as an alias to `cds init --add java`.
package/bin/add.js CHANGED
@@ -1,7 +1,6 @@
1
1
  const cds = require('../lib/cds'), { fs } = cds.utils
2
2
  const DEBUG = cds.debug('add')
3
3
  const { join } = require('path')
4
- const cmd = require('../lib/util/command')
5
4
 
6
5
  const fromDk = fs.readdirSync(join(__dirname, '../lib/init/template'))
7
6
  const fromPlugins = require('../lib/init/add').registered
@@ -60,7 +59,7 @@ ${Object.entries(conf).sort(([a], [b]) => a.localeCompare(b)).flatMap(([pluginNa
60
59
  const text4 = arr => arr.map(({ key, help, short }) => help && ` ${key}${short ? ' | '+short : ''}\n\n ${help}`).join('\n\n')
61
60
  const optionsText = text4(options), flagsText = text4(flags)
62
61
  if (!optionsText && !flagsText) return []
63
- return ` *cds add ${pluginName}*\n\n${optionsText}${flagsText}`
62
+ return ` *cds add ${pluginName}*${optionsText?`\n\n${optionsText}`:''}${flagsText?`\n\n${flagsText}`:''}`
64
63
  }).join('\n\n\n')}
65
64
 
66
65
 
@@ -100,17 +99,8 @@ async function handleCompletion(currentWord, previousWord, argv, util) {
100
99
  }
101
100
 
102
101
  async function add(args) {
103
- // await cds.plugins
104
102
  const CDSGenerator = require('../lib/init')
105
103
  const generator = new CDSGenerator()
106
104
  const features = args.map(f => f.split(/[,\s]/)).flat()
107
- if (cds.cli.options?.package) {
108
- console.log(`running with --package, adding dependency ${cds.cli.options.package}`)
109
- await cmd.spawnCommand(`npm`, ['add', cds.cli.options.package], { cwd: cds.root })
110
- await cmd.spawnCommand(`cds`, ['add', ...features], { cwd: cds.root }) // plugins need to reload
111
- // await cds.plugins
112
- // await generator.add(features)
113
- } else {
114
- await generator.add(features)
115
- }
105
+ await generator.add(features)
116
106
  }
package/bin/cds.js CHANGED
@@ -62,6 +62,11 @@ const cli = {
62
62
  // REVISIT: Have to load plugins to allow for plugin-contributed flags!
63
63
  if (cmd === 'add') {
64
64
  cds.cli = { command: 'add' }
65
+ if (argv.includes('-p') || argv.includes('--package')) {
66
+ const p = argv[argv.indexOf('-p') + 1] || argv[argv.indexOf('--package') + 1]
67
+ console.log(`running with --package, adding dependency ${p}`)
68
+ await require('../lib/util/command').spawnCommand(`npm`, ['add', p], { cwd: cds.root })
69
+ }
65
70
  await cds.plugins
66
71
  for (const [, Plugin] of cds.add.registered) {
67
72
  const all = Object.entries((new Plugin).options())
@@ -238,6 +238,12 @@ async function compile (models, options={}) {
238
238
  if (/mta.*\.ya?ml/.test(models[0])) {
239
239
  model = Promise.resolve(models[0])
240
240
  } else {
241
+ for (const kind of ['to', 'for']) {
242
+ const tail = options[kind]
243
+ if (typeof tail === 'string' && !cds.compile[kind][tail]) {
244
+ throw `Unknown model processor: cds.compile.${kind}.${tail}`
245
+ }
246
+ }
241
247
  model = cds.load (models, options)
242
248
  src = models[0] .replace (/\.[^.]+$/,'') //> excluding source file extension, e.g. .cds
243
249
  }
@@ -305,7 +311,6 @@ async function compile (models, options={}) {
305
311
  let p = cds.compile[kind]
306
312
  for (let each of tail.split('.')) {
307
313
  p = p[each]
308
- if (!p) throw `Unknown model processor: cds.compile.${kind}.${tail}`
309
314
  }
310
315
  return p
311
316
  }
@@ -1,6 +1,6 @@
1
1
  module.exports = Object.assign(deploy, {
2
2
  options: ['--to', '--for', '--out', '--tunnel-address', '--vcap-file', '--delta-from' ],
3
- flags: ['--no-save', '--auto-undeploy', '--dry', '--xdry', '--with-mocks', '--store-credentials', '--model-only', '--script', '--with-auto-schema-evolution' ],
3
+ flags: ['--no-save', '--auto-undeploy', '--dry', '--xdry', '--with-mocks', '--store-credentials', '--no-build', '--model-only', '--script', '--with-auto-schema-evolution' ],
4
4
  shortcuts: ['-2', '-4', '-o'],
5
5
  help: `
6
6
  # SYNOPSIS
@@ -37,6 +37,10 @@ module.exports = Object.assign(deploy, {
37
37
 
38
38
  Just print the SQL statements without executing them.
39
39
 
40
+ *--no-build* (only in combination with *--to hana*)
41
+
42
+ Skip running *cds build*.
43
+
40
44
  *--out* | *-o* <file>
41
45
 
42
46
  Write the DDL statements to the given file.
@@ -97,6 +101,7 @@ module.exports = Object.assign(deploy, {
97
101
  cds deploy --to sqlite:db
98
102
  cds deploy --to hana
99
103
  cds deploy --to hana:myService --auto-undeploy
104
+ cds deploy --to hana --no-build
100
105
 
101
106
  `})
102
107
 
package/lib/bind/index.js CHANGED
@@ -118,7 +118,7 @@ function mergeCredentials(credentials, customCredentials) {
118
118
  if (!credentials[key]) {
119
119
  credentials[key] = {};
120
120
  }
121
- credentials[key] = this._mergeCredentials(credentials[key], customCredentials[key]);
121
+ credentials[key] = this.mergeCredentials(credentials[key], customCredentials[key]);
122
122
  } else {
123
123
  credentials[key] = customCredentials[key];
124
124
  }
@@ -56,7 +56,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
56
56
  }
57
57
  // copy native hana artifacts from workspaces
58
58
  if (workspaceFiles) {
59
- const dest = path.join(this.task.dest, 'src/gen')
59
+ const dest = path.join(this.task.dest, this.isStagingBuild() ? 'src' : 'src/gen')
60
60
  await Promise.all(
61
61
  workspaceFiles.map((src) => {
62
62
  const { dir, filePath } = src
@@ -7,44 +7,57 @@ const hardcoded = {
7
7
  Attributes: ['id', 'tenant', 'ext_attr']
8
8
  }
9
9
 
10
-
11
10
  module.exports = function compile_to_xsuaa (model, options) {
12
11
 
13
12
  const base = getBaseModel(options)
14
13
  const scopes = {}
15
14
  const attributes = {}
16
15
 
16
+ const findUserAttrInExpression = (expr, attributes = []) => {
17
+ // find `$user` ref, but not e.g. `$self.$user`
18
+ if (typeof expr === 'object' && expr.ref?.length && expr.ref[0] === '$user') {
19
+ if (expr.ref.length === 2) {
20
+ const attr = expr.ref[1]
21
+ if (!/^[a-zA-Z0-9_]*$/.test(attr)) {
22
+ // see https://help.sap.com/docs/CP_AUTHORIZ_TRUST_MNG/ae8e8427ecdf407790d96dad93b5f723/517895a9612241259d6941dbf9ad81cb.html#attributes
23
+ throw new Error(`Illegal attribute name '${attr}' in '${expr}'. Only characters a-z, A-Z, 0-9, _ are allowed.`)
24
+ }
25
+ if (!hardcoded.Attributes.includes(attr)) attributes.push(attr)
26
+ }
27
+ // Reject expressions like `$user.foo.bar`, as xsuaa attributes are primitive, not structured
28
+ else if (expr.ref.length > 2) {
29
+ throw new Error (`Structured attributes like '${expr.ref.join('.')}' are not supported`)
30
+ }
31
+ }
32
+ else if (typeof expr === 'object') { // descend sub expressions etc.
33
+ Object.values(expr).forEach(e => findUserAttrInExpression(e, attributes))
34
+ }
35
+ else if (Array.isArray(expr)) { // descend arrays
36
+ expr.forEach(e => findUserAttrInExpression(e, attributes))
37
+ }
38
+ return attributes
39
+ }
40
+
41
+ /**
42
+ * @param {string|object} condition - the condition object or string
43
+ * @returns string[] - the attributes found in the condition
44
+ */
17
45
  const parseAttributes = (condition) => { // e.g. 'foo = $user.bar or baz = $user.boo'
18
46
  if (!condition) return []
19
47
 
20
- // first validate basic syntax of expression by parsing it
21
48
  let expr
22
- try {
23
- expr = cds.parse.expr(condition).xpr
24
- } catch (err) {
25
- throw new Error(`Invalid expression': ${err.message}`)
26
- }
27
-
28
- // Reject expressions like `$user.foo.bar`, as xsuaa attributes are primitive, not structured
29
- expr.forEach(expr => {
30
- const userIdx = expr.ref?.indexOf('$user')
31
- if (userIdx >= 0 && expr.ref.length > userIdx+2) {
32
- throw new Error (`Structured attributes like '${expr.ref.join('.')}' are not supported`)
49
+ if (typeof condition === 'object') {
50
+ if (!condition.xpr && !condition['=']) throw new Error(`Invalid expression: ${JSON.stringify(condition)}`)
51
+ expr = condition.xpr
52
+ } else {
53
+ try {
54
+ expr = cds.parse.expr(condition).xpr
55
+ } catch (err) {
56
+ throw new Error(`Invalid expression': ${err.message}`, { cause: err })
33
57
  }
34
- })
58
+ }
35
59
 
36
- // Reject expressions like `$user.foo-bar`
37
- // Impl. note: use the expression string rather than the parsed expression as the above example would be
38
- // `[ { ref: [ '$user', 'foo' ] }, '-', { ref: [ 'bar' ] } ]`
39
- // making it ambiguous to tell if it was `$user.foo-bar` or `$user.foo - bar`
40
- return Array.from(condition.matchAll(/\$user\s*\.\s*(\S*)/g))
41
- .map(m => m[1])
42
- .filter(m => !hardcoded.Attributes.includes(m))
43
- .map(m => {
44
- // see https://help.sap.com/docs/CP_AUTHORIZ_TRUST_MNG/ae8e8427ecdf407790d96dad93b5f723/517895a9612241259d6941dbf9ad81cb.html#attributes
45
- if (!/^[a-zA-Z0-9_]*$/.test(m)) throw new Error(`Illegal attribute name '${m}' in '${condition}'. Only characters a-z, A-Z, 0-9, _ are allowed.`)
46
- return m
47
- })
60
+ return findUserAttrInExpression(expr)
48
61
  }
49
62
 
50
63
  cds.reflect(model).foreach(def => {
@@ -3,7 +3,7 @@ const path = require('path');
3
3
  const fs = require('fs').promises;
4
4
 
5
5
  const cds = require('../../../lib/cds');
6
- const { exists, read } = cds.utils;
6
+ const { exists, read, write, mkdirp } = cds.utils;
7
7
 
8
8
  const { build } = require('../../build');
9
9
 
@@ -67,68 +67,73 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
67
67
  }
68
68
  }
69
69
 
70
- const buildResults = await this._build();
70
+ let buildResult
71
+ if (cds.cli.options?.['no-build']) {
72
+ console.log(`Provided flag '--no-build', skipping cds build`)
73
+ } else {
74
+ buildResult = await this._build();
75
+ }
71
76
 
72
77
  if (process.env.TARGET_CONTAINER) {
73
78
  vcapEnv.TARGET_CONTAINER = process.env.TARGET_CONTAINER;
74
79
  }
75
80
 
76
- for (const { task, result } of buildResults) {
77
- if (dry) {
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
- console.log();
84
- }
81
+ if (dry) {
82
+ const sortedResult = Array.from(buildResult.hana).sort();
83
+ for (const file of sortedResult) {
84
+ const srcPath = path.join(buildResult.dest, file);
85
+ console.log(`-- ${path.relative(cds.root, srcPath)}`);
86
+ console.log(await read(srcPath));
87
+ console.log();
88
+ }
89
+ } else {
90
+ // the primary database is always built and deployed
91
+ const dest = path.join(cds.env.build.target, cds.env.folders.db);
92
+
93
+ let serviceName = pServiceName;
94
+ let serviceKeyName;
95
+ const { instance, key } = cds.requires.db?.binding ?? {};
96
+ if (!serviceName && instance) {
97
+ serviceName = instance;
98
+ serviceKeyName = key;
99
+ }
100
+
101
+ if (undeployWhitelist) {
102
+ console.log('Writing undeploy.json');
103
+ await write(undeployWhitelist).to(dest, 'undeploy.json')
104
+ }
105
+
106
+ const hasVCAPEnv = Object.keys(vcapEnv).length > 0;
107
+ if (!serviceName && hasVCAPEnv) {
108
+ await mkdirp(dest);
85
109
  } else {
86
- const dest = task.dest;
110
+ const { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey } =
111
+ await this._getOrCreateCFService(dest, serviceName, serviceKeyName, tunnelAddress);
87
112
 
88
- let serviceName = pServiceName;
89
- let serviceKeyName;
90
- if (!serviceName && cds.env.requires.db?.binding?.instance) {
91
- serviceName = cds.env.requires.db.binding.instance;
92
- serviceKeyName = cds.env.requires.db.binding.key;
113
+ serviceName ??= cfServiceInstanceName;
114
+ serviceKeyName ??= cfServiceInstanceKeyName
115
+ if (!vcapEnv.VCAP_SERVICES || serviceName) {
116
+ vcapEnv.VCAP_SERVICES = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
93
117
  }
94
118
 
95
- if (undeployWhitelist) {
96
- console.log('Writing undeploy.json');
97
- await fs.write(path.join(dest, 'undeploy.json'), JSON.stringify(undeployWhitelist, null, 2));
119
+ if (storeCredentials) {
120
+ await this._addInstanceToDefaultEnvJson([dest, cds.root], cfServiceInstanceName, serviceKey);
98
121
  }
122
+ }
99
123
 
100
- const hasVCAPEnv = Object.keys(vcapEnv).length > 0;
101
- if (!serviceName && hasVCAPEnv) {
102
- await fs.mkdir(dest, { recursive: true });
103
- } else {
104
- const { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey } =
105
- await this._getOrCreateCFService(dest, serviceName, serviceKeyName, tunnelAddress);
106
-
107
- serviceName ??= cfServiceInstanceName;
108
- serviceKeyName ??= cfServiceInstanceKeyName
109
- if (!vcapEnv.VCAP_SERVICES || serviceName) {
110
- vcapEnv.VCAP_SERVICES = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
111
- }
112
-
113
- if (storeCredentials) {
114
- await this._addInstanceToDefaultEnvJson([dest, cds.root], cfServiceInstanceName, serviceKey);
115
- }
116
- }
124
+ console.log(`${bold('Starting deployment to SAP HANA ...')}`);
125
+ DEBUG?.({ dest, vcapEnv, hdiOptions })
126
+ await hdi.deploy(dest, vcapEnv, hdiOptions);
117
127
 
118
- console.log(`${bold('Starting deployment to SAP HANA ...')}`);
119
- DEBUG?.({ dest, vcapEnv, hdiOptions })
120
- await hdi.deploy(dest, vcapEnv, hdiOptions);
121
-
122
- if (!hasVCAPEnv) {
123
- const bindOptions = {
124
- ...options,
125
- to: `${serviceName}:${serviceKeyName}`,
126
- for: options.for,
127
- kind: task.for
128
- }
129
- const services = await bind(bindOptions);
130
- await storeBindings(services, bindOptions);
128
+ if (!hasVCAPEnv) {
129
+ const bindOptions = {
130
+ ...options,
131
+ to: `${serviceName}:${serviceKeyName}`,
132
+ for: options.for,
133
+ kind: BUILD_TASK_HANA
131
134
  }
135
+ const services = await bind(bindOptions);
136
+ await storeBindings(services, bindOptions);
132
137
  }
133
138
 
134
139
  if (storeCredentials) {
@@ -145,7 +150,7 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
145
150
  console.warn(warn(warnings.join('\n')));
146
151
  }
147
152
 
148
- return { buildResults };
153
+ return buildResult;
149
154
  }
150
155
 
151
156
 
@@ -206,9 +211,11 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
206
211
  src: cds.env.folders.db,
207
212
  [OUTPUT_MODE]: OUTPUT_MODE_FILESYSTEM | OUTPUT_MODE_RESULT
208
213
  }
209
-
210
214
  console.log(`Running build`);
211
- return build(buildTaskOptions);
215
+
216
+ // cds build throws an error if the src folder does not exist
217
+ const buildResult = await build(buildTaskOptions);
218
+ return buildResult[0].result;
212
219
  }
213
220
 
214
221
  async _addInstanceToDefaultEnvJson(currentFolders, serviceInstanceName, serviceKey) {
package/lib/index.js CHANGED
@@ -35,6 +35,20 @@ class CdsDk {
35
35
  }
36
36
  }
37
37
 
38
+ const REQUIRED_CDS_MAJOR = 7, REQUIRED_CDS_MINOR = 2
39
+ const [major, minor] = cds.version.split('.').map(Number)
40
+ if (major < REQUIRED_CDS_MAJOR || major === REQUIRED_CDS_MAJOR && minor < REQUIRED_CDS_MINOR) {
41
+ const [dkVersion] = require('../package.json').version.split('.')
42
+
43
+ console.error(`
44
+ This application uses '@sap/cds' version ${major}.${minor}, which is not compatible with the installed '@sap/cds-dk' version ${dkVersion}.
45
+ You can either:
46
+ - update '@sap/cds' to the latest version (recommended)
47
+ - downgrade '@sap/cds-dk' to version ${REQUIRED_CDS_MAJOR}
48
+ `)
49
+ process.exit(1)
50
+ }
51
+
38
52
  cds.extend (cds.constructor) .with (CdsDk)
39
53
 
40
54
  class CdsDkAdd {
@@ -49,20 +63,6 @@ if (process.argv[2] in { add:1, init:1, help:1 }) {
49
63
  cds.extend (cds.constructor) .with (CdsDkBuild)
50
64
  }
51
65
 
52
- const REQUIRED_CDS_MAJOR = 7, REQUIRED_CDS_MINOR = 2
53
- const [major, minor] = cds.version.split('.').map(Number)
54
- if (major < REQUIRED_CDS_MAJOR || major === REQUIRED_CDS_MAJOR && minor < REQUIRED_CDS_MINOR) {
55
- const [dkVersion] = require('../package.json').version.split('.')
56
-
57
- console.error(`
58
- This application uses '@sap/cds' version ${major}.${minor}, which is not compatible with the installed '@sap/cds-dk' version ${dkVersion}.
59
- You can either:
60
- - update '@sap/cds' to the latest version (recommended)
61
- - downgrade '@sap/cds-dk' to version ${REQUIRED_CDS_MAJOR}
62
- `)
63
- process.exit(1)
64
- }
65
-
66
66
  function _local (id) {
67
67
  return require.resolve (id, {paths:[
68
68
  process.cwd(), // project-local module is preferred
@@ -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.3.1',
8
+ MAVEN_ARCHETYPE_VERSION: '3.4.1',
9
9
 
10
10
  OPTIONS: Object.freeze({
11
11
  COMPLETION: 'completion',
@@ -1,7 +1,11 @@
1
1
  {
2
+ {{#isNodejs}}
2
3
  "cds": {
4
+ {{/isNodejs}}
3
5
  "requires": {
4
- "application-logging": true
5
- }
6
+ "application-logging": true
7
+ }
8
+ {{#isNodejs}}
6
9
  }
10
+ {{/isNodejs}}
7
11
  }
@@ -1,6 +1,7 @@
1
1
  const { readProject } = require('../../projectReader')
2
2
  const { merge } = require('../../merge')
3
3
  const { srv4, mtxSidecar4, applicationLogging, approuter } = require('../../registries/mta')
4
+ const mvn = require('../../mvn')
4
5
 
5
6
  module.exports = class ApplicationLoggingTemplate extends require('../../plugin') {
6
7
 
@@ -20,10 +21,9 @@ module.exports = class ApplicationLoggingTemplate extends require('../../plugin'
20
21
 
21
22
  async run() {
22
23
  const project = readProject()
23
- const { isNodejs } = project
24
- if (isNodejs) {
25
- await merge(__dirname, 'files/package.json.hbs').into('package.json')
26
- }
24
+ const { isJava, configFile } = project
25
+ await merge(__dirname, 'files/package.json.hbs').into(configFile, { with: project })
26
+ if (isJava) await mvn.add('APPLICATION_LOGGING')
27
27
  }
28
28
 
29
29
  async combine() {
@@ -4,7 +4,7 @@
4
4
  "@sap/approuter": "^17.0.0"
5
5
  },
6
6
  "engines": {
7
- "node": "^20.0.0"
7
+ "node": "^22.0.0"
8
8
  },
9
9
  "scripts": {
10
10
  "start": "node node_modules/@sap/approuter/approuter.js"
@@ -39,7 +39,7 @@ module.exports = async (data={}, csn, { headerOnly=false, separator=',' }={}) =>
39
39
  }
40
40
  record.push(...Object.values(value))
41
41
  }
42
- else if (element.is_struct) {
42
+ else if (element.is_struct && element.type !== 'cds.Map') {
43
43
  if (i === 0) {
44
44
  header.push(...Object.keys(value).map(structField => name + '_' + structField))
45
45
  }
@@ -47,7 +47,7 @@ module.exports = async (data={}, csn, { headerOnly=false, separator=',' }={}) =>
47
47
  }
48
48
  else {
49
49
  if (i === 0) header.push(name)
50
- if (Array.isArray(value)) { // `array of` / `many`
50
+ if (Array.isArray(value) || typeof value === 'object') { // `array of`/`many` and `cds.Map`
51
51
  value = JSON.stringify(value)
52
52
  }
53
53
  value = escape(value)
@@ -10,35 +10,37 @@ const MAX_ASSOC_DEPTH = 5
10
10
  *
11
11
  * @param {Record<string, object[]>|undefined} data the data with entities and their records to be created
12
12
  * @param {object} csn the model
13
- * @param {Number} numRecords the number of records to be created for each entity
13
+ * @param {Number} numRecords the number of records to be created for each artifact
14
14
  * @param {{referenceData? : Record<string, string}} options misc. options
15
15
  * @returns {Record<string, object[]>} the filled data
16
16
  */
17
17
  const asJson = module.exports = async (data, csn, numRecords=1, options={}) => {
18
- let { referenceData, contentType } = options
18
+ let { referenceData, contentType, artifact='entity' } = options
19
19
 
20
- // if no entity restrictions are given, use all entities
21
- if (!data) data = csn.all('entity').reduce((all, e) => { all[e.name] = []; return all }, {})
20
+ // if no data is given, use all data
21
+ if (!data) {
22
+ data = csn.all(artifact).reduce((all, e) => { all[e.name] = []; return all }, {})
23
+ }
22
24
 
23
25
  // if no reference data is given, scan for it using cds.deploy.resources
24
26
  if (!referenceData) referenceData = await cds.deploy.resources(csn)
25
27
 
26
28
  // sort all entities first that are referenced by others, so that dependant entities can be created after them
27
29
  const { definitions: defs } = csn
28
- const entityNames = Object.keys(data).sort((n1, n2) => {
29
- const n1HasComp = Object.values(defs[n1].elements).some(el => el.type === 'cds.Composition')
30
- const n2HasComp = Object.values(defs[n2].elements).some(el => el.type === 'cds.Composition')
30
+ const names = Object.keys(data).sort((n1, n2) => {
31
+ const n1HasComp = Object.values(defs[n1].elements??[]).some(el => el.type === 'cds.Composition')
32
+ const n2HasComp = Object.values(defs[n2].elements??[]).some(el => el.type === 'cds.Composition')
31
33
  if (n1HasComp && !n2HasComp) return -1
32
34
  if (!n1HasComp && n2HasComp) return 1
33
35
  return 0
34
36
  })
35
37
 
36
38
  // requested entities shall always be created, so remove it from reference data
37
- Object.entries(referenceData).filter(([, name]) => entityNames.includes(name)).forEach(([file]) => delete referenceData[file])
39
+ Object.entries(referenceData).filter(([, name]) => names.includes(name)).forEach(([file]) => delete referenceData[file])
38
40
 
39
41
  addSemantics(csn)
40
42
 
41
- for (const name of entityNames) {
43
+ for (const name of names) {
42
44
  if (!data[name] // removed on purpose because records are created inline through compositions
43
45
  || data[name].length >= numRecords) { // numRecords already created by following associations
44
46
  continue
@@ -47,26 +49,26 @@ const asJson = module.exports = async (data, csn, numRecords=1, options={}) => {
47
49
  let numRecordsToGenerate // numRecords might be reduced if some records were created by following associations
48
50
  let records = []
49
51
  if (data[name].length > 0) {
50
- numRecordsToGenerate = numRecords - data[name].length
52
+ numRecordsToGenerate = numRecords - data[name].length
51
53
  records = Array(numRecordsToGenerate).fill({}).map(() => ({}))
52
54
  } else {
53
55
  numRecordsToGenerate = numRecords
54
56
  records = data[name] = Array(numRecordsToGenerate).fill({}).map(() => ({}))
55
57
  }
56
- const entity = defs[name]
58
+ const artifact = defs[name]
57
59
 
58
60
  const idNum = randomNumber()
59
61
  for (let i = 0; i < numRecordsToGenerate; i++) {
60
- fillRecord(records[i], idNum+i, entity, csn, data, null, 0, referenceData)
62
+ fillRecord(records[i], idNum+i, artifact, csn, data, null, 0, referenceData)
61
63
  }
62
- data[name] = data[name].concat(records) // append the new records to the possibly existingand referenced records
64
+ data[name] = data[name].concat(records) // append the new records to the possibly existing and referenced records
63
65
  // remove records w/ duplicate keys. This can happen if the key is an enum (and we have less enum values than requested records).
64
- data[name] = uniqRecords(data[name], entity)
66
+ data[name] = uniqRecords(data[name], artifact)
65
67
  }
66
68
  for (let [key, d] of Object.entries(data)) {
67
69
  if (contentType === 'json' && d.deleteReference && !d.keepReferece) {
68
70
  delete data[key]
69
- }
71
+ }
70
72
  delete d.keepReferece
71
73
  delete d.deleteReference
72
74
  uniqRecords(d, csn.definitions[key])
@@ -74,13 +76,13 @@ const asJson = module.exports = async (data, csn, numRecords=1, options={}) => {
74
76
  return data
75
77
  }
76
78
 
77
- function fillRecord(record, recordID, entity, csn, data, parentEntity, depth, referenceData) {
79
+ function fillRecord(record, recordID, artifact, csn, data, parent, depth, referenceData) {
78
80
  Object.defineProperty(record, '$id', {value: recordID, enumerable: false})
79
- if (data[entity.name]) data[entity.name].push(record) // add id to data, to be referenced by associations to prevent circular dependencies
81
+ if (data[artifact.name]) data[artifact.name].push(record) // add id to data, to be referenced by associations to prevent circular dependencies
80
82
 
81
- const { elements } = entity
82
- Object.values(elements).sort(asJson.elementsSorter(csn)).forEach(el => {
83
- record[el.name] = dataForField(el, entity, csn, record, data, parentEntity, depth, referenceData)
83
+ const elems = Object.values(artifact.elements ?? artifact.params).sort(asJson.elementsSorter(csn))
84
+ elems.forEach(el => {
85
+ record[el.name] = dataForField(el, artifact, csn, record, data, parent, depth, referenceData)
84
86
  })
85
87
  return record
86
88
  }
@@ -110,7 +112,7 @@ asJson.elementsSorter = function (csn) {
110
112
  }
111
113
 
112
114
 
113
- function dataForField(element, entity, csn, record, data, parentEntity, depth, referenceData) {
115
+ function dataForField(element, artifact, csn, record, data, parent, depth, referenceData) {
114
116
  if (element.virtual || (element['@Core.Computed'] && element.value))
115
117
  return
116
118
  if (record[element.name] !== undefined)
@@ -119,12 +121,12 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
119
121
  if (element.items?.elements) // `array of` / `many` with complex type
120
122
  return [ fillRecord({}, record['$id'], element.items, csn, data, null, depth+1, referenceData) ]
121
123
  else if (element.items?.type) // `array of` / `many` with simple type
122
- return [ dataForField(element.items, entity, csn, record, data, null, depth+1, referenceData) ]
124
+ return [ dataForField(element.items, artifact, csn, record, data, null, depth+1, referenceData) ]
123
125
 
124
126
  if (element.is_struct) {
125
127
  let struct = {}
126
128
  for (const el of element.elements) {
127
- struct[el.name] = dataForField(el, entity, csn, record, data, null, depth+1, referenceData)
129
+ struct[el.name] = dataForField(el, artifact, csn, record, data, null, depth+1, referenceData)
128
130
  }
129
131
  return struct
130
132
  }
@@ -166,11 +168,11 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
166
168
  const enumName = randomFromList(Object.keys(element.enum))
167
169
  const value = element.enum[enumName].val ?? element.enum[enumName].value ?? enumName
168
170
  // key of common.CodeList, also fill name and descr with enum name
169
- if (element.key && entity.elements.name && entity.elements.descr)
171
+ if (element.key && artifact.elements.name && artifact.elements.descr)
170
172
  record.name = record.descr = enumName
171
173
  return value
172
174
  }
173
- if (entity['@Communication.Contact.email']?.some(mail => mail.address?.['='] === element.name)
175
+ if (artifact['@Communication.Contact.email']?.some(mail => mail.address?.['='] === element.name)
174
176
  || element['@Communication.IsEmailAddress']
175
177
  || element['@cds.on.insert']?.['='] === '$user'
176
178
  || element['@cds.on.update']?.['='] === '$user') {
@@ -180,9 +182,9 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
180
182
  let str = `${element.name}-${record['$id']}`
181
183
 
182
184
  if (element.key) {
183
- str = `${entity.name.split('.').pop()}-${record['$id']}`
184
- if (element.name === 'locale' && entity.name.endsWith('texts')) {
185
- const record = randomFromReferenceData(entity, csn, referenceData)
185
+ str = `${artifact.name.split('.').pop()}-${record['$id']}`
186
+ if (element.name === 'locale' && artifact.name.endsWith('texts')) {
187
+ const record = randomFromReferenceData(artifact, csn, referenceData)
186
188
  if (record) return record.locale
187
189
  return randomLanguage()
188
190
  }
@@ -227,9 +229,11 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
227
229
  }
228
230
  case 'cds.hana.REAL':
229
231
  case 'cds.Decimal': {
230
- const range = {from: 0, to: 100}
232
+ const prec = element.precision ? Math.min(Math.pow(10, element.precision), Number.MAX_SAFE_INTEGER) : 100
233
+ const range = { from: 0, to: prec }
234
+ const scale = element.scale ?? (element.precision ? 0 : 2) // scale is 0 if only precision is given
231
235
  annotatedIntRange(element, range)
232
- return +(randomDecimalInRange(range).toFixed(element.scale ?? 2))
236
+ return +(randomDecimalInRange(range).toFixed(scale))
233
237
  }
234
238
  case 'cds.Double': {
235
239
  const range = {from: 0, to: 100}
@@ -237,15 +241,15 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
237
241
  }
238
242
  case 'cds.Composition': {
239
243
  const target = csn.definitions[element.target]
240
- if (!target || entity.name === target.name || depth > MAX_ASSOC_DEPTH) return
241
-
244
+ if (!target || artifact.name === target.name || depth > MAX_ASSOC_DEPTH) return
245
+
242
246
  let targetRecords = [] // in findKeysInOnCondition target records are linked to the entity, we don't want to overwrite any existing data
243
247
  if (data[target.name]) data[target.name].deleteReference = true // for json composites references are nested and will be deleted when writing to file
244
-
248
+
245
249
  const targetRecordCount = element.is2many ? 2 : 1 // TODO: make this configurable
246
250
  const targetRecordID = randomNumber()
247
251
  for (let i = 0; i < targetRecordCount; i++) {
248
- const newRecord = fillRecord({}, targetRecordID+i, target, csn, data, entity, depth+1, referenceData)
252
+ const newRecord = fillRecord({}, targetRecordID+i, target, csn, data, artifact, depth+1, referenceData)
249
253
  if (newRecord) targetRecords.push(newRecord)
250
254
  }
251
255
 
@@ -259,8 +263,8 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
259
263
  }
260
264
  uniqRecords(targetRecords, target)
261
265
 
262
- // set the entire target record inline
263
- return element.is2many ? targetRecords : targetRecords[0]
266
+ // consistent return data type: array
267
+ return element.is2many ? targetRecords : [targetRecords[0]]
264
268
  }
265
269
  case 'cds.Association': {
266
270
  if (element.is2many)
@@ -280,16 +284,16 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
280
284
  targetRecords = data[target.name] = []
281
285
  else if (!targetRecords) // target entity is not in the list of entities to be created
282
286
  return
283
-
287
+
284
288
  if (targetRecords.length > 0) { // use record generated by following asociation
285
289
  targetRecord = targetRecords.at(-1) // make sure the localized record references the newly generated texts
286
290
  } else {
287
- targetRecord = fillRecord({}, randomNumber(), target, csn, data, entity, depth+1, referenceData)
291
+ targetRecord = fillRecord({}, randomNumber(), target, csn, data, artifact, depth+1, referenceData)
288
292
  if (targetRecord) {
289
293
  targetRecords.push(targetRecord)
290
294
  uniqRecords(targetRecords, target)
291
295
  }
292
- if (parentEntity && (parentEntity.name != entity.name)) data[target.name].keepReferece = true
296
+ if (parent && (parent.name != artifact.name)) data[target.name].keepReferece = true
293
297
  }
294
298
  }
295
299
 
@@ -308,7 +312,7 @@ function dataForField(element, entity, csn, record, data, parentEntity, depth, r
308
312
  case 'cds.Vector': // TODO add Vector
309
313
  return
310
314
  default: {
311
- console.error(`Unknown type ${element._type} (${element.type}) in '${entity.name}:${element.name}'`)
315
+ console.error(`Unknown type ${element._type} (${element.type}) in '${artifact.name}:${element.name}'`)
312
316
  return
313
317
  }
314
318
  }
@@ -393,14 +397,14 @@ function findKeysInOnCondition(assocElement, target) {
393
397
  return [ownKey, ownKeyStructured, targetKey]
394
398
  }
395
399
 
396
- function uniqRecords(records, entity) {
397
- const keys = Object.keys(entity.keys||{})
398
- if (keys.length === 0) return records
400
+ function uniqRecords(records, artifact) {
401
+ const keys = Object.keys(artifact.keys||{})
402
+ if (keys.length === 0) keys.push('$id') // no keys (e.g. in actions) -> use record's $id
399
403
  const keySet = new Set()
400
404
  for (let i = 0; i < records.length; i++) {
401
405
  const key = keys.map(k => records[i][k]).join('|')
402
406
  if (keySet.has(key)) {
403
- // console.debug('Removing', keys, key, 'from', entity.name)
407
+ // console.debug('Removing', keys, key, 'from', artifact.name)
404
408
  records.splice(i, 1)
405
409
  i--
406
410
  }
@@ -5,7 +5,7 @@
5
5
  "@sap/hdi-deploy": "^5"
6
6
  },
7
7
  "engines": {
8
- "node": "^20.0.0"
8
+ "node": "^22.0.0"
9
9
  },
10
10
  "scripts": {
11
11
  "start": "node node_modules/@sap/hdi-deploy/deploy.js --use-hdb",
@@ -3,10 +3,7 @@
3
3
  "dependencies": {
4
4
  "@cap-js/hana": "^1"
5
5
  },
6
- {{/isNodejs}}
7
- {{#isNodejs}}
8
6
  "cds": {
9
- {{/isNodejs}}
10
7
  "sql": {
11
8
  "native_hana_associations": false
12
9
  }{{#profile}},
@@ -22,7 +19,24 @@
22
19
  {{/shortcut}}
23
20
  }
24
21
  }{{/profile}}
25
- {{#isNodejs}}
26
22
  }
27
23
  {{/isNodejs}}
24
+ {{#isJava}}
25
+ "requires": {
26
+ {{#profile}}
27
+ "[{{profile}}]": {
28
+ {{/profile}}
29
+ {{#shortcut}}
30
+ "db": "hana"
31
+ {{/shortcut}}
32
+ {{^shortcut}}
33
+ "db": {
34
+ "kind": "hana"
35
+ }
36
+ {{/shortcut}}
37
+ {{#profile}}
38
+ }
39
+ {{/profile}}
40
+ }
41
+ {{/isJava}}
28
42
  }
@@ -29,12 +29,14 @@ module.exports = class HttpTemplate extends require('../../plugin') {
29
29
  'for-app': {
30
30
  type: 'string',
31
31
  short: 'a',
32
- help: 'Specify the name of the app to generate requests for. If not specified, localhost and default auth will be used.'
32
+ help: `Specify the name of the app to generate requests for.
33
+ If not specified, localhost and default auth will be used.`
33
34
  },
34
35
  'out': {
35
36
  type: 'string',
36
37
  short: 'o',
37
- help: 'The output directory. By default, an `http` dir will be created in one of the following: test/tests/__tests__, whichever already exists. If none exists, an `http` dir will be created at the root level.'
38
+ help: `The output directory.
39
+ By default, an \`http\` dir is created in either \`test/\`, \`tests/\`, \`__tests__/\`, or at the root level.`
38
40
  },
39
41
  'dry': {
40
42
  type: 'boolean',
@@ -130,9 +132,12 @@ module.exports = class HttpTemplate extends require('../../plugin') {
130
132
  for (const e of service.entities) {
131
133
  payload[e.name] = []
132
134
  }
135
+ for (const a of service.actions) {
136
+ payload[a.name] = []
137
+ }
133
138
  }
134
139
  // csn.all('entity').reduce((all, e) => { all[e.name] = []; return all }, {})
135
- await asJson(payload, csn, 1)
140
+ await asJson(payload, csn, 1, {referenceData: []})
136
141
  for (const p in payload) {
137
142
  delete payload[p][0].texts
138
143
  delete payload[p][0].localized
@@ -176,11 +181,11 @@ module.exports = class HttpTemplate extends require('../../plugin') {
176
181
  else {
177
182
  const postBody = JSON.stringify(entityData, null, 2)
178
183
  let id
179
- const compositeKey = Object.keys(e.keys).length > 1
184
+ const compositeKey = Object.keys(e.keys||{}).length > 1
180
185
  if (compositeKey) {
181
186
  id = [...e.keys].map(k => k.type === 'cds.String' ? `${k.name}='${entityData[k.name]}'` : `${k.name}=${entityData[k.name]}`).join(',')
182
187
  } else {
183
- id = entityData[Object.keys(e.keys)[0]]
188
+ id = entityData[Object.keys(e.keys||{})[0]]
184
189
  }
185
190
  compositeKey ? id = `(${id})` : id = `/${id}`
186
191
  requests.push(`${requestTitle}\n# @name ${nameSlug}_POST\nPOST ${url}\n${headers}\n\n${postBody}\n`)
@@ -195,12 +200,8 @@ module.exports = class HttpTemplate extends require('../../plugin') {
195
200
 
196
201
  let actionPayload = {}
197
202
  if (a.params) {
198
- for (const [key, value] of Object.entries(a.params)) {
199
- actionPayload[key] = value.type === 'cds.Integer' ? 1
200
- : value.type === 'string' ? 'sample string'
201
- // TODO reference an object that has been generated
202
- : typeof value.type === 'object' && value.type.ref ? payload[value.type.ref[0]][0][value.type.ref[1]]
203
- : null
203
+ for (const pName of Object.keys(a.params)) {
204
+ actionPayload[pName] = payload[a.name]?.[0]?.[pName] ?? null
204
205
  }
205
206
  }
206
207
  requests.push(`\n### ${simpleNameAction}\n# @name ${simpleNameAction}_POST\nPOST ${url}\n${headers}\n\n${JSON.stringify(actionPayload, null, 2)}\n`)
@@ -19,6 +19,7 @@ module.exports = class extends require('../../plugin') {
19
19
  canRun() {
20
20
  const { isNodejs } = readProject()
21
21
  if (isNodejs) throw 'Side-by-side extensibility is not supported for Node.js yet.'
22
+ return true
22
23
  }
23
24
 
24
25
  async run() {
@@ -24,7 +24,6 @@ module.exports = class TyperTemplate extends require('../../plugin') {
24
24
  let target = 'jsconfig.json'
25
25
 
26
26
  if (exists('tsconfig.json')) {
27
- source.compilerOptions.paths["#cds-models/*"][0] += '/index.ts'
28
27
  target = 'tsconfig.json'
29
28
  }
30
29
  await merge(template('package.json')).into('package.json')
package/lib/mtx/push.js CHANGED
@@ -26,7 +26,6 @@ module.exports = class Push {
26
26
  const extensionName = this.getExtensionName(projectFolder);
27
27
 
28
28
  if (!('extArchive' in pushOptions)) {
29
- console.log(`Building project`);
30
29
  await buildSystem.build();
31
30
  }
32
31
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "8.4.0",
3
+ "version": "8.4.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@sap/cds-dk",
9
- "version": "8.4.0",
9
+ "version": "8.4.2",
10
10
  "license": "SEE LICENSE IN LICENSE",
11
11
  "dependencies": {
12
12
  "@cap-js/asyncapi": "^1.0.0",
@@ -43,7 +43,7 @@
43
43
  }
44
44
  },
45
45
  "node_modules/@cap-js/db-service": {
46
- "version": "1.14.1",
46
+ "version": "1.15.0",
47
47
  "license": "SEE LICENSE",
48
48
  "optional": true,
49
49
  "dependencies": {
@@ -64,11 +64,11 @@
64
64
  }
65
65
  },
66
66
  "node_modules/@cap-js/sqlite": {
67
- "version": "1.7.5",
67
+ "version": "1.7.7",
68
68
  "license": "SEE LICENSE",
69
69
  "optional": true,
70
70
  "dependencies": {
71
- "@cap-js/db-service": "^1.9.0",
71
+ "@cap-js/db-service": "^1.14.1",
72
72
  "better-sqlite3": "^11.0.0"
73
73
  },
74
74
  "peerDependencies": {
@@ -149,7 +149,7 @@
149
149
  }
150
150
  },
151
151
  "node_modules/@eslint/js": {
152
- "version": "9.13.0",
152
+ "version": "9.14.0",
153
153
  "license": "MIT",
154
154
  "engines": {
155
155
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -190,6 +190,17 @@
190
190
  "node": ">=18.18.0"
191
191
  }
192
192
  },
193
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
194
+ "version": "0.3.1",
195
+ "license": "Apache-2.0",
196
+ "engines": {
197
+ "node": ">=18.18"
198
+ },
199
+ "funding": {
200
+ "type": "github",
201
+ "url": "https://github.com/sponsors/nzakas"
202
+ }
203
+ },
193
204
  "node_modules/@humanwhocodes/module-importer": {
194
205
  "version": "1.0.1",
195
206
  "license": "Apache-2.0",
@@ -202,7 +213,7 @@
202
213
  }
203
214
  },
204
215
  "node_modules/@humanwhocodes/retry": {
205
- "version": "0.3.1",
216
+ "version": "0.4.1",
206
217
  "license": "Apache-2.0",
207
218
  "engines": {
208
219
  "node": ">=18.18"
@@ -213,7 +224,7 @@
213
224
  }
214
225
  },
215
226
  "node_modules/@sap/cds": {
216
- "version": "8.4.0",
227
+ "version": "8.4.2",
217
228
  "license": "SEE LICENSE IN LICENSE",
218
229
  "dependencies": {
219
230
  "@sap/cds-compiler": ">=5.1",
@@ -239,7 +250,7 @@
239
250
  }
240
251
  },
241
252
  "node_modules/@sap/cds-compiler": {
242
- "version": "5.4.0",
253
+ "version": "5.4.4",
243
254
  "license": "SEE LICENSE IN LICENSE",
244
255
  "dependencies": {
245
256
  "antlr4": "4.9.3"
@@ -287,7 +298,7 @@
287
298
  }
288
299
  },
289
300
  "node_modules/@sap/eslint-plugin-cds": {
290
- "version": "3.1.1",
301
+ "version": "3.1.2",
291
302
  "license": "See LICENSE file",
292
303
  "dependencies": {
293
304
  "@sap/cds": ">=7",
@@ -717,7 +728,7 @@
717
728
  "license": "MIT"
718
729
  },
719
730
  "node_modules/cross-spawn": {
720
- "version": "7.0.3",
731
+ "version": "7.0.5",
721
732
  "license": "MIT",
722
733
  "dependencies": {
723
734
  "path-key": "^3.1.0",
@@ -875,19 +886,19 @@
875
886
  }
876
887
  },
877
888
  "node_modules/eslint": {
878
- "version": "9.13.0",
889
+ "version": "9.14.0",
879
890
  "license": "MIT",
880
891
  "dependencies": {
881
892
  "@eslint-community/eslint-utils": "^4.2.0",
882
- "@eslint-community/regexpp": "^4.11.0",
893
+ "@eslint-community/regexpp": "^4.12.1",
883
894
  "@eslint/config-array": "^0.18.0",
884
895
  "@eslint/core": "^0.7.0",
885
896
  "@eslint/eslintrc": "^3.1.0",
886
- "@eslint/js": "9.13.0",
897
+ "@eslint/js": "9.14.0",
887
898
  "@eslint/plugin-kit": "^0.2.0",
888
- "@humanfs/node": "^0.16.5",
899
+ "@humanfs/node": "^0.16.6",
889
900
  "@humanwhocodes/module-importer": "^1.0.1",
890
- "@humanwhocodes/retry": "^0.3.1",
901
+ "@humanwhocodes/retry": "^0.4.0",
891
902
  "@types/estree": "^1.0.6",
892
903
  "@types/json-schema": "^7.0.15",
893
904
  "ajv": "^6.12.4",
@@ -895,9 +906,9 @@
895
906
  "cross-spawn": "^7.0.2",
896
907
  "debug": "^4.3.2",
897
908
  "escape-string-regexp": "^4.0.0",
898
- "eslint-scope": "^8.1.0",
899
- "eslint-visitor-keys": "^4.1.0",
900
- "espree": "^10.2.0",
909
+ "eslint-scope": "^8.2.0",
910
+ "eslint-visitor-keys": "^4.2.0",
911
+ "espree": "^10.3.0",
901
912
  "esquery": "^1.5.0",
902
913
  "esutils": "^2.0.2",
903
914
  "fast-deep-equal": "^3.1.3",
@@ -933,7 +944,7 @@
933
944
  }
934
945
  },
935
946
  "node_modules/eslint-scope": {
936
- "version": "8.1.0",
947
+ "version": "8.2.0",
937
948
  "license": "BSD-2-Clause",
938
949
  "dependencies": {
939
950
  "esrecurse": "^4.3.0",
@@ -947,7 +958,7 @@
947
958
  }
948
959
  },
949
960
  "node_modules/eslint-visitor-keys": {
950
- "version": "4.1.0",
961
+ "version": "4.2.0",
951
962
  "license": "Apache-2.0",
952
963
  "engines": {
953
964
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -957,12 +968,12 @@
957
968
  }
958
969
  },
959
970
  "node_modules/espree": {
960
- "version": "10.2.0",
971
+ "version": "10.3.0",
961
972
  "license": "BSD-2-Clause",
962
973
  "dependencies": {
963
- "acorn": "^8.12.0",
974
+ "acorn": "^8.14.0",
964
975
  "acorn-jsx": "^5.3.2",
965
- "eslint-visitor-keys": "^4.1.0"
976
+ "eslint-visitor-keys": "^4.2.0"
966
977
  },
967
978
  "engines": {
968
979
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1690,7 +1701,7 @@
1690
1701
  }
1691
1702
  },
1692
1703
  "node_modules/object-inspect": {
1693
- "version": "1.13.2",
1704
+ "version": "1.13.3",
1694
1705
  "license": "MIT",
1695
1706
  "engines": {
1696
1707
  "node": ">= 0.4"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "8.4.0",
3
+ "version": "8.4.2",
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": [