@sap/cds-dk 4.6.4 → 4.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +68 -1
  2. package/bin/activate.js +5 -1
  3. package/bin/add.js +2 -1
  4. package/bin/bind.js +121 -48
  5. package/bin/cds.js +8 -0
  6. package/bin/env.js +0 -2
  7. package/bin/repl.js +5 -2
  8. package/bin/watch.js +10 -7
  9. package/lib/bind/bindingResolver.js +26 -14
  10. package/lib/bind/bindingUtil.js +144 -0
  11. package/lib/bind/cfKeyProvider.js +2 -80
  12. package/lib/bind/kubernetesCredentialsProvider.js +157 -0
  13. package/lib/bind/userCdsrcJsonStorageProvider.js +7 -7
  14. package/lib/client/activate.js +16 -13
  15. package/lib/client/base_command.js +5 -1
  16. package/lib/client/helper/cf_helper.js +5 -2
  17. package/lib/client/params.js +16 -2
  18. package/lib/client/settings_manager.js +15 -8
  19. package/lib/compile/openapi/csdl2openapi.js +6 -8
  20. package/lib/compile/to-xsuaa.js +0 -14
  21. package/lib/import/utils/MetadataConverter.js +6 -4
  22. package/lib/import/utils/MultiMap.js +1 -0
  23. package/lib/import/v2parser.js +150 -64
  24. package/lib/import/v4parser.js +118 -56
  25. package/lib/index.js +6 -5
  26. package/lib/init/constants.js +11 -5
  27. package/lib/init/index.js +14 -7
  28. package/lib/init/template/_merging/existences.js +161 -0
  29. package/lib/init/template/approuter/files/default-env.json +9 -0
  30. package/lib/init/template/approuter/files/mta.yaml.hbs +48 -0
  31. package/lib/init/template/approuter/files/package.json +9 -0
  32. package/lib/init/template/approuter/files/xs-app.json +8 -0
  33. package/lib/init/template/approuter/index.js +66 -0
  34. package/lib/init/template/auditlog/files/dependencies.package.json +5 -0
  35. package/lib/init/template/auditlog/files/mta.yaml.hbs +13 -0
  36. package/lib/init/template/auditlog/index.js +38 -0
  37. package/lib/init/template/cf-manifest/index.js +1 -1
  38. package/lib/init/template/data/index.js +18 -6
  39. package/lib/init/template/hana/files/common/undeploy.json +5 -0
  40. package/lib/init/template/hana/files/java/package.json +3 -0
  41. package/lib/init/template/hana/files/mta.yaml.hbs +42 -0
  42. package/lib/init/template/hana/files/nodejs/dependencies.package.json +5 -0
  43. package/lib/init/template/hana/index.js +53 -24
  44. package/lib/init/template/java/index.js +1 -5
  45. package/lib/init/template/lint/index.js +17 -1
  46. package/lib/init/template/mta/files/mta.yaml.hbs +27 -216
  47. package/lib/init/template/mta/index.js +25 -17
  48. package/lib/init/template/mtx/files/cds.package.json +24 -0
  49. package/lib/init/template/mtx/files/dependencies.package.json +5 -0
  50. package/lib/init/template/mtx/files/mta.yaml.hbs +74 -0
  51. package/lib/init/template/mtx/files/xs-security.json.hbs +23 -9
  52. package/lib/init/template/mtx/index.js +126 -29
  53. package/lib/init/template/templateBase.js +14 -0
  54. package/lib/init/template/xsuaa/files/cds.package.json +9 -0
  55. package/lib/init/template/xsuaa/files/dependencies.package.json +6 -0
  56. package/lib/init/template/xsuaa/files/mta.yaml.hbs +17 -0
  57. package/lib/init/template/xsuaa/index.js +75 -0
  58. package/lib/init/util/fsUtil.js +18 -4
  59. package/lib/init/util/logger.js +3 -1
  60. package/lib/init/util/mvnArchetypeUtil.js +3 -1
  61. package/lib/init/{template/mta → util}/projectReader.js +26 -11
  62. package/lib/init/util/templateUtil.js +267 -3
  63. package/lib/lint/Linter.js +1 -2
  64. package/lib/lint/io.js +3 -12
  65. package/lib/util/exec.d.ts +54 -0
  66. package/lib/util/exec.js +210 -0
  67. package/lib/watch/livereload-server.js +43 -31
  68. package/lib/watch/node-watch.js +46 -20
  69. package/lib/watch/open.js +1 -1
  70. package/npm-shrinkwrap.json +3382 -9106
  71. package/package.json +8 -9
  72. package/readme.md +1 -1
  73. package/lib/init/template/mtx/package/cds.package.json +0 -29
  74. package/lib/init/template/mtx/package/dependencies.package.json +0 -9
