@sap/cds-dk 7.8.1 → 7.9.1

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 (90) hide show
  1. package/bin/add.js +28 -7
  2. package/bin/bind.js +64 -21
  3. package/bin/build.js +35 -3
  4. package/bin/cds.js +32 -13
  5. package/bin/compile/index.js +44 -3
  6. package/bin/completion/completionFs.js +61 -0
  7. package/bin/completion/index.js +212 -0
  8. package/bin/completion/scripts/cds-bash.sh +17 -0
  9. package/bin/completion/scripts/cds-ps.ps1 +18 -0
  10. package/bin/completion/scripts/cds-zsh.sh +12 -0
  11. package/bin/help.js +8 -2
  12. package/bin/init.js +34 -4
  13. package/bin/subscribe.js +5 -1
  14. package/bin/watch.js +4 -2
  15. package/lib/bind/KubernetesApiAccessor.js +3 -2
  16. package/lib/bind/bindingResolver.js +3 -2
  17. package/lib/bind/bindingUtil.js +10 -8
  18. package/lib/bind/cfKeyProvider.js +58 -153
  19. package/lib/bind/index.js +46 -36
  20. package/lib/bind/kubernetesCredentialsProvider.js +6 -5
  21. package/lib/bind/userCdsrcJsonStorageProvider.js +25 -3
  22. package/lib/build/buildTaskEngine.js +1 -1
  23. package/lib/build/provider/hana/index.js +1 -1
  24. package/lib/build/provider/hana/template/undeploy.json +3 -1
  25. package/lib/build/provider/nodejs/index.js +1 -1
  26. package/lib/compile/openapi/csdl2openapi.js +4 -0
  27. package/lib/compile/openapi/index.js +5 -0
  28. package/lib/deploy/to-hana/hana.js +25 -7
  29. package/lib/env/schemas/cds-rc.js +29 -2
  30. package/lib/import/odata/v4parser.js +78 -25
  31. package/lib/init/constants.js +2 -1
  32. package/lib/init/index.js +18 -13
  33. package/lib/init/merge.js +1 -0
  34. package/lib/init/mvn.js +8 -47
  35. package/lib/init/plugin.js +0 -4
  36. package/lib/init/projectReader.js +17 -10
  37. package/lib/init/template/approuter/files/values.yaml.hbs +6 -12
  38. package/lib/init/template/approuter/index.js +1 -7
  39. package/lib/init/template/completion/completionSetup.js +184 -0
  40. package/lib/init/template/completion/index.js +22 -0
  41. package/lib/init/template/data/as-csv.js +25 -17
  42. package/lib/init/template/data/as-json.js +115 -30
  43. package/lib/init/template/data/index.js +28 -15
  44. package/lib/init/template/hana/files/undeploy.json +2 -1
  45. package/lib/init/template/helm/files/chart/values.schema.json +1 -1
  46. package/lib/init/template/helm/files/content-deployment/Chart.yaml +0 -2
  47. package/lib/init/template/helm/files/content-deployment/values.schema.json +0 -1
  48. package/lib/init/template/helm/files/service-instance/Chart.yaml +0 -2
  49. package/lib/init/template/helm/files/service-instance/values.schema.json +0 -1
  50. package/lib/init/template/helm/files/web-application/Chart.yaml +1 -3
  51. package/lib/init/template/helm/files/web-application/templates/_helpers.tpl +23 -0
  52. package/lib/init/template/helm/files/web-application/templates/deployment.yaml +3 -29
  53. package/lib/init/template/helm/files/web-application/values.schema.json +17 -2
  54. package/lib/init/template/helm/files/web-application/values.yaml +10 -2
  55. package/lib/init/template/helm-unified-runtime/files/templates/NOTES.txt +2 -0
  56. package/lib/init/template/helm-unified-runtime/files/templates/_deployment_helpers.tpl +10 -2
  57. package/lib/init/template/helm-unified-runtime/files/values.schema.json +1 -1
  58. package/lib/init/template/http/index.js +150 -104
  59. package/lib/init/template/kafka/index.js +6 -0
  60. package/lib/init/template/lint/index.js +12 -13
  61. package/lib/init/template/mta/files/mta.yaml.hbs +1 -1
  62. package/lib/init/template/multitenancy/files/mtxs-configmap-java.yaml +1 -1
  63. package/lib/init/template/multitenancy/files/values.yaml.hbs +0 -5
  64. package/lib/init/template/multitenancy/index.js +13 -5
  65. package/lib/init/template/nodejs/files/.vscode/settings.json +11 -0
  66. package/lib/init/template/nodejs/files/eslint.config.js +13 -0
  67. package/lib/init/template/nodejs/files/package.json.hbs +3 -1
  68. package/lib/init/template/sample/index.js +18 -20
  69. package/lib/init/template/tiny-sample/index.js +1 -2
  70. package/lib/init/template/typer/index.js +2 -2
  71. package/lib/lint/Linter.js +133 -55
  72. package/lib/lint/checks.js +5 -6
  73. package/lib/lint/io.js +119 -10
  74. package/lib/mtx/auth_manager.js +5 -5
  75. package/lib/mtx/params.js +4 -2
  76. package/lib/mtx/settings_manager.js +1 -1
  77. package/lib/mtx/tenant_action.js +12 -2
  78. package/lib/mtx/util/logging.js +10 -4
  79. package/lib/util/axios.js +2 -1
  80. package/lib/util/cf.js +32 -37
  81. package/lib/util/fs.js +1 -1
  82. package/lib/util/git.js +1 -1
  83. package/lib/util/term.js +1 -1
  84. package/{bin → lib/watch}/watched.js +2 -2
  85. package/npm-shrinkwrap.json +317 -293
  86. package/package.json +2 -2
  87. package/lib/init/template/approuter/files/approuter-mtxs-configmap.yaml +0 -19
  88. package/lib/init/template/nodejs/files/.eslintrc +0 -26
  89. /package/lib/init/template/{approuter/files/mtxs-configmap-java.yaml → multitenancy/files/mtxs-configmap-java-approuter.yaml} +0 -0
  90. /package/lib/init/template/{approuter/files/mtxs-configmap-nodejs.yaml → multitenancy/files/mtxs-configmap-nodejs-approuter.yaml} +0 -0
