@sap/cds-dk 9.7.2 → 9.8.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.
- package/CHANGELOG.md +35 -2
- package/bin/bind.js +7 -2
- package/bin/build.js +14 -4
- package/bin/cds.js +2 -2
- package/bin/completion/completionFs.js +1 -1
- package/bin/completion/index.js +2 -2
- package/bin/data-products/get-csn-via-dp-cli.js +1 -1
- package/bin/debug.js +1 -1
- package/bin/deploy/index.js +3 -2
- package/bin/gen.js +1 -1
- package/bin/help.js +1 -1
- package/bin/import.js +3 -0
- package/bin/minify.js +1 -1
- package/bin/mock.js +1 -1
- package/bin/repl.js +212 -39
- package/bin/test.js +0 -2
- package/bin/up.js +27 -3
- package/bin/util/containerize.js +1 -3
- package/bin/version.js +27 -7
- package/bin/watch.js +20 -4
- package/lib/bind/cf.js +2 -2
- package/lib/bind/k8s.js +0 -1
- package/lib/bind/plugin.js +2 -0
- package/lib/bind/shared.js +6 -1
- package/lib/build/constants.js +0 -8
- package/lib/build/index.js +569 -25
- package/lib/build/{provider/edmxBuildPlugin.js → plugins/edmx.js} +6 -15
- package/lib/build/{provider/featureToggleBuildPlugin.js → plugins/featureToggles.js} +3 -23
- package/lib/build/{provider → plugins}/fiori/index.js +27 -38
- package/lib/build/{provider → plugins}/hana/index.js +61 -83
- package/lib/build/{provider → plugins}/hana/migrationtable.js +4 -3
- package/lib/build/plugins/helm/index.js +6 -6
- package/lib/build/{provider/internalBuildPlugin.js → plugins/internal.js} +7 -31
- package/lib/build/{provider → plugins}/java/index.js +18 -26
- package/lib/build/plugins/mtx/index.js +41 -0
- package/lib/build/plugins/mtx/resourcesTarBuilder.js +41 -0
- package/lib/build/{provider → plugins}/mtx-extension/extensionCompilation.js +1 -1
- package/lib/build/{provider → plugins}/mtx-extension/index.js +35 -44
- package/lib/build/{provider → plugins}/mtx-sidecar/index.js +17 -3
- package/lib/build/{provider → plugins}/nodejs/index.js +29 -28
- package/lib/build/{plugin.js → plugins/plugin.js} +45 -59
- package/lib/build/plugins.js +11 -8
- package/lib/build/util.js +0 -8
- package/lib/compile/to-xsuaa.js +2 -2
- package/lib/deploy/to-hana/hana.js +64 -56
- package/lib/import/common.js +1 -1
- package/lib/import/odata/metadataconverter/MetadataConverter.js +4 -3
- package/lib/import/odata/v2parser.js +28 -28
- package/lib/import/odata/v4parser.js +37 -2
- package/lib/import/openapi/index.js +1 -1
- package/lib/init/constants.js +0 -1
- package/lib/init/template/app-frontend/index.js +1 -1
- package/lib/init/template/approuter/files/package.json +1 -1
- package/lib/init/template/completion/completionSetup.js +1 -1
- package/lib/init/template/data/as-csv.js +1 -1
- package/lib/init/template/github-actions/index.js +1 -1
- package/lib/init/template/handler/index.js +1 -1
- package/lib/init/template/helm/index.js +3 -3
- package/lib/init/template/kyma/index.js +3 -3
- package/lib/init/template/lint/index.js +7 -21
- package/lib/init/template/multitenancy/files/package.json.hbs +0 -5
- package/lib/init/template/postgres/files/containerize.yaml.hbs +15 -0
- package/lib/init/template/postgres/files/package.json.hbs +35 -0
- package/lib/init/template/postgres/files/values.yaml.hbs +23 -0
- package/lib/init/template/postgres/index.js +34 -9
- package/lib/init/template/test/files/package.json +1 -1
- package/lib/init/template/typescript/files/package.json +1 -1
- package/lib/init/template/workzone-standard/files/mta.yaml.hbs +1 -0
- package/lib/lint/eslintHelp.json +1 -1
- package/lib/lint/io.js +9 -13
- package/lib/mtx/pull.js +1 -1
- package/lib/mtx/settings_manager.js +11 -5
- package/lib/mtx/tenant_action.js +1 -1
- package/lib/mtx/util/fs.js +2 -2
- package/lib/util/fs.js +3 -3
- package/lib/util/git.js +1 -1
- package/lib/util/glob.js +1 -1
- package/lib/watch/hot-reload.js +202 -0
- package/lib/watch/livereload-server.js +6 -4
- package/lib/watch/node-watch.js +5 -1
- package/lib/watch/watched.js +7 -3
- package/lib/watch/watcher.js +41 -3
- package/npm-shrinkwrap.json +31 -22
- package/package.json +1 -1
- package/lib/build/buildTaskEngine.js +0 -377
- package/lib/build/buildTaskFactory.js +0 -198
- package/lib/build/buildTaskProvider.js +0 -38
- package/lib/build/buildTaskProviderFactory.js +0 -233
- package/lib/build/provider/internalBuildTaskProvider.js +0 -227
- package/lib/build/provider/java-cf/index.js +0 -1
- package/lib/build/provider/mtx/index.js +0 -36
- package/lib/build/provider/mtx/resourcesTarBuilder.js +0 -102
- package/lib/build/provider/node-cf/index.js +0 -1
- package/lib/init/template/lint/files/.eslint/rules/no-entity-moo.js +0 -40
- package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/fixed/schema.cds +0 -4
- package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/invalid/schema.cds +0 -4
- package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/rule.test.js +0 -15
- package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/valid/schema.cds +0 -6
- package/lib/init/types.d.ts +0 -23
- /package/lib/build/{provider → plugins}/hana/2migration.js +0 -0
- /package/lib/build/{provider → plugins}/hana/template/.hdiconfig-all +0 -0
- /package/lib/build/{provider → plugins}/hana/template/.hdiconfig-cloud +0 -0
- /package/lib/build/{provider → plugins}/hana/template/.hdinamespace +0 -0
- /package/lib/build/{provider → plugins}/hana/template/package.json +0 -0
- /package/lib/build/{provider → plugins}/hana/template/undeploy.json +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,7 +6,35 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
8
8
|
|
|
9
|
-
## Version 9.
|
|
9
|
+
## Version 9.8.0 - 2026-03-09
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `cds up --overlay <file|name> (Cloud Foundry) now resolves MTA extension descriptors (<name>.mtaext) from the project root.`
|
|
14
|
+
- `cds add postgres` now supports Kyma with automatic database provisioning and IP whitelisting.
|
|
15
|
+
- `cds deploy --to hana --store-credentials` now warns about the deprecated option `--store-credentials`.
|
|
16
|
+
- `cds import` now uses the default options defined via `cds.import.options[<from>] = {option: 'value'}`
|
|
17
|
+
- `cds version` now shows version of `@sap/eslint-plugin-cds`.
|
|
18
|
+
- `cds version --json` returns a stable output format as JSON.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- `cds add multitenancy` does not add an explicit `java` profile to the main app any more.
|
|
23
|
+
- `cds deploy` and `cds bind` support Kyma-by-default for projects with Kyma-only deployment descriptors.
|
|
24
|
+
- `cds deploy --to hana --on k8s` now supports deploying to SAP HANA using an existing Kubernetes service binding, allowing non-default Kyma naming conventions.
|
|
25
|
+
- `cds import` now correctly imports Views with Parameters from a given OData EDMX service definition.
|
|
26
|
+
- `cds import` now interprets empty navigation path references as if it would be a null value.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- `cds bind` reports additional error information while accessing Cloud Foundry services.
|
|
31
|
+
- `cds version` now works again if executed in subfolders of Java projects without a `pom.xml` file.
|
|
32
|
+
- `cds add workzone` in combination with `cds add ias` adds the `HTML5.IASDependencyName` for the `srv-api`.
|
|
33
|
+
- `cds add lint` now creates an ESLint config file again.
|
|
34
|
+
- `cds import` now correctly handles enum types as keys in EDMX v4 import.
|
|
35
|
+
- `cds push` now shows the correct passcode URL when the subdomain changes between logins.
|
|
36
|
+
|
|
37
|
+
## Version 9.7.2 - 2026-02-13
|
|
10
38
|
|
|
11
39
|
### Fixed
|
|
12
40
|
|
|
@@ -21,7 +49,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
|
21
49
|
### Fixed
|
|
22
50
|
|
|
23
51
|
- `cds add completion` now works without specifying the project runtime.
|
|
24
|
-
|
|
25
52
|
- `cds add xsuaa` correctly adds the the binding for the `html5-apps-deployer` on Kyma.
|
|
26
53
|
- `cds version` prints an older format in a legacy situation.
|
|
27
54
|
- `cds add nodejs` is automatically added in BAS scenarios for compatibility if no other runtime is specified.
|
|
@@ -470,6 +497,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
|
470
497
|
- `cds add ams` for Java adds a custom builder to the `mta.yaml` to circumvent the missing `srv/src/gen/policies`.
|
|
471
498
|
- `cds add multitenancy` requires the `srv-api` instead of the `mtx-api` for Java projects.
|
|
472
499
|
|
|
500
|
+
## Version 8.9.14 - 2026-02-25
|
|
501
|
+
|
|
502
|
+
### Fixed
|
|
503
|
+
|
|
504
|
+
- CVE-2026-25639: Update `axios` version to 1.13.5
|
|
505
|
+
|
|
473
506
|
## Version 8.9.13 - 2029-01-09
|
|
474
507
|
|
|
475
508
|
### Fixed
|
package/bin/bind.js
CHANGED
|
@@ -32,7 +32,8 @@ module.exports = Object.assign(cds_bind, {
|
|
|
32
32
|
|
|
33
33
|
*-n | --on* cf | k8s
|
|
34
34
|
|
|
35
|
-
Target platform (Cloud Foundry or Kubernetes) to bind to.
|
|
35
|
+
Target platform (Cloud Foundry or Kubernetes) to bind to.
|
|
36
|
+
Defaults to *k8s* when Helm charts are present and no *mta.yaml* exists, otherwise *cf*.
|
|
36
37
|
|
|
37
38
|
*-k | --kind* <kind>
|
|
38
39
|
|
|
@@ -79,11 +80,15 @@ module.exports = Object.assign(cds_bind, {
|
|
|
79
80
|
const os = require('os');
|
|
80
81
|
const IS_WIN = os.platform() === 'win32';
|
|
81
82
|
|
|
83
|
+
const cds = require('../lib/cds')
|
|
82
84
|
const { bind, storeBindings } = require('../lib/bind/shared');
|
|
85
|
+
const { readProject } = require('../lib/init/projectReader')
|
|
83
86
|
|
|
84
87
|
|
|
85
88
|
async function cds_bind(args, options) {
|
|
86
|
-
const {
|
|
89
|
+
const { hasKyma, hasMta } = readProject()
|
|
90
|
+
const onKyma = cds.cli.options.on === 'k8s' || (hasKyma && !hasMta && cds.cli.options.on !== 'cf')
|
|
91
|
+
const { on = onKyma ? 'k8s' : 'cf', 'to-app-services': app, to, out, 'output-file': outputFile, exec, credentials } = options
|
|
87
92
|
|
|
88
93
|
if (exec) {
|
|
89
94
|
process.env.CDS_ENV ??= 'hybrid'
|
package/bin/build.js
CHANGED
|
@@ -165,17 +165,27 @@ async function build([project], options = {}) {
|
|
|
165
165
|
options.taskOptions = scanTaskOptionParams(options.opts)
|
|
166
166
|
delete options.opts
|
|
167
167
|
}
|
|
168
|
-
const { build } = require('../lib/build')
|
|
169
|
-
|
|
168
|
+
const { build, BuildError } = require('../lib/build')
|
|
169
|
+
try {
|
|
170
|
+
return build(options)
|
|
171
|
+
} catch (e) {
|
|
172
|
+
if (e instanceof BuildError || e.constructor.name === 'CompilationError') {
|
|
173
|
+
cds._log(e.messages.length ? e.messages : e.message, { 'log-level': options['log-level'] || cds.env['log-level'] })
|
|
174
|
+
console.log()
|
|
175
|
+
// CompilationError.message also includes the detail messages - do not log twice
|
|
176
|
+
throw e.constructor.name === 'CompilationError' ? 'CDS compilation failed' : e.messages.length ? e.message : 'CDS build failed'
|
|
177
|
+
}
|
|
178
|
+
throw e
|
|
179
|
+
}
|
|
170
180
|
}
|
|
171
181
|
|
|
182
|
+
const quoteRegex = /([\w-]+)=([\w/.-]+|\[([\w/,.-]+)\])/g
|
|
172
183
|
function scanTaskOptionParams(optsParams) {
|
|
173
|
-
// need to create new regex every call since a constant would keep the match state
|
|
174
|
-
const quoteRegex = /([\w-]+)=([\w/.-]+|\[([\w/,.-]+)\])/g
|
|
175
184
|
|
|
176
185
|
// captures a=1 => a:1
|
|
177
186
|
// a=[x,y,z] => a:[x,y,z]
|
|
178
187
|
// a=1,b=[x,y,z] => a:1 b=[x,y,z]
|
|
188
|
+
quoteRegex.lastIndex = 0 // so we don't need to create a new regex every call since a constant would keep the match state
|
|
179
189
|
let match = quoteRegex.exec(optsParams)
|
|
180
190
|
const taskOptions = {}
|
|
181
191
|
|
package/bin/cds.js
CHANGED
|
@@ -78,7 +78,7 @@ const cli = {
|
|
|
78
78
|
const flags = all.filter(([,f]) => f.type === 'boolean')
|
|
79
79
|
const options = all.filter(([,o]) => o.type !== 'boolean')
|
|
80
80
|
task.options.push(...options.map(([key]) => '--'+key))
|
|
81
|
-
task.shortcuts.push(...options.map(([
|
|
81
|
+
task.shortcuts.push(...options.map(([, { short }]) => short ? '-'+short : undefined).filter(Boolean))
|
|
82
82
|
const byShortcut = (a, b) => (b.short ? 1 : 0) - (a.short ? 1 : 0)
|
|
83
83
|
task.options.sort(byShortcut)
|
|
84
84
|
task.shortcuts.sort(byShortcut)
|
|
@@ -226,7 +226,7 @@ function getCommands() {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
function _require (id,o) {
|
|
229
|
-
try { var resolved = require.resolve(id,o) } catch
|
|
229
|
+
try { var resolved = require.resolve(id,o) } catch { DEBUG?.(`[cds] - Command not found: ${id}`); return }
|
|
230
230
|
DEBUG?.(`[cds] - Command resolved: ${resolved}`)
|
|
231
231
|
return require (resolved)
|
|
232
232
|
}
|
|
@@ -17,7 +17,7 @@ module.exports = new class CompletionFS {
|
|
|
17
17
|
let entries;
|
|
18
18
|
try {
|
|
19
19
|
entries = await fsp.readdir(folder, { withFileTypes: true });
|
|
20
|
-
} catch
|
|
20
|
+
} catch {
|
|
21
21
|
folder = path.dirname(folder);
|
|
22
22
|
entries = await fsp.readdir(folder, { withFileTypes: true });
|
|
23
23
|
}
|
package/bin/completion/index.js
CHANGED
|
@@ -123,7 +123,7 @@ async function completion(_argv, options) {
|
|
|
123
123
|
reply(completions);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
async function handleCompletion(currentWord, previousWord, argv
|
|
126
|
+
async function handleCompletion(currentWord, previousWord, argv) {
|
|
127
127
|
const allOptionsFlags = ['--add', '--remove', '--shell']
|
|
128
128
|
.filter(e => !argv.includes(e));
|
|
129
129
|
|
|
@@ -222,7 +222,7 @@ async function getCompletions(cdsCmd, previousWord, currentWord, segments) {
|
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
return all;
|
|
225
|
-
} catch
|
|
225
|
+
} catch {
|
|
226
226
|
return [];
|
|
227
227
|
}
|
|
228
228
|
}
|
package/bin/debug.js
CHANGED
|
@@ -259,7 +259,7 @@ async function debug() {
|
|
|
259
259
|
function _execSyncSafe(...args) {
|
|
260
260
|
try {
|
|
261
261
|
return _execSync(...args)
|
|
262
|
-
} catch
|
|
262
|
+
} catch { /* ignore */ }
|
|
263
263
|
}
|
|
264
264
|
function _execSync(cmd, options={ encoding: 'utf-8', shell: true }) {
|
|
265
265
|
try {
|
package/bin/deploy/index.js
CHANGED
|
@@ -50,7 +50,8 @@ module.exports = Object.assign(deploy, {
|
|
|
50
50
|
|
|
51
51
|
*--on* | *-n* cf|k8s
|
|
52
52
|
|
|
53
|
-
The target platform.
|
|
53
|
+
The target platform.
|
|
54
|
+
Defaults to *k8s* when Helm charts are present and no *mta.yaml* exists, otherwise *cf*.
|
|
54
55
|
|
|
55
56
|
*--with-mocks*
|
|
56
57
|
|
|
@@ -137,7 +138,7 @@ async function deploy(_model, options) {
|
|
|
137
138
|
var deployer = require('./to-' + kind)
|
|
138
139
|
} catch (e) {
|
|
139
140
|
if (e.code === 'MODULE_NOT_FOUND') {
|
|
140
|
-
throw new Error(`Didn't find a deployer module for '${kind}'`)
|
|
141
|
+
throw new Error(`Didn't find a deployer module for '${kind}'`) // eslint-disable-line preserve-caught-error
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
try {
|
package/bin/gen.js
CHANGED
|
@@ -8,7 +8,7 @@ function global (id, subpath) {
|
|
|
8
8
|
let mod
|
|
9
9
|
try {
|
|
10
10
|
mod = require.resolve (pkgPath, { paths: [ process.cwd(), __dirname ] })
|
|
11
|
-
} catch
|
|
11
|
+
} catch { // one more try w/ NPM global paths
|
|
12
12
|
const paths = [ process.cwd(), __dirname, npmGlobalModules() ]
|
|
13
13
|
try {
|
|
14
14
|
mod = require.resolve (pkgPath, { paths })
|
package/bin/help.js
CHANGED
|
@@ -42,7 +42,7 @@ module.exports = exports = Object.assign (help, {formatHelp, handleCompletion, h
|
|
|
42
42
|
|
|
43
43
|
`});
|
|
44
44
|
|
|
45
|
-
async function handleCompletion(
|
|
45
|
+
async function handleCompletion() {
|
|
46
46
|
const cds = require('./cds');
|
|
47
47
|
return Object.values(cds.cmds);
|
|
48
48
|
}
|
package/bin/import.js
CHANGED
|
@@ -168,6 +168,9 @@ async function _import(args, options = {}) {
|
|
|
168
168
|
// the required plugin might not be installed, e.g. rfc importer
|
|
169
169
|
throw messages.UNSUPPORTED_FROM_OPTION.replace('{0}', options.from).replace('{1}', Object.keys(cds.import.from).join(', '));
|
|
170
170
|
}
|
|
171
|
+
if (options.from && cds.import.options && cds.import.options[options.from]) {
|
|
172
|
+
options = Object.assign(cds.import.options[options.from], options)
|
|
173
|
+
}
|
|
171
174
|
csn = await cds.import.from[options.from](input, apiOptions);
|
|
172
175
|
} else {
|
|
173
176
|
csn = await cds.import(input, apiOptions);
|
package/bin/minify.js
CHANGED
package/bin/mock.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const cds = require ('../lib/cds'); require = cds._local // eslint-disable-line no-global-assign
|
|
2
|
-
const serve = require ('@sap/cds/bin/serve')
|
|
2
|
+
const serve = require ('@sap/cds/bin/serve')
|
|
3
3
|
|
|
4
4
|
module.exports = exports = Object.assign (cds_mock, serve)
|
|
5
5
|
|
package/bin/repl.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module.exports = Object.assign (repl, {
|
|
2
|
-
options: [ '--run', '--use' ],
|
|
2
|
+
options: [ '--run', '--use', '--ql' ],
|
|
3
3
|
shortcuts: [ '-r', '-u' ],
|
|
4
4
|
help: `
|
|
5
5
|
# SYNOPSIS
|
|
@@ -24,6 +24,11 @@ module.exports = Object.assign (repl, {
|
|
|
24
24
|
if you specify _xl_ it makes the _cds.xl_ module's methods available.
|
|
25
25
|
It's the same as doing _{ref,val,xpr,...} = cds.xl_ within the repl.
|
|
26
26
|
|
|
27
|
+
*--ql*
|
|
28
|
+
|
|
29
|
+
Starts the repl in cql evaluation mode.
|
|
30
|
+
It's the same as using the _.ql_ command within the repl.
|
|
31
|
+
|
|
27
32
|
# EXAMPLES
|
|
28
33
|
|
|
29
34
|
*cds repl* --run bookshop
|
|
@@ -37,9 +42,10 @@ module.exports = Object.assign (repl, {
|
|
|
37
42
|
|
|
38
43
|
async function repl ([project], options={}) {
|
|
39
44
|
|
|
40
|
-
const
|
|
45
|
+
const isTerminal = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
41
46
|
const cds = require('../lib/cds')
|
|
42
|
-
|
|
47
|
+
const { GREEN, PINK, BLUE, CYAN, YELLOW, GRAY, BOLD, RESET } = cds.utils.colors
|
|
48
|
+
console.log(`${GREEN}${BOLD}Welcome to cds repl v${cds.version}${RESET}`)
|
|
43
49
|
|
|
44
50
|
const { inspect } = require('util')
|
|
45
51
|
inspect.defaultOptions.colors = true
|
|
@@ -50,48 +56,57 @@ async function repl ([project], options={}) {
|
|
|
50
56
|
if ((options.run ??= project)) await _run (options.run)
|
|
51
57
|
if (options.use) _export (...options.use.split(','))
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
useGlobal: true,
|
|
57
|
-
writer: o => {
|
|
58
|
-
const options = {}
|
|
59
|
-
if (!o || typeof o !== 'object') return o
|
|
60
|
-
if ('init' in o && 'on' in o && 'run' in o) options.depth = 0
|
|
61
|
-
return inspect(o,options).replace(/\[Object: null prototype\] /g, '')
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
if (Object.keys(context).length) Object.assign (repl.context, context)
|
|
66
|
-
|
|
67
|
-
cds.extend (repl.context) .with ( class {
|
|
68
|
-
get Foo() { return ctx.Foo ??= new cds.entity({ name: 'Foo' }) }; set Foo(v) { ctx.Foo = v }
|
|
69
|
-
get Bar() { return ctx.Bar ??= new cds.entity({ name: 'Bar' }) }; set Bar(v) { ctx.Bar = v }
|
|
70
|
-
get expect() { return ctx.expect ??= cds.test.expect }; set expect(v) { ctx.expect = v }
|
|
71
|
-
} .prototype )
|
|
72
|
-
const ctx={}
|
|
73
|
-
|
|
74
|
-
repl.defineCommand ('run', { action: _run,
|
|
75
|
-
help: 'Runs a cds server from a given CAP project folder, or module name like @capire/bookshop.',
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
repl.defineCommand ('inspect', { action: _inspect,
|
|
79
|
-
help: 'Sets options for util.inspect, e.g. `.inspect .depth=1`.',
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
repl.history = new Array(222)
|
|
83
|
-
const home = process.env.HOME || process.env.USERPROFILE
|
|
84
|
-
const history = require('path').join(home,'.cds-repl-history')
|
|
85
|
-
const fs = require('fs')
|
|
86
|
-
fs.readFile(history, 'utf-8', (e, txt) => e || Object.assign(repl.history, txt.split('\n')))
|
|
87
|
-
repl.on('exit', () => fs.writeFile(history, repl.history.join('\n'), () => {}))
|
|
88
|
-
repl.on('exit', cds.shutdown)
|
|
59
|
+
if (options.ql) _ql()
|
|
60
|
+
else _js()
|
|
61
|
+
|
|
89
62
|
process.on('uncaughtException', console.error)
|
|
90
63
|
process.on('unhandledRejection', console.error)
|
|
91
64
|
|
|
92
65
|
// Ugly hack to prevent "[ERR_INVALID_REPL_INPUT]: Listeners for `uncaughtException` cannot be used in the REPL" errors caused by winston when connecting to remote services
|
|
93
66
|
process.on = (event, listener) => event !== 'uncaughtException' && event !== 'unhandledRejection' && process.addListener(event, listener)
|
|
94
67
|
|
|
68
|
+
|
|
69
|
+
function _js() {
|
|
70
|
+
repl = require('repl').start ({
|
|
71
|
+
...options,
|
|
72
|
+
ignoreUndefined: true,
|
|
73
|
+
useGlobal: true,
|
|
74
|
+
terminal: isTerminal,
|
|
75
|
+
writer: o => {
|
|
76
|
+
const options = {}
|
|
77
|
+
if (!o || typeof o !== 'object') return o
|
|
78
|
+
if ('init' in o && 'on' in o && 'run' in o) options.depth = 0
|
|
79
|
+
return inspect(o,options).replace(/\[Object: null prototype\] /g, '')
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
_initHistory(repl, '.cds-repl-history')
|
|
84
|
+
repl.on('exit', () => options.context = repl.context)
|
|
85
|
+
repl.on('exit', () => { if (!transition.started) cds.shutdown() })
|
|
86
|
+
|
|
87
|
+
if (Object.keys(context).length) Object.assign (repl.context, context)
|
|
88
|
+
if (!Object.hasOwn(repl.context, 'Foo')) cds.extend (repl.context) .with ( class {
|
|
89
|
+
get Foo() { return ctx.Foo ??= new cds.entity({ name: 'Foo' }) }; set Foo(v) { ctx.Foo = v }
|
|
90
|
+
get Bar() { return ctx.Bar ??= new cds.entity({ name: 'Bar' }) }; set Bar(v) { ctx.Bar = v }
|
|
91
|
+
get expect() { return ctx.expect ??= cds.test.expect }; set expect(v) { ctx.expect = v }
|
|
92
|
+
} .prototype )
|
|
93
|
+
const ctx={}
|
|
94
|
+
|
|
95
|
+
repl.defineCommand ('run', { action: _run,
|
|
96
|
+
help: 'Runs a cds server from a given CAP project folder, or module name like @capire/bookshop.',
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
repl.defineCommand ('inspect', { action: _inspect,
|
|
100
|
+
help: 'Sets options for util.inspect, e.g. `.inspect .depth=1`.',
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
repl.defineCommand ('ql', { action: transition(_ql),
|
|
104
|
+
help: 'Switch to cql repl mode, evaluating cql queries'
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
repl.displayPrompt()
|
|
108
|
+
}
|
|
109
|
+
|
|
95
110
|
async function _run (project) {
|
|
96
111
|
await require('../bin/serve') ([project], { project, port: 0,
|
|
97
112
|
'with-bindings': true,
|
|
@@ -143,6 +158,164 @@ async function repl ([project], options={}) {
|
|
|
143
158
|
console.log()
|
|
144
159
|
repl.displayPrompt()
|
|
145
160
|
}
|
|
161
|
+
|
|
162
|
+
function _initHistory(repl, file) {
|
|
163
|
+
repl.history = []
|
|
164
|
+
const home = process.env.HOME || process.env.USERPROFILE
|
|
165
|
+
const history = require('path').join(home,file)
|
|
166
|
+
const fs = require('fs')
|
|
167
|
+
fs.readFile(history, 'utf-8', (e, txt) => e || Object.assign(repl.history, txt.split('\n')))
|
|
168
|
+
repl.on('exit', () => fs.writeFile(history, repl.history.join('\n'), () => {}))
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function transition(start) {
|
|
172
|
+
return function () {
|
|
173
|
+
transition.started = true
|
|
174
|
+
this.clearBufferedCommand()
|
|
175
|
+
this.close()
|
|
176
|
+
setImmediate(() => {
|
|
177
|
+
start()
|
|
178
|
+
transition.started = false
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
// CQL Mode
|
|
186
|
+
function _ql() {
|
|
187
|
+
const cqlColors = {
|
|
188
|
+
keyword: BLUE,
|
|
189
|
+
entity: CYAN,
|
|
190
|
+
string: GREEN,
|
|
191
|
+
number: YELLOW,
|
|
192
|
+
token: PINK,
|
|
193
|
+
suggestion: GRAY,
|
|
194
|
+
reset: RESET,
|
|
195
|
+
}
|
|
196
|
+
const highlight = createHighlighter(cds, cqlColors)
|
|
197
|
+
const prompt = `cql${PINK}>${RESET} `
|
|
198
|
+
|
|
199
|
+
repl = require('repl').start ({
|
|
200
|
+
prompt,
|
|
201
|
+
eval: _cql,
|
|
202
|
+
terminal: isTerminal,
|
|
203
|
+
completer: (line) => {
|
|
204
|
+
if (line.startsWith('.'))
|
|
205
|
+
return [Object.keys(repl.commands).map(key => `.${key}`).filter(c => c.startsWith(line)), line]
|
|
206
|
+
return [[], line]
|
|
207
|
+
},
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
_initHistory(repl, '.cds-repl-ql-history')
|
|
211
|
+
repl.on('exit', () => { if (!transition.started) cds.shutdown() })
|
|
212
|
+
|
|
213
|
+
repl.defineCommand ('target', { action: _target,
|
|
214
|
+
help: 'Specify the service to evaluate against'
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
repl.defineCommand ('namespace', { action: _namespace,
|
|
218
|
+
help: 'Specify a default namespace'
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
repl.defineCommand ('js', { action: transition(_js),
|
|
222
|
+
help: 'Switch to js repl mode, evaluating javascript'
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
// Syntax highlighting for input line
|
|
226
|
+
let terminated = false
|
|
227
|
+
function refresh() { setTimeout(() => {if (terminated) return; repl._refreshLine(); }, 0) }
|
|
228
|
+
repl._writeToOutput = function _writeToOutput(str) {
|
|
229
|
+
if (terminated) return
|
|
230
|
+
if (!str.startsWith(prompt)) return repl.output.write(str)
|
|
231
|
+
repl.output.write(prompt + highlight(str.slice(prompt.length)));
|
|
232
|
+
};
|
|
233
|
+
process.stdin.on('keypress', refresh);
|
|
234
|
+
repl.on('exit', () => {
|
|
235
|
+
terminated = true
|
|
236
|
+
process.stdin.off('keypress', refresh)
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
repl.displayPrompt()
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async function _cql(code, context, replResourceName, callback) {
|
|
243
|
+
const { service = cds.db, user } = repl.context
|
|
244
|
+
if (code.trim() === '') return callback()
|
|
245
|
+
cds.context = { user }
|
|
246
|
+
const query = cds.ql(code)
|
|
247
|
+
const result = await service.run(query)
|
|
248
|
+
callback(null, result)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function _target(args) {
|
|
252
|
+
if (!args) return console.info(
|
|
253
|
+
`Specify a service. Available services: ${Object.keys(cds.services).join(', ')}
|
|
254
|
+
Examples:
|
|
255
|
+
.target db
|
|
256
|
+
.target AdminService --roles admin`)
|
|
257
|
+
const [s] = args.split(' ')
|
|
258
|
+
const service = cds.services[s]
|
|
259
|
+
if (!service) throw new Error(`No service with name ${s}. Available services: ${Object.keys(cds.services).join(', ')}`)
|
|
260
|
+
repl.context.service = service
|
|
261
|
+
|
|
262
|
+
const roles = args.match(/(-r|--roles) ([^\s]+)/)?.[2]?.split(',')
|
|
263
|
+
repl.context.user = roles? {roles} : undefined
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function _namespace(ns) {
|
|
267
|
+
cds.model.namespace = ns
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function createHighlighter(cds, colors) {
|
|
272
|
+
const { compiler } = cds
|
|
273
|
+
|
|
274
|
+
const keywords = new Set(['as', 'and', 'or', 'not', 'null'])
|
|
275
|
+
|
|
276
|
+
function category(token) {
|
|
277
|
+
const type = token.type?.toLowerCase()
|
|
278
|
+
if (['number', 'string'].includes(type)) return type;
|
|
279
|
+
|
|
280
|
+
let cat = token.parsedAs?.toLowerCase()
|
|
281
|
+
.replace('ext','').replace('implicit','')
|
|
282
|
+
.replace(/^.*alias/,'alias')
|
|
283
|
+
.replace('from', 'entity').replace('artref', 'entity')
|
|
284
|
+
.replace('item', 'element')
|
|
285
|
+
|
|
286
|
+
if (cat === 'keyword' && !keywords.has(token.text?.trim())) return '_keyword'
|
|
287
|
+
return cat
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return input => {
|
|
291
|
+
const prefix = 'entity __query as '
|
|
292
|
+
const options = { attachTokens: true, messages: [] }
|
|
293
|
+
const { tokenStream: { tokens } } = compiler.$lsp.parse(prefix + input, '<query>.cds', options)
|
|
294
|
+
|
|
295
|
+
const highlighted = []
|
|
296
|
+
let lastToken
|
|
297
|
+
for (const token of tokens) {
|
|
298
|
+
if (lastToken) {
|
|
299
|
+
const start = lastToken.start - prefix.length
|
|
300
|
+
const stop = token.start - prefix.length
|
|
301
|
+
if (start >= 0) {
|
|
302
|
+
const value = input.slice(start, stop)
|
|
303
|
+
let cat = category(lastToken)
|
|
304
|
+
const colored = _color(cat, value)
|
|
305
|
+
highlighted.push(colored)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
lastToken = token
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return highlighted.join('')
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function _color(type, value) {
|
|
315
|
+
const start = colors[type] || ''
|
|
316
|
+
const reset = colors.reset || ''
|
|
317
|
+
return start ? `${start}${value}${reset}` : value
|
|
318
|
+
}
|
|
146
319
|
}
|
|
147
320
|
|
|
148
321
|
if (!module.parent) repl()
|
package/bin/test.js
CHANGED
|
@@ -2,6 +2,4 @@
|
|
|
2
2
|
const {resolve} = require, paths = [ process.cwd(), __dirname ]
|
|
3
3
|
require = id => module.require (resolve(id,{paths})) // eslint-disable-line no-global-assign
|
|
4
4
|
|
|
5
|
-
// FIXME: that check below doesn't work correctly!
|
|
6
|
-
// eslint-disable-next-line cds-dk/no-missing-dependencies
|
|
7
5
|
module.exports = require ('@cap-js/cds-test/bin/chest.js')
|
package/bin/up.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module.exports = Object.assign(up, {
|
|
2
|
-
options: ['--to', '--namespace'],
|
|
2
|
+
options: ['--to', '--namespace', '--overlay'],
|
|
3
3
|
shortcuts: ['-2', '-n'],
|
|
4
4
|
flags: [],
|
|
5
5
|
help: `
|
|
@@ -17,6 +17,12 @@ module.exports = Object.assign(up, {
|
|
|
17
17
|
|
|
18
18
|
Defaults to *k8s* when Helm charts are present and no *mta.yaml* exists, otherwise *cf*.
|
|
19
19
|
|
|
20
|
+
*--overlay* <file> (Cloud Foundry only)
|
|
21
|
+
|
|
22
|
+
Use a deployment overlay.
|
|
23
|
+
|
|
24
|
+
Path to an MTA extension descriptor (*.mtaext*). Optionally omit the *.mtaext* suffix.
|
|
25
|
+
|
|
20
26
|
*-n* | *--namespace* <name>
|
|
21
27
|
|
|
22
28
|
(Kubernetes only) Target namespace. If it does not yet exist it will be
|
|
@@ -27,6 +33,7 @@ module.exports = Object.assign(up, {
|
|
|
27
33
|
*cds up*
|
|
28
34
|
*cds up --to k8s*
|
|
29
35
|
*cds up --to k8s --namespace e2e-tests*
|
|
36
|
+
*cds up --to cf --overlay .deploy/mtaext/eu10.mtaext*
|
|
30
37
|
`})
|
|
31
38
|
|
|
32
39
|
const { execSync } = require('node:child_process')
|
|
@@ -82,7 +89,7 @@ function readStoredChecksum(appName) {
|
|
|
82
89
|
if (exists(checksumFile)) {
|
|
83
90
|
try {
|
|
84
91
|
return fs.readFileSync(checksumFile, 'utf-8').trim()
|
|
85
|
-
} catch
|
|
92
|
+
} catch {
|
|
86
93
|
// Ignore read errors and assume no checksum is stored
|
|
87
94
|
}
|
|
88
95
|
}
|
|
@@ -99,11 +106,24 @@ function writeStoredChecksum(appName, checksum) {
|
|
|
99
106
|
fs.writeFileSync(checksumFile, checksum, 'utf-8')
|
|
100
107
|
}
|
|
101
108
|
|
|
109
|
+
const _mtaextForOverlay = overlay => {
|
|
110
|
+
if (!overlay) return
|
|
111
|
+
if (exists(overlay)) return overlay
|
|
112
|
+
if (!overlay.endsWith('.mtaext')) {
|
|
113
|
+
const file = `${overlay}.mtaext`
|
|
114
|
+
if (exists(file)) return file
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
102
118
|
async function up() {
|
|
103
119
|
const project = readProject()
|
|
104
120
|
const { apps, appName, hasApprouter, hasXsuaa, isMonorepoMicroservice, hasKyma, hasMta, k8s } = project
|
|
105
121
|
const toKyma = cds.cli.options.to === 'k8s' || hasKyma && !hasMta, toCf = !toKyma
|
|
106
122
|
|
|
123
|
+
if (cds.cli.options.overlay && toKyma) {
|
|
124
|
+
console.log(warn(`Ignoring --overlay ${cds.cli.options.overlay} (Cloud Foundry only)`))
|
|
125
|
+
}
|
|
126
|
+
|
|
107
127
|
if (toCf) {
|
|
108
128
|
if (!hasCommand('mbt')) {
|
|
109
129
|
throw `Missing 'mbt' command. Run ${bold('npm install -g mbt')} to install the Cloud MTA Build Tool.`
|
|
@@ -286,8 +306,12 @@ async function up() {
|
|
|
286
306
|
if (exists('mtx/sidecar')) run(`kubectl rollout status deployment ${appName}-sidecar ${nsKubectl} --timeout=8m`)
|
|
287
307
|
} else {
|
|
288
308
|
run('mbt build -t gen --mtar mta.tar')
|
|
309
|
+
const overlayOption = cds.cli.options.overlay
|
|
310
|
+
const overlay = overlayOption && _mtaextForOverlay(overlayOption)
|
|
311
|
+
if (overlayOption && !overlay) console.log(`MTA extension descriptor not found at '${overlayOption}' - continuing with base mta.yaml`)
|
|
312
|
+
const ext = overlay ? `-e ${overlay}` : ''
|
|
289
313
|
const retries = process.env.CI ? '' : '--retries 0'
|
|
290
|
-
run(`cf deploy gen/mta.tar -f ${retries}`)
|
|
314
|
+
run(`cf deploy gen/mta.tar ${ext} -f ${retries}`)
|
|
291
315
|
}
|
|
292
316
|
}
|
|
293
317
|
|
package/bin/util/containerize.js
CHANGED
|
@@ -133,9 +133,7 @@ async function executeModules(modules, options) {
|
|
|
133
133
|
run(`docker inspect --format='{{index .RepoTags 0}}' ${m.image}:${m.checksum}`, { silent: true })
|
|
134
134
|
console.log(info(`image ${m.image}:${m.checksum} already exists – skipping build and push`))
|
|
135
135
|
continue
|
|
136
|
-
} catch
|
|
137
|
-
// ignore
|
|
138
|
-
}
|
|
136
|
+
} catch { /* ignore */ }
|
|
139
137
|
}
|
|
140
138
|
|
|
141
139
|
for (const cmd of m.buildCmd) run(cmd)
|