@@ -0,0 +1,210 @@
1
+ const { spawn, spawnSync } = require('child_process');
2
+
3
+ const cds = require('@sap/cds/lib');
4
+ const log = cds.log('exec');
5
+
6
+ function* commandIterator(strs, ...args) {
7
+ for (let i=0; i<strs.length; i++) {
8
+ const str = strs[i];
9
+ const regex = /((\s+)|(["'])|$)/g;
10
+ let match;
11
+ let lastIndex = 0;
12
+ do {
13
+ match = regex.exec(str);
14
+ if (match.index > lastIndex) {
15
+ yield { type: 'WORD', value: str.substr(lastIndex, match.index - lastIndex) };
16
+ }
17
+ if (match[2]) {
18
+ yield { type: 'SPACE', value: match[2] };
19
+ }
20
+ if (match[3]) {
21
+ yield { type: 'QUOTE', value: match[3] }
22
+ }
23
+ lastIndex = match.index + match[0].length;
24
+ } while (lastIndex < str.length);
25
+
26
+ if (args[i]) {
27
+ if (Array.isArray(args[i])) {
28
+ let sep = false;
29
+ for (const value of args[i]) {
30
+ if (sep) yield { type: 'SPACE', value: ' '};
31
+ yield { type: 'WORD', value };
32
+ sep = true;
33
+ }
34
+ } else {
35
+ yield { type: 'WORD', value: args[i] };
36
+ }
37
+ }
38
+ }
39
+ }
40
+
41
+ function processTemplate(strs, args) {
42
+ let quote;
43
+ const result = [];
44
+ let index = 0;
45
+
46
+ const append = str => result[index] = (result[index] || '') + str;
47
+ const push = () => { if (index < result.length) index++; };
48
+
49
+ for (const token of commandIterator(strs, args)) {
50
+ switch (token.type) {
51
+ case 'WORD':
52
+ append(token.value);
53
+ break;
54
+ case 'SPACE':
55
+ if (quote) {
56
+ append(token.value);
57
+ } else {
58
+ push();
59
+ }
60
+ break;
61
+ case 'QUOTE':
62
+ if (quote === token.value) {
63
+ quote = undefined;
64
+ } else if (quote) {
65
+ append(token.value);
66
+ } else {
67
+ quote = token.value;
68
+ }
69
+ break;
70
+ }
71
+ }
72
+
73
+ return result;
74
+ }
75
+
76
+ function fetchOptions(cmd) {
77
+ const { options, args } = cmd;
78
+ if (typeof args[0] === 'object' && !Array.isArray(args[0])) {
79
+ const newOptions = args[0];
80
+ cmd.options = {...options, ...newOptions};
81
+ cmd.args = args.slice(1);
82
+ return true;
83
+ } else {
84
+ return false;
85
+ }
86
+ }
87
+
88
+ function exec_func(...args) {
89
+ const cmd = { options: {
90
+ log,
91
+ acceptStatuses: [ 0 ],
92
+ stdio: 'inherit',
93
+ shell: false,
94
+ encoding: 'utf-8'
95
+ }, args };
96
+
97
+ let hasOptions = false;
98
+ while (fetchOptions(cmd)) hasOptions = true;
99
+ if (hasOptions && cmd.args.length === 0) {
100
+ return (...args) => exec_func(cmd.options, ...args);
101
+ }
102
+
103
+ if (Array.isArray(cmd.args[0])) cmd.args = processTemplate(...cmd.args);
104
+ return cmd.options.executer(cmd.options, ...cmd.args);
105
+ }
106
+
107
+ function getExecOptions(options) {
108
+ const execOptions = {...options};
109
+ [ 'acceptStatuses', 'appendEnv', 'exec', 'log', 'result' ].forEach( name => delete execOptions[name] );
110
+
111
+ const { appendEnv, result } = options;
112
+ if (appendEnv) {
113
+ execOptions.env = {...(options.env || process.env),...appendEnv};
114
+ }
115
+
116
+ switch (result) {
117
+ case 'stdout':
118
+ execOptions.stdio = [ 'inherit', 'pipe', 'inherit' ];
119
+ break;
120
+ case 'stderr':
121
+ execOptions.stdio = [ 'inherit', 'inherit', 'pipe' ];
122
+ break;
123
+ case undefined:
124
+ break;
125
+
126
+ default:
127
+ throw new Error `Unexpected "result" option: "${result}"`;
128
+ }
129
+
130
+ return execOptions;
131
+ }
132
+
133
+ function toString(a) {
134
+ return typeof a === 'string' ? a : Buffer.from(a).toString('utf-8');
135
+ }
136
+
137
+ function processResult(result, options) {
138
+ switch (options.result) {
139
+ case 'stdout':
140
+ return toString(result.stdout);
141
+ case 'stderr':
142
+ return toString(result.stderr);
143
+ case undefined:
144
+ return result;
145
+
146
+ default:
147
+ throw new Error(`Unexpected "result" option: "${result}"`);
148
+ }
149
+ }
150
+
151
+
152
+ function executeSync(options = {}, command, ...args) {
153
+ const { acceptStatuses } = options;
154
+ const execOptions = getExecOptions(options);
155
+
156
+ log.debug(`exec: ${command} ${args.map( v => v.match(/\s/) ? `"${v}"` : v ).join(' ')}`);
157
+ const result = spawnSync(command, args, execOptions);
158
+
159
+ if (acceptStatuses !== '*' && acceptStatuses.indexOf(result.status) === -1) throw new Error(`Command returned with status code ${result.status}`);
160
+ return processResult(result, options);
161
+ }
162
+
163
+ async function executeAsync(options = {}, command, ...args) {
164
+ const { acceptStatuses } = options;
165
+ const execOptions = getExecOptions(options);
166
+
167
+ // ctx.log(`exec: ${command} ${args.map( v => v.match(/\s/) ? `"${v}"` : v ).join(' ')}`);
168
+
169
+
170
+ const result = await new Promise( (resolve, reject) => {
171
+ const child = spawn(command, args, execOptions);
172
+ const stdoutChunks = [];
173
+ const stderrChunks = [];
174
+
175
+ if (child.stdout) {
176
+ child.stdout.on('data', data => {
177
+ stdoutChunks.push(Buffer.from(data));
178
+ });
179
+ }
180
+
181
+ if (child.stderr) {
182
+ child.stderr.on('data', data => {
183
+ stderrChunks.push(Buffer.from(data));
184
+ });
185
+ }
186
+
187
+ const exit = status => {
188
+ if (acceptStatuses !== '*' && acceptStatuses.indexOf(status) === -1) reject(new Error(`Command returned with status code ${status}`))
189
+ else resolve({
190
+ status,
191
+ stdout: child.stdout ? Buffer.concat(stdoutChunks).toString("utf-8") : undefined,
192
+ stderr: child.stderr ? Buffer.concat(stderrChunks).toString("utf-8") : undefined
193
+ })
194
+ };
195
+
196
+ child.on('close', exit);
197
+ child.on('exit', exit);
198
+ });
199
+
200
+ return processResult(result, options);
201
+ }
202
+
203
+ const exec = exec_func({ executer: executeAsync });
204
+ const execSync = exec_func({ executer: executeSync });
205
+
206
+ module.exports = {
207
+ exec,
208
+ execSync,
209
+ $ : execSync
210
+ }
@@ -14,46 +14,58 @@ module.exports = class LiveReload {
14
14
  app.use('/livereload.js', express.static(require.resolve('livereload-js')))
15
15
 
16
16
  return new Promise ((resolve, reject) => {
17
- // start server
18
- const server = app.listen (port,'localhost')
19
- server.on('error', reject)
20
- server.on('listening', async () => {
17
+ let server
18
+ const listening = async () => {
21
19
  const {address,port} = server.address()
20
+ startWs(server)
22
21
  this.url = await livereloadURL(address, port)
23
22
  debug && debug(`[cds] - live reload available at ${this.url}`)
24
23
  resolve(this.url)
24
+ }
25
+ // start server
26
+ server = app.listen (port,'localhost')
27
+ server.once('listening', listening)
28
+ server.on('error', e => {
29
+ // try once more w/ ephemeral port 0
30
+ if (e.code === 'EADDRINUSE') {
31
+ server = app.listen (0,'localhost')
32
+ server.once('listening', listening)
33
+ server.on('error', reject)
34
+ }
35
+ else reject(e)
25
36
  })
26
37
 
27
38
  // start websocket
28
- this.wsServer = new ws.Server({ server })
29
- this.wsServer.on('connection', (socket) => {
30
- debug && debug('Client connected')
31
- socket.on('message', (msg) => {
32
- debug && debug('Client message', msg)
33
- const request = JSON.parse(msg)
39
+ const startWs = (server) => {
40
+ this.wsServer = new ws.Server({ server })
41
+ this.wsServer.on('connection', (socket) => {
42
+ debug && debug('Client connected')
43
+ socket.on('message', (msg) => {
44
+ debug && debug('Client message', msg)
45
+ const request = JSON.parse(msg)
34
46
 
35
- if (request.command === 'hello') {
36
- debug && debug('Client handshake')
37
- const data = JSON.stringify({
38
- command: 'hello',
39
- protocols: [
40
- 'http://livereload.com/protocols/official-7',
41
- 'http://livereload.com/protocols/official-8',
42
- 'http://livereload.com/protocols/official-9',
43
- 'http://livereload.com/protocols/2.x-origin-version-negotiation',
44
- 'http://livereload.com/protocols/2.x-remote-control',
45
- ],
46
- serverName: 'cds-watch',
47
- })
48
- socket.send(data)
49
- } else if (request.command === 'info') {
50
- debug && debug('Server received client data. Not sending response.')
51
- }
47
+ if (request.command === 'hello') {
48
+ debug && debug('Client handshake')
49
+ const data = JSON.stringify({
50
+ command: 'hello',
51
+ protocols: [
52
+ 'http://livereload.com/protocols/official-7',
53
+ 'http://livereload.com/protocols/official-8',
54
+ 'http://livereload.com/protocols/official-9',
55
+ 'http://livereload.com/protocols/2.x-origin-version-negotiation',
56
+ 'http://livereload.com/protocols/2.x-remote-control',
57
+ ],
58
+ serverName: 'cds-watch',
59
+ })
60
+ socket.send(data)
61
+ } else if (request.command === 'info') {
62
+ debug && debug('Server received client data. Not sending response.')
63
+ }
64
+ })
52
65
  })
53
- })
54
- this.wsServer.on('close', () => { debug && debug('Socket closed') })
55
- this.wsServer.on('error', (e) => debug && debug(e))
56
-
66
+ this.wsServer.on('close', () => { debug && debug('Socket closed') })
67
+ this.wsServer.on('error', (e) => debug && debug(e))
68
+ }
57
69
  })
58
70
  }
59
71
 
@@ -1,3 +1,5 @@
1
+ const DEBUG = /\bwatch\b/.test(process.env.DEBUG) && console.debug
2
+
1
3
  module.exports = ({ cwd, script, ext, ignore=RegExp(), env={}, delay=200, options={} })=>{
2
4
 
3
5
  let child
@@ -5,19 +7,23 @@ module.exports = ({ cwd, script, ext, ignore=RegExp(), env={}, delay=200, option
5
7
  // Phoning home...
6
8
  const EventEmitter = require('events')
7
9
  const emitter = new EventEmitter
8
- process.on('exit', () => { gracefullyKill (child); emitter.emit('quit') })
10
+ process.on('exit', () => { _kill (child); emitter.emit('quit') })
9
11
 
10
12
  // Re-starting child process...
11
13
  const { fork } = require('child_process')
12
- const restart = (evt, name)=>{
13
- gracefullyKill (child, () => {
14
+ const restart = _coalesceEvents(delay, (events) => {
15
+ DEBUG && DEBUG('>> Events:', events)
16
+ _kill (child, () => {
14
17
  const execArgv = [...process.execArgv]
15
- if (evt && evt.startsWith('--')) execArgv.push(evt)
16
- if (name && name.startsWith('--')) execArgv.push(name)
18
+ events.forEach(evt => {
19
+ if (evt.type && evt.type.startsWith('--')) execArgv.push(evt.type)
20
+ if (evt.name && evt.name.startsWith('--')) execArgv.push(evt.name)
21
+ })
17
22
  if (options.debug) execArgv.push('--inspect')
18
23
  if (options['debug-brk']) execArgv.push('--inspect-brk')
19
- const files = evt === "update" ? [ name ] : undefined
20
- emitter.emit ('restart', files)
24
+
25
+ const updated = events.filter(evt => evt.type && evt.type === 'update').map(evt => evt.name)
26
+ emitter.emit ('restart', updated)
21
27
  child = fork (script, { cwd, env, stdio:'inherit', execArgv }, (err)=>{
22
28
  if (err) console.error (err)
23
29
  })
@@ -26,14 +32,16 @@ module.exports = ({ cwd, script, ext, ignore=RegExp(), env={}, delay=200, option
26
32
  emitter.emit('message', msg)
27
33
  })
28
34
  })
29
- }
35
+
36
+ })
37
+
30
38
  restart()
31
39
 
32
40
  // Watching for touched files...
33
41
  const watch = require('node-watch')
34
42
  const include = RegExp(`\\.(${ext.replace(/,/g,'|')})$`)
35
43
  const filter = f => !ignore.test(f) && include.test(f)
36
- watch (process.cwd(),{ recursive:true, filter, delay }, restart)
44
+ watch (process.cwd(),{ recursive:true, filter, delay:0 }, restart)
37
45
 
38
46
  // Live commands...
39
47
  const readline = require('readline')
@@ -44,12 +52,12 @@ module.exports = ({ cwd, script, ext, ignore=RegExp(), env={}, delay=200, option
44
52
  else if (input === 'break' || input === 'brk') restart('--inspect-brk')
45
53
  else if (input === 'debug-brk') restart('--inspect-brk')
46
54
  else if (input === 'ps') ps(child,env)
47
- else if (input === 'bye' || input.match(/^n$/i)) { gracefullyKill (child, () => process.exit()) }
55
+ else if (input === 'bye' || input.match(/^n$/i)) { _kill (child, () => process.exit()) }
48
56
  else console.log ('?\n')
49
57
  })
50
58
 
51
59
  emitter.restart = restart
52
- emitter.stop = () => { gracefullyKill(child, () => false); child = undefined; }
60
+ emitter.stop = () => { _kill(child, () => false); child = undefined; }
53
61
  emitter.setEnv = (name, value) => {
54
62
  if (typeof value === "undefined") {
55
63
  delete env[name]
@@ -67,14 +75,32 @@ const ps = (child,env) => console.log (`\x1b[32m
67
75
  \x1b[0m`
68
76
  )
69
77
 
70
- // On Windows, kill() abruptly kills the child process (https://nodejs.org/api/child_process.html#subprocesskillsignal)
71
- // Send a custom message an wait a bit to give cleanup code a chance to execute.
72
- const isWindows = (process.platform === 'win32')
73
- const waitTime = parseInt(process.env.CDS_WATCH_KILL_DELAY) || (isWindows ? 500 : 0)
74
- function gracefullyKill (proc, fn=()=>{}) {
75
- if (proc) {
76
- proc.send({ exit: true }, () => { /* ignore */ })
77
- setTimeout(()=> { proc.kill(); fn()}, waitTime)
78
+ // reduces mutiple node-watch events in a time frame into a bulk
79
+ function _coalesceEvents(delay, fn) {
80
+ let timer, cache = []
81
+ function handle() {
82
+ fn(cache)
83
+ timer = null
84
+ cache = []
85
+ }
86
+ return function(type, name) { // node-watch callback signature
87
+ cache.push({type, name})
88
+ if (!timer) {
89
+ timer = setTimeout(handle, delay);
90
+ }
91
+ }
92
+ }
93
+
94
+ function _kill (proc, fn=()=>{}) {
95
+ if (!proc) return fn()
96
+
97
+ // On Windows, child_process.kill() abruptly kills the process (https://nodejs.org/api/child_process.html#subprocesskillsignal)
98
+ // Server has cleanup code to run, so send a custom message and give it a bit time.
99
+ if (process.platform === 'win32') {
100
+ proc.send({ exit: true }, () => {})
101
+ const waitTime = parseInt(process.env.CDS_WATCH_KILL_DELAY) || 500
102
+ setTimeout(()=> { proc.kill(); fn() }, waitTime)
103
+ } else {
104
+ proc.kill(); fn()
78
105
  }
79
- else fn()
80
106
  }
package/lib/watch/open.js CHANGED
@@ -19,7 +19,7 @@ function _open(args) {
19
19
  // http://stackoverflow.com/q/1480971/3191, but see below for Windows.
20
20
  let platform = process.platform
21
21
 
22
- // Attempt to detect Windows Subystem for Linux (WSL). WSL itself as Linux (which works in most cases), but in
22
+ // Attempt to detect Windows Subsystem for Linux (WSL). WSL itself as Linux (which works in most cases), but in
23
23
  // this specific case we need to treat it as actually being Windows. The 'Windows-way' of opening things through
24
24
  // cmd.exe works just fine here, whereas using xdg-open does not, since there is no X Windows in WSL.
25
25
  if (platform === 'linux' && os.release().indexOf('Microsoft') !== -1) {