package/lib/lint/io.js CHANGED
@@ -11,7 +11,7 @@ module.exports = {
11
11
  * @param {*} configPath config file path to read from
12
12
  * @returns config file contents
13
13
  */
14
- async readEslintConfig(configPath) {
14
+ async readEslintConfigLegacy(configPath) {
15
15
  let config;
16
16
  const configFile = path.basename(configPath);
17
17
  const configString = await read(configPath, "utf8");
@@ -33,7 +33,7 @@ module.exports = {
33
33
  config = await readJSONC(configPath);
34
34
  break;
35
35
  case "package.json":
36
- config = JSON.parse(await read(configPath, "utf8"));
36
+ config = await read(configPath, "json");
37
37
  if ("eslintConfig" in config) {
38
38
  config = config["eslintConfig"];
39
39
  } else {
@@ -46,16 +46,68 @@ module.exports = {
46
46
  return config;
47
47
  },
48
48
 
49
+ /**
50
+ * Reads ESLint config contents from file
51
+ * @param {*} configPath config file path to read from
52
+ * @returns config file contents
53
+ */
54
+ async readEslintConfig(configPath) {
55
+ let config;
56
+ const configFile = path.basename(configPath);
57
+ switch (configFile) {
58
+ case "eslint.config.js":
59
+ case "eslint.config.cjs":
60
+ case "eslint.config.mjs":
61
+ config = this.safeRequire(configPath)
62
+ break;
63
+ default:
64
+ break;
65
+ }
66
+ return config;
67
+ },
68
+
49
69
  /**
50
70
  * Checks ESLint config file contents for locally installed
51
71
  * plugin with:
52
72
  * (1) All CDS recommended rules "on"
53
- * (2) Option "root": false to stop further config merging
54
- * (3) Add's custom rule example 'no-entity-moo' if requested
73
+ * (2) Add's custom rule example 'no-entity-moo' if requested
55
74
  * @param {*} configPath ESLint config file
56
75
  * @returns {void}
57
76
  */
58
77
  async sanitizeEslintConfig(configIn, customRuleExample = false, logger = console) {
78
+ let configContents;
79
+ const isFilePath = typeof configIn === "string";
80
+ if (isFilePath) {
81
+ configContents = this.safeRequire(configIn); // TODO error handling invalid json
82
+ }
83
+
84
+ configContents ??= { ...configIn };
85
+
86
+ if (customRuleExample) {
87
+ const ruleName = "no-entity-moo";
88
+ const ruleValue = 2;
89
+ if ("rules" in configContents) {
90
+ configContents["rules"][ruleName] = ruleValue;
91
+ } else {
92
+ configContents["rules"] = { [ruleName]: ruleValue };
93
+ }
94
+ }
95
+ if (isFilePath) {
96
+ await this.writeEslintConfig(configIn, logger);
97
+ }
98
+ return configContents;
99
+ },
100
+
101
+ /**
102
+ * Checks ESLint config file contents for locally installed
103
+ * plugin with:
104
+ * (1) All CDS recommended rules "on"
105
+ * (2) Option "root": false to stop further config merging
106
+ * (3) Add's custom rule example 'no-entity-moo' if requested
107
+ * @param {*} configPath ESLint config file
108
+ * @returns {void}
109
+ */
110
+ async sanitizeEslintConfigLegacy(configIn, customRuleExample = false, logger = console) {
59
111
  let configContents = {};
60
112
  const isFilePath = typeof configIn === "string";
61
113
  if (isFilePath) {
@@ -93,19 +145,20 @@ module.exports = {
93
145
  }
94
146
  }
95
147
  if (isFilePath) {
96
- await this.writeEslintConfig(configIn, configContents, logger);
148
+ await this.writeEslintConfigLegacy(configIn, configContents, logger);
97
149
  }
98
150
  return configContents;
99
151
  },
100
152
 
153
+
101
154
  /**
102
155
  * Writes ESLint config contents to file
103
156
  * @param {*} configContents config file contents
104
157
  * @param {*} configPath config file path to write to
105
158
  * @returns {Promise<void>}
106
159
  */
107
- async writeEslintConfig(configPath, configContents, logger=console) {
108
- const configType = "recommended";
160
+ async writeEslintConfigLegacy(configPath, configContents, logger = console) {
161
+ const configType = "recommended-legacy";
109
162
  const extendsValue = `plugin:@sap/cds/${configType}`;
110
163
  let contents = {};
111
164
  let configFile = path.basename(configPath);
@@ -114,7 +167,7 @@ module.exports = {
114
167
  case ".eslintrc.js":
115
168
  case ".eslintrc.cjs":
116
169
  try {
117
- configContents = await this.readEslintConfig(configPath);
170
+ configContents = await this.readEslintConfigLegacy(configPath);
118
171
  } catch (err) {
119
172
  configContents = {};
120
173
  }
@@ -146,7 +199,7 @@ module.exports = {
146
199
  break;
147
200
  case "package.json":
148
201
  if (exists(configPath)) {
149
- contents = JSON.parse(await read(configPath, "utf8"));
202
+ contents = await read(configPath, "json");
150
203
  } else {
151
204
  contents = {};
152
205
  }
@@ -162,6 +215,54 @@ module.exports = {
162
215
  }
163
216
  },
164
217
 
218
+ /**
219
+ * Suggest ESLint config contents to file
220
+ * @param {*} configContents config file contents
221
+ * @param {*} configPath config file path to write to
222
+ * @returns {Promise<void>}
223
+ */
224
+ async writeEslintConfig(configPath, logger = console) {
225
+ let configFile = path.basename(configPath);
226
+ switch (configFile) {
227
+ case "eslint.config.js":
228
+ case "eslint.config.cjs":
229
+ case "eslint.config.mjs":
230
+ if (exists(configPath)) {
231
+ logger.log(
232
+ `${term.warn(
233
+ `\n\nPlease add the following to your "${configFile}":` +
234
+ '\n\n 1. At the top of your file, import the following:')}` +
235
+ `\n\n ${term.bold('const cds = require(\'@sap/eslint-plugin-cds\')')}` +
236
+ `${term.warn(
237
+ '\n\n 2. In your export, add the following to the top of the array:')}` +
238
+ `\n\n ${term.bold('cds.configs.recommended')}`
239
+ );
240
+ } else {
241
+ await write(configPath, `
242
+ const cds = require('@sap/eslint-plugin-cds')
243
+
244
+ module.exports = [
245
+ cds.configs.recommended,
246
+ {
247
+ plugins: {
248
+ "@sap/cds": cds
249
+ },
250
+ "files": [
251
+ ...cds.configs.recommended.files
252
+ ],
253
+ "rules": {
254
+ ...cds.configs.recommended.rules
255
+ }
256
+ }
257
+ ]
258
+ `)
259
+ }
260
+ break;
261
+ default:
262
+ break;
263
+ }
264
+ },
265
+
165
266
  /**
166
267
  * Read VS Code settings from file
167
268
  * @param {*} settingsPath settings file path to read from
@@ -204,7 +305,7 @@ module.exports = {
204
305
  * (3) "Global" case adds configFile and removes rulePaths
205
306
  * @param {*} settingsPath VS Code settings file
206
307
  */
207
- async sanitizeVscodeSettings(projectPath, settingsPath, lintType, lintFileTypes, customRuleExample = false) {
308
+ async sanitizeVscodeSettings(settingsPath, lintType, lintFileTypes, customRuleExample = false) {
208
309
  let settings = (await this.readVscodeSettings(settingsPath)) || {};
209
310
  const rulePaths = [path.join(".eslint/rules")];
210
311
 
@@ -253,4 +354,12 @@ module.exports = {
253
354
  }
254
355
  return userOpts;
255
356
  },
357
+
358
+ safeRequire(module) {
359
+ try {
360
+ return require(module)
361
+ } catch (e) {
362
+ return null
363
+ }
364
+ }
256
365
  };
@@ -65,26 +65,26 @@ async function retrieveTokenOrPasscodeUrl(params) {
65
65
  DEBUG?.(`Getting authentication token or passcode URL from ${method.toUpperCase()} ${params.obfuscateQueryParams(url)}`);
66
66
  response = await axios[method](url, data);
67
67
  } catch (e) {
68
- error = e;
69
- if (error.status === 404) { // may represent 405 in case of wrong method
68
+ if (e.status === 404) { // may represent 405 in case of wrong method
70
69
  continue reqParams;
71
70
  } else if (params.has('refreshToken')) {
72
71
  DEBUG?.('Discarding invalid refresh token');
73
72
  params.delete('refreshToken');
74
73
  } else {
74
+ error = e;
75
75
  break reqParams;
76
76
  }
77
77
  }
78
- } while (!(response || error));
78
+ } while (!response);
79
79
  }
80
80
  const data = response?.data ?? error?.auth;
81
81
  assign(params, data);
82
- if (!response) {
82
+ if (error) {
83
83
  throw new Error(REASONS.invalid_scope.test(error.message)
84
84
  ? 'Token has invalid scope. Check if your user has the required roles'
85
85
  : `No valid passcode or token provided. Get a passcode${
86
86
  params.get('passcodeUrl') ? ' from ' + params.get('passcodeUrl') : ''
87
- } and re-run the command with '-p <passcode>'`);
87
+ } and re-run the command with '-p <passcode>'`, { cause: error });
88
88
  }
89
89
  }
90
90
 
package/lib/mtx/params.js CHANGED
@@ -213,6 +213,7 @@ class AllParams extends BasicParams {
213
213
  // `extend` PARAMS
214
214
  this.force = new Param('force', false);
215
215
  this.templates = new Param('templates', false);
216
+ this["download-migrated-projects"] = new Param('download-migrated-projects', false)
216
217
 
217
218
  // `activate` PARAMS
218
219
  this.undeploy = new Param('undeploy', false);
@@ -220,6 +221,9 @@ class AllParams extends BasicParams {
220
221
  this.sync = new Param('sync', false);
221
222
  this.async = new Param('async', false);
222
223
 
224
+ // `subscribe` PARAMS
225
+ this.body = new Param('body', '', { persist: Persistence.none });
226
+
223
227
  // `login` PARAMS
224
228
  this.skipToken = new Param('skipToken', false, { internal: true });
225
229
 
@@ -229,8 +233,6 @@ class AllParams extends BasicParams {
229
233
  this.clearInvalid = new Param('clearInvalid', false);
230
234
  this["clear-invalid"] = this.clearInvalid.newAlias('clear-invalid');
231
235
 
232
- // download for mxts
233
- this["download-migrated-projects"] = new Param('download-migrated-projects', false)
234
236
  }
235
237
  }
236
238
 
@@ -252,7 +252,7 @@ class SettingsManager {
252
252
  async function updateTokenUrl() {
253
253
  const tokenUrl = params.get('tokenUrl');
254
254
  const renewUrl = !logout && !params.get('skipToken')
255
- && (!tokenUrl || tokenUrl.includes('//-/cds') || params.get('appUrl') !== loadedSettings.get('appUrl'));
255
+ && (!tokenUrl || tokenUrl.includes('//-/cds') || tokenUrl.includes(OAUTH_PATH_OLD_MTX) || params.get('appUrl') !== loadedSettings.get('appUrl'));
256
256
  if (renewUrl) {
257
257
  const tokenUrl = await getTokenBaseUrl(params, renewUrl);
258
258
  params.set('tokenUrl', tokenUrl);
@@ -48,8 +48,17 @@ module.exports = class TenantAction {
48
48
  console.log(capitalize(this.gerund), { tenant }, this.preposition, { url: params.get('appUrl') });
49
49
  const url = `${params.get('appUrl')}/-/cds/deployment/${this.command}`;
50
50
  DEBUG?.(`Sending request to ${url}`);
51
+ const body = params.get('body');
52
+ let parsedBody;
53
+ if (body) {
54
+ try {
55
+ parsedBody = JSON.parse(body)
56
+ } catch (error) {
57
+ throw getMessage('Invalid subscription body', { error, command: this.command });
58
+ }
59
+ }
51
60
  try {
52
- await require('axios').post(url, { tenant }, params.get('reqAuth'));
61
+ await require('axios').post(url, { tenant, metadata: parsedBody, options: parsedBody }, params.get('reqAuth'));
53
62
  } catch (error) {
54
63
  handleHttpError(error, params, { url });
55
64
  }
@@ -74,6 +83,7 @@ module.exports = class TenantAction {
74
83
  if (!cds_mtx) {
75
84
  throw '@sap/cds-mtxs outdated. Please upgrade to @sap/cds-mtxs@>=1.7.0 and re-run this command.';
76
85
  }
77
- return cds_mtx(this.command, tenant);
86
+ const body = params.get('body')
87
+ return cds_mtx(this.command, tenant, body);
78
88
  }
79
89
  }
@@ -3,9 +3,12 @@
3
3
  function getMessage(errorText, { error, command, help = command && getCommandHelp(command) } = {}) {
4
4
  let message = errorText ? errorText.trim() : '';
5
5
  if (error) {
6
- const includeStack = error.stack && require('../../cds').debug('cli');
7
- message = message.replace(/(?<!\p{P})$/u, ':'); // Append colon if no punctuation at the end
8
- message += includeStack ? '\n' : ' ';
6
+ const debug = require('../../cds').debug('cli');
7
+ const includeStack = debug && error.stack?.includes('\n');
8
+ if (message.length) {
9
+ message = message.replace(/(?<!\p{P})$/u, ':'); // Append colon if no punctuation at the end
10
+ message += includeStack ? '\n' : ' ';
11
+ }
9
12
  const oriMsg = error.response?.data?.message // case: response body == error
10
13
  || error.response?.data?.error?.message // case: response body == { error }
11
14
  || (includeStack ? '' : error.message);
@@ -13,9 +16,12 @@ function getMessage(errorText, { error, command, help = command && getCommandHel
13
16
  if (includeStack) {
14
17
  message += (oriMsg ? '\n' : '') + error.stack;
15
18
  }
19
+ if (debug && error.cause) {
20
+ message += '\nCaused by: ' + getMessage('', { error: error.cause }).replace(/^(?= *at)/gm, ' ');
21
+ }
16
22
  }
17
23
  if (typeof help === 'string') {
18
- message += require('../../../bin/help').formatHelp(help);
24
+ message += '\n' + require('../../../bin/help').formatHelp(help);
19
25
  }
20
26
  return message;
21
27
  }
package/lib/util/axios.js CHANGED
@@ -15,7 +15,8 @@ function pruneErrors() {
15
15
  response => response,
16
16
  axError => {
17
17
  const { inspect } = require('util');
18
- const { url, method } = axError.config ?? {};
18
+ let { url, method } = axError.config ?? {};
19
+ url = url?.replace(/(passcode|refreshToken|clientsecret|key)=[^&]+/g, '$1=...');
19
20
  const { errno, code, response = {}, message: axMessage } = axError;
20
21
  const prefix = (url && method ? `${method.toUpperCase()} ${url}` : 'Request') + ' failed';
21
22
 
package/lib/util/cf.js CHANGED
@@ -28,12 +28,8 @@ const OPERATION_STATE_SUCCEEDED = 'succeeded';
28
28
 
29
29
  const CF_CLIENT_MINIMUM_VERSION = 8;
30
30
 
31
-
32
- class CfUtil {
33
-
34
- _clear() {
35
- this.spaceInfo = null;
36
- }
31
+ module.exports = new class CfUtil {
32
+ spaceInfo;
37
33
 
38
34
  async _sleep(ms) {
39
35
  return new Promise((resolve) => {
@@ -41,7 +37,7 @@ class CfUtil {
41
37
  });
42
38
  }
43
39
 
44
- async _cfRun(...args) {
40
+ async cfRun(...args) {
45
41
  args = args.map(arg => arg.replace(/"/g, '\\"'));
46
42
  const cmdLine = `${CF_COMMAND} "${args.join('" "')}"`;
47
43
  DEBUG && console.time(cmdLine);
@@ -65,12 +61,15 @@ class CfUtil {
65
61
  }
66
62
  }
67
63
 
68
- async _cfRequest(urlPath, queryObj, bodyObj) {
64
+ async cfRequest(urlPath, queryObj, bodyObj) {
69
65
  if (queryObj) {
70
66
  const entries = Object.entries(queryObj);
71
67
  const queryStr = entries.map(([key, value]) => {
68
+ if (Array.isArray(value)) {
69
+ value = value.join(',');
70
+ }
72
71
  // commas cause problems in cf curl when not double encoded
73
- value = value.replace(/,/g, encodeURIComponent(','));
72
+ // value = value.replace(/,/g, encodeURIComponent(','));
74
73
  return `${key}=${encodeURIComponent(value)}`;
75
74
  }).join('&');
76
75
 
@@ -84,7 +83,7 @@ class CfUtil {
84
83
  args.push(JSON.stringify(bodyObj)); // cfRun uses spawn so no special handling for quotes on cli required
85
84
  }
86
85
 
87
- const result = await this._cfRun(...args);
86
+ const result = await this.cfRun(...args);
88
87
  let response = {};
89
88
  if (result.stdout) {
90
89
  response = JSON.parse(result.stdout);
@@ -108,7 +107,7 @@ class CfUtil {
108
107
  throw new Error(errorMsg);
109
108
  }
110
109
 
111
- async getCfTargetFromConfigFile() {
110
+ async _getCfTargetFromConfigFile() {
112
111
  const cfHome = process.env.CF_HOME || process.env.cf_home || path.join(os.homedir(), '.cf');
113
112
  try {
114
113
  const fileContent = await fsp.readFile(path.join(cfHome, 'config.json'));
@@ -125,8 +124,8 @@ class CfUtil {
125
124
  }
126
125
  }
127
126
 
128
- async getCfTargetFromCli() {
129
- const result = await this._cfRun('target');
127
+ async _getCfTargetFromCli() {
128
+ const result = await this.cfRun('target');
130
129
  if (result?.stdout) {
131
130
  return {
132
131
  apiEndpoint: this._extract(result.stdout, /api endpoint\s*:\s*([^\s]+)/i, `CF API endpoint is missing. Use 'cf login' to login.`),
@@ -137,23 +136,23 @@ class CfUtil {
137
136
  }
138
137
  }
139
138
 
140
- async getCfAuthorization() {
139
+ async _getCfAuthorization() {
141
140
  DEBUG?.('getting authorization');
142
141
  try {
143
- return (await this._cfRun('oauth-token')).stdout;
142
+ return (await this.cfRun('oauth-token')).stdout;
144
143
  } catch (err) {
145
144
  throw err.message;
146
145
  }
147
146
  }
148
147
 
149
148
  async getCfTarget() {
150
- await this.checkCliVersion();
151
- await this.getCfAuthorization();
152
- return await this.getCfTargetFromConfigFile() || await this.getCfTargetFromCli();
149
+ await this._checkCliVersion();
150
+ await this._getCfAuthorization();
151
+ return await this._getCfTargetFromConfigFile() || await this._getCfTargetFromCli();
153
152
  }
154
153
 
155
- async checkCliVersion() {
156
- const result = await this._cfRun('-v');
154
+ async _checkCliVersion() {
155
+ const result = await this.cfRun('-v');
157
156
  const version = result?.stdout?.match(/version.*(\d+\.\d+\.\d+)/i)
158
157
  if (parseInt(version?.[1]) < CF_CLIENT_MINIMUM_VERSION) {
159
158
  console.log(warn(`
@@ -170,13 +169,13 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
170
169
  const target = await this.getCfTarget();
171
170
 
172
171
  const { org, space } = target;
173
- const orgs = await this._cfRequest(`/v3/organizations`, { names: org });
172
+ const orgs = await this.cfRequest(`/v3/organizations`, { names: org });
174
173
  if (!orgs?.resources?.length) {
175
174
  throw new Error(`CF org ${bold(org)} not found!`);
176
175
  }
177
176
 
178
177
  const orgGuid = orgs.resources[0].guid;
179
- const spaces = await this._cfRequest(`/v3/spaces`, { names: space, organization_guids: orgGuid });
178
+ const spaces = await this.cfRequest(`/v3/spaces`, { names: space, organization_guids: orgGuid });
180
179
  if (!spaces?.resources?.length) {
181
180
  throw new Error(`CF space ${bold(space)} not found in org ${bold(org)}!`);
182
181
  }
@@ -196,10 +195,10 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
196
195
 
197
196
  async getEnv(app) {
198
197
  const { spaceGuid: space_guids, orgGuid: organization_guids } = await this.getCfSpaceInfo();
199
- const apps = await this._cfRequest('/v3/apps', { space_guids, organization_guids, names: app });
198
+ const apps = await this.cfRequest('/v3/apps', { space_guids, organization_guids, names: app });
200
199
  if (apps.resources.length < 1) throw `Cannot find app with name '${app}'`
201
200
  const guid = apps.resources[0].guid;
202
- return await this._cfRequest('/v3/apps/' + guid + '/env', { space_guids, organization_guids, names: app });
201
+ return await this.cfRequest('/v3/apps/' + guid + '/env', { space_guids, organization_guids, names: app });
203
202
  }
204
203
 
205
204
  async getService(serviceName, showMessage = true) {
@@ -209,7 +208,7 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
209
208
  let counter = POLL_COUNTER;
210
209
  while (counter > 0) {
211
210
  counter--;
212
- const serviceInstances = await this._cfRequest('/v3/service_instances', {
211
+ const serviceInstances = await this.cfRequest('/v3/service_instances', {
213
212
  names: serviceName,
214
213
  space_guids: spaceInfo.spaceGuid,
215
214
  organization_guids: spaceInfo.orgGuid
@@ -253,7 +252,7 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
253
252
 
254
253
  const spaceInfo = await this.getCfSpaceInfo();
255
254
 
256
- const servicePlan = await this._cfRequest(`/v3/service_plans`, {
255
+ const servicePlan = await this.cfRequest(`/v3/service_plans`, {
257
256
  names: planName,
258
257
  space_guids: spaceInfo.spaceGuid,
259
258
  organization_guids: spaceInfo.orgGuid,
@@ -286,7 +285,7 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
286
285
  body.parameters = { ...options };
287
286
  }
288
287
 
289
- const postResult = await this._cfRequest('/v3/service_instances', undefined, body);
288
+ const postResult = await this.cfRequest('/v3/service_instances', undefined, body);
290
289
  if (postResult?.errors) {
291
290
  throw new Error(postResult.errors[0].detail);
292
291
  }
@@ -299,14 +298,13 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
299
298
  throw new Error(`Could not create service ${bold(serviceName)}`);
300
299
  }
301
300
 
302
-
303
- async getServiceKey(serviceInstance, serviceKeyName, showMessage = true) {
301
+ async _getServiceKey(serviceInstance, serviceKeyName, showMessage = true) {
304
302
  showMessage && console.log(`Getting service key ${bold(serviceKeyName)}`);
305
303
 
306
304
  let counter = POLL_COUNTER;
307
305
  while (counter > 0) {
308
306
  counter--;
309
- const bindings = await this._cfRequest(`/v3/service_credential_bindings`, { names: serviceKeyName, service_instance_guids: serviceInstance.guid });
307
+ const bindings = await this.cfRequest(`/v3/service_credential_bindings`, { names: serviceKeyName, service_instance_guids: serviceInstance.guid });
310
308
  if (!bindings?.resources?.length) {
311
309
  return null;
312
310
  }
@@ -319,7 +317,7 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
319
317
  break;
320
318
 
321
319
  case OPERATION_STATE_SUCCEEDED: {
322
- const keyDetails = await this._cfRequest(`/v3/service_credential_bindings/${encodeURIComponent(binding.guid)}/details`);
320
+ const keyDetails = await this.cfRequest(`/v3/service_credential_bindings/${encodeURIComponent(binding.guid)}/details`);
323
321
  return keyDetails.credentials;
324
322
  }
325
323
 
@@ -335,9 +333,8 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
335
333
  throw new Error(`Timeout occurred while getting service key ${bold(serviceKeyName)}`);
336
334
  }
337
335
 
338
-
339
336
  async getOrCreateServiceKey(serviceInstance, serviceKeyName, parameters, { silent = false, create = true } = {}) {
340
- const serviceKey = await this.getServiceKey(serviceInstance, serviceKeyName, false);
337
+ const serviceKey = await this._getServiceKey(serviceInstance, serviceKeyName, false);
341
338
  if (serviceKey || !create) {
342
339
  if (serviceKey && !silent) console.log(`Getting service key ${bold(serviceKeyName)}`);
343
340
  return serviceKey;
@@ -358,12 +355,12 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
358
355
  parameters
359
356
  }
360
357
 
361
- const postResult = await this._cfRequest('/v3/service_credential_bindings', undefined, body);
358
+ const postResult = await this.cfRequest('/v3/service_credential_bindings', undefined, body);
362
359
  if (postResult?.errors) {
363
360
  throw new Error(postResult.errors[0].detail);
364
361
  }
365
362
 
366
- const newServiceKey = await this.getServiceKey(serviceInstance, serviceKeyName, false);
363
+ const newServiceKey = await this._getServiceKey(serviceInstance, serviceKeyName, false);
367
364
  if (newServiceKey) {
368
365
  return newServiceKey;
369
366
  }
@@ -371,5 +368,3 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
371
368
  throw new Error(`Could not create service key ${bold(serviceKeyName)}`);
372
369
  }
373
370
  }
374
-
375
- module.exports = new CfUtil();
package/lib/util/fs.js CHANGED
@@ -28,7 +28,7 @@ module.exports = new class FsUtil {
28
28
  }
29
29
 
30
30
  async writeYAML(dst, yaml) {
31
- const content = YAML.stringify(yaml, { collectionStyle: 'block' })
31
+ const content = YAML.stringify(yaml, { collectionStyle: 'block', lineWidth: 120 })
32
32
  await write(content).to(dst)
33
33
  }
34
34
 
package/lib/util/git.js CHANGED
@@ -35,7 +35,7 @@ class GitUtil {
35
35
  }
36
36
  }
37
37
 
38
- console.log(`adding entry '${file}' to ${GIT_IGNORE_FILE}.`);
38
+ console.log(`Adding entry '${file}' to ${GIT_IGNORE_FILE}.`);
39
39
  let gitignore = await this.readFileSafely(gitIgnorePath);
40
40
  gitignore = gitignore + `${os.EOL}# added by cds${os.EOL}${file}${os.EOL}`;
41
41
 
package/lib/util/term.js CHANGED
@@ -26,7 +26,7 @@ module.exports.info = o => as(t.green, o)
26
26
  module.exports.link = o => as(t.link, o)
27
27
  module.exports.bold = o => as(t.bold, o)
28
28
  module.exports.highlight = o => as(t.blue,as(t.bold, o))
29
- module.exports.secondary = o => as(t.grey, o)
29
+ module.exports.dim = o => as(t.grey, o)
30
30
 
31
31
  const format = module.exports.format = (o, severity='Error', asInternalError=false, withStack=false) => {
32
32
  switch (severity) {
@@ -7,8 +7,8 @@
7
7
 
8
8
  // run through cli in order to use its error handling
9
9
  const args = JSON.parse(process.env._args)
10
- const cds = require('../lib/cds'); cds.watched = true
11
- const bootstrap = require('./cds')(...args)
10
+ const cds = require('../cds'); cds.watched = true
11
+ const bootstrap = require('../../bin/cds')(...args)
12
12
 
13
13
  cds.once('listening', ({server}) => {
14
14
  process.send ({ code:'listening', port: server.address().port })