@sap/cds-dk 8.7.3 → 8.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 (72) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/bin/bind.js +7 -8
  3. package/bin/cds.js +2 -4
  4. package/bin/compile/index.js +1 -0
  5. package/bin/deploy/index.js +10 -5
  6. package/bin/watch.js +48 -7
  7. package/lib/bind/{cfKeyProvider.js → cf.js} +43 -53
  8. package/lib/bind/k8s.js +138 -0
  9. package/lib/bind/plugin.js +48 -0
  10. package/lib/bind/shared.js +267 -0
  11. package/lib/build/buildTaskEngine.js +1 -1
  12. package/lib/build/plugins/helm/files/subcharts/content-deployment/Chart.yaml +0 -2
  13. package/lib/build/plugins/helm/files/subcharts/web-application/Chart.yaml +1 -3
  14. package/lib/build/plugins/helm/files/subcharts/web-application/templates/service-binding.yaml +3 -0
  15. package/lib/build/plugins/helm/files/subcharts/web-application/values.schema.json +3 -0
  16. package/lib/build/plugins/helm/files/subcharts/web-application/values.yaml +2 -1
  17. package/lib/build/util.js +1 -1
  18. package/lib/compile/to-mermaid.js +1 -2
  19. package/lib/deploy/to-hana/hana.js +1 -1
  20. package/lib/deploy/to-hana/mtaUtil.js +0 -13
  21. package/lib/import/common.js +6 -7
  22. package/lib/init/merge.js +41 -32
  23. package/lib/init/projectReader.js +15 -7
  24. package/lib/init/registries/mta.js +7 -1
  25. package/lib/init/template/approuter/files/package.json +1 -1
  26. package/lib/init/template/approuter/files/xs-app.json.hbs +0 -11
  27. package/lib/init/template/cloud-logging/files/package.json.hbs +12 -1
  28. package/lib/init/template/cloud-logging/files/values.yaml.hbs +0 -2
  29. package/lib/init/template/cloud-logging/index.js +8 -15
  30. package/lib/init/template/destination/files/values.yaml +4 -4
  31. package/lib/init/template/destination/index.js +1 -1
  32. package/lib/init/template/html5-repo/files/app-package.json.hbs +0 -2
  33. package/lib/init/template/html5-repo/files/containerize.yaml.hbs +25 -0
  34. package/lib/init/template/html5-repo/files/mta.yaml.hbs +1 -2
  35. package/lib/init/template/html5-repo/files/package.json.hbs +1 -1
  36. package/lib/init/template/html5-repo/files/ui-resources/package.json.hbs +11 -0
  37. package/lib/init/template/html5-repo/files/ui5-deploy.yaml.hbs +4 -7
  38. package/lib/init/template/html5-repo/files/values.yaml.hbs +6 -4
  39. package/lib/init/template/html5-repo/index.js +22 -10
  40. package/lib/init/template/http/index.js +1 -1
  41. package/lib/init/template/multitenancy/files/mta.yaml.hbs +0 -2
  42. package/lib/init/template/multitenancy/index.js +1 -1
  43. package/lib/init/template/portal/files/mta.yaml.hbs +9 -0
  44. package/lib/init/template/portal/index.js +3 -1
  45. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +119 -119
  46. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +1 -1
  47. package/lib/init/template/telemetry/index.js +0 -13
  48. package/lib/init/template/typer/files/package.json +3 -0
  49. package/lib/init/template/workzone-standard/files/cdm.json.hbs +97 -0
  50. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +23 -0
  51. package/lib/init/template/workzone-standard/index.js +70 -39
  52. package/lib/init/template/xsuaa/files/mta.yaml.hbs +2 -2
  53. package/lib/init/template/xsuaa/files/values.yaml.hbs +0 -2
  54. package/lib/init/template/xsuaa/index.js +22 -10
  55. package/lib/mtx/util/cf.js +1 -2
  56. package/lib/util/cf.js +104 -84
  57. package/lib/util/command.js +1 -2
  58. package/lib/util/git.js +1 -1
  59. package/npm-shrinkwrap.json +31 -31
  60. package/package.json +1 -1
  61. package/bin/deploy/to-cf.js +0 -0
  62. package/bin/deploy/via-mta.js +0 -0
  63. package/lib/bind/KubernetesApiAccessor.js +0 -81
  64. package/lib/bind/bindingManager.js +0 -89
  65. package/lib/bind/bindingResolver.js +0 -54
  66. package/lib/bind/bindingUtil.js +0 -129
  67. package/lib/bind/index.js +0 -127
  68. package/lib/bind/kubernetesCredentialsProvider.js +0 -84
  69. package/lib/bind/userCdsrcJsonStorageProvider.js +0 -92
  70. package/lib/init/template/destination/files/package.sidecar.json +0 -7
  71. package/lib/init/template/html5-repo/files/package.sidecar.json +0 -7
  72. package/lib/init/template/telemetry/files/package.json.hbs +0 -18
@@ -2,10 +2,19 @@ const cds = require('../../../cds')
2
2
  const { read } = cds.utils
3
3
  const { readProject } = require('../../projectReader')
4
4
  const { merge } = require('../../merge')
5
- const { srv4, xsuaa, mtxSidecar4 } = require('../../registries/mta')
5
+ const { srv4, xsuaa: xsuaaDefault, xsuaa4, mtxSidecar4 } = require('../../registries/mta')
6
6
 
7
7
  module.exports = class XsuaaTemplate extends require('../../plugin') {
8
8
 
9
+ options() {
10
+ return {
11
+ 'plan': {
12
+ type: 'string',
13
+ help: 'Specify the service plan.',
14
+ }
15
+ }
16
+ }
17
+
9
18
  static help() {
10
19
  return 'authentication via XSUAA'
11
20
  }
@@ -26,16 +35,20 @@ module.exports = class XsuaaTemplate extends require('../../plugin') {
26
35
  const { hasMultitenancy, hasMta, hasHelm, hasHelmUnifiedRuntime, isJava, srvPath } = project
27
36
 
28
37
  const cds = require('../../../../lib')
29
- const xsSecurity = await (async () => { try {
30
- const models = await cds.load(cds.env.folders.srv)
31
- return cds.compile.to.xsuaa(models)
32
- } catch (error) { /* ignore */ }})() ?? { scopes: [], attributes: [], 'role-templates': [], 'authorities-inheritance': false }
38
+ const xsSecurity = await (async () => {
39
+ try {
40
+ const models = await cds.load(cds.env.folders.srv)
41
+ return cds.compile.to.xsuaa(models)
42
+ } catch (error) { /* ignore */ }
43
+ })() ?? { scopes: [], attributes: [], 'role-templates': [], 'authorities-inheritance': false }
33
44
 
34
45
  const additions = xsSecurity.scopes.map(scope => ({ in: 'scopes', where: { name: scope.name } }))
35
46
  await merge(xsSecurity).into('xs-security.json', { project, additions })
36
47
 
37
48
  if (hasMta) {
38
49
  const srv = srv4(srvPath)
50
+ const { plan } = cds.cli.options
51
+ const xsuaa = plan ? xsuaa4(plan) : xsuaaDefault
39
52
  const additions = [srv, xsuaa]
40
53
  const mtxSidecar = mtxSidecar4(isJava ? 'mtx/sidecar' : 'gen/mtx/sidecar')
41
54
  if (hasMultitenancy && isJava) additions.push(mtxSidecar)
@@ -48,19 +61,18 @@ module.exports = class XsuaaTemplate extends require('../../plugin') {
48
61
  insert: [xsuaa, 'name'],
49
62
  into: [mtxSidecar, 'requires', 'name']
50
63
  })
51
-
64
+ if (plan) project.servicePlan = plan
52
65
  await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', { project, additions, overwrites, relationships })
53
66
  }
54
67
 
55
68
  if (hasHelm || hasHelmUnifiedRuntime) {
56
69
  const overwrites = hasMultitenancy ? [{
57
70
  item: [{
58
- in: 'xsuaa',
59
- where: { serviceOfferingName: 'xsuaa' }
71
+ in: 'xsuaa',
72
+ where: { serviceOfferingName: 'xsuaa' }
60
73
  }, 'parameters.tenant-mode'], withValue: 'shared'
61
- }] : []
74
+ }] : []
62
75
  await merge(__dirname, 'files/values.yaml.hbs').into('chart/values.yaml', { with: project, overwrites })
63
-
64
76
  await merge({
65
77
  xsuaa: {
66
78
  parameters: {
@@ -1,4 +1,3 @@
1
- const os = require('os');
2
1
  const { execFile } = require('child_process');
3
2
 
4
3
  const cds = require('../../cds');
@@ -139,7 +138,7 @@ async function getSubdomain(appName) {
139
138
  return resolve(errorResult(getErrorMessage(error, stdout)));
140
139
  }
141
140
 
142
- const nl = os.EOL;
141
+ const nl = '\r?\n'; //more robust than os.EOL;
143
142
  const match = new RegExp(`^System-Provided:${nl}(?:\\{${nl}((.|${nl})*?)^}|((.|${nl})*?^}))`, 'm').exec(stdout);
144
143
  if (!match) {
145
144
  return resolve(errorResult(`CF reports no system-provided environment for app '${appName}'`));
package/lib/util/cf.js CHANGED
@@ -32,7 +32,6 @@ const CF_CLIENT_MINIMUM_VERSION = 8;
32
32
  const ENCODED_COMMA = encodeURIComponent(',');
33
33
 
34
34
  module.exports = new class CfUtil {
35
- spaceInfo;
36
35
 
37
36
  async _sleep(ms) {
38
37
  return new Promise((resolve) => {
@@ -68,11 +67,11 @@ module.exports = new class CfUtil {
68
67
  }
69
68
  }
70
69
 
71
- async cfRequest(urlPath, queryObj, bodyObj) {
72
- const url = this.spaceInfo.apiEndpoint + urlPath;
70
+ async curl(urlPath, queryObj, bodyObj) {
71
+ const url = this.#target.apiEndpoint + urlPath;
73
72
 
74
73
  const options = {
75
- headers: { 'Authorization': this.spaceInfo.token }
74
+ headers: { 'Authorization': this.#target.token }
76
75
  };
77
76
 
78
77
  if (queryObj && Object.keys(queryObj).length > 0) {
@@ -96,8 +95,6 @@ module.exports = new class CfUtil {
96
95
  LOG.debug(`POST ${url}\n${JSON.stringify(options, null, 4)}\n${JSON.stringify(response.data, null, 4)}`);
97
96
  }
98
97
 
99
- // require('../../test/testUtil/recorder').record(urlPath, options.params, response.data, 'cf.json');
100
-
101
98
  return response.data;
102
99
  } catch (err) {
103
100
  if (err instanceof axios.AxiosError) {
@@ -134,111 +131,113 @@ module.exports = new class CfUtil {
134
131
  }
135
132
  }
136
133
 
134
+ #cfTargetPromise
137
135
  async _getCfTargetFromCli() {
138
- const result = await this.cfRun('target');
139
- if (result?.stdout) {
140
- return {
141
- apiEndpoint: this._extract(result.stdout, /api endpoint\s*:\s*([^\s]+)/i, `CF API endpoint is missing. Use 'cf login' to login.`),
142
- user: this._extract(result.stdout, /user\s*:\s*(.+)/i, `CF user is missing. Use 'cf login' to login.`),
143
- org: this._extract(result.stdout, /org\s*:\s*(.+)/i, `CF org is missing. Use 'cf target -o <ORG>' to specify.`),
144
- space: this._extract(result.stdout, /space\s*:\s*(.+)/i, `CF space is missing. Use 'cf target -s <SPACE>' to specify.`),
145
- };
146
- }
136
+ if (this.#cfTargetPromise) return this.#cfTargetPromise
137
+ this.#cfTargetPromise = (async () => {
138
+ const result = await this.cfRun('target')
139
+ if (result?.stdout) {
140
+ return {
141
+ apiEndpoint: this._extract(result.stdout, /api endpoint\s*:\s*([^\s]+)/i, `CF API endpoint is missing. Use 'cf login' to login.`),
142
+ user: this._extract(result.stdout, /user\s*:\s*(.+)/i, `CF user is missing. Use 'cf login' to login.`),
143
+ org: this._extract(result.stdout, /org\s*:\s*(.+)/i, `CF org is missing. Use 'cf target -o <ORG>' to specify.`),
144
+ space: this._extract(result.stdout, /space\s*:\s*(.+)/i, `CF space is missing. Use 'cf target -s <SPACE>' to specify.`),
145
+ }
146
+ }
147
+ })()
148
+ return this.#cfTargetPromise
147
149
  }
148
150
 
149
151
  #token
150
- async _getCfAuthorization() {
152
+ #tokenPromise
153
+ async _token() {
151
154
  if (this.#token) return this.#token
152
- if (!this._tokenPromise) {
153
- this._tokenPromise = this.cfRun('oauth-token')
155
+ if (!this.#tokenPromise) {
156
+ this.#tokenPromise = this.cfRun('oauth-token')
154
157
  .then(result => {
155
158
  this.#token = result.stdout
156
159
  return this.#token
157
160
  })
158
161
  .catch(err => {
159
- this._tokenPromise = null
162
+ this.#tokenPromise = null
160
163
  throw err.message
161
164
  })
162
165
  }
163
- return this._tokenPromise
164
- }
165
-
166
- async getCfTarget() {
167
- await this._checkCliVersion();
168
- const token = await this._getCfAuthorization();
169
- const target = await this._getCfTargetFromConfigFile() || await this._getCfTargetFromCli();
170
- target.token = token;
171
- return target;
166
+ return this.#tokenPromise
172
167
  }
173
168
 
174
- #checkedVersion = false
169
+ #cfVersionPromise
175
170
  async _checkCliVersion() {
176
- if (this.#checkedVersion) return
177
- this.#checkedVersion = true
178
- const result = await this.cfRun('-v');
179
- const version = result?.stdout?.match(/version.*(\d+\.\d+\.\d+)/i)
180
- if (parseInt(version?.[1]) < CF_CLIENT_MINIMUM_VERSION) {
181
- console.log(warn(`
182
- [Warning] You are using Cloud Foundry client version ${version[1]}. We recommend version ${CF_CLIENT_MINIMUM_VERSION} or higher.
183
- Deployment will stop in the near future for Cloud Foundry client versions < ${CF_CLIENT_MINIMUM_VERSION}.
184
- `));
185
- }
171
+ if (this.#cfVersionPromise) return this.#cfVersionPromise
172
+ this.#cfVersionPromise = (async () => {
173
+ const result = await this.cfRun('-v');
174
+ const version = result?.stdout?.match(/version.*(\d+\.\d+\.\d+)/i);
175
+ if (parseInt(version?.[1]) < CF_CLIENT_MINIMUM_VERSION) {
176
+ console.log(warn(`
177
+ [Warning] You are using Cloud Foundry client version ${version[1]}. We recommend version ${CF_CLIENT_MINIMUM_VERSION} or higher.
178
+ Deployment will stop in the near future for Cloud Foundry client versions < ${CF_CLIENT_MINIMUM_VERSION}.
179
+ `))
180
+ }
181
+ })()
182
+ return this.#cfVersionPromise
186
183
  }
187
184
 
188
- async getCfSpaceInfo() {
189
- if (!this.spaceInfo) {
190
- LOG.debug('getting space info');
185
+ #target
186
+ async target() {
187
+ if (!this.#target) {
188
+ await this._checkCliVersion();
189
+ const token = await this._token();
190
+ const target = await this._getCfTargetFromConfigFile() || await this._getCfTargetFromCli();
191
+ target.token = token;
192
+ this.#target = target;
191
193
 
192
- const target = await this.getCfTarget();
193
- this.spaceInfo = target;
194
194
  const { org, space } = target;
195
- const orgs = await this.cfRequest('/v3/organizations', { names: org });
195
+ const orgs = await this.curl('/v3/organizations', { names: org });
196
196
  if (!orgs?.resources?.length) {
197
197
  throw `CF org ${bold(org)} not found!`;
198
198
  }
199
-
200
199
  const orgGuid = orgs.resources[0].guid;
201
- const spaces = await this.cfRequest('/v3/spaces', { names: space, organization_guids: orgGuid });
200
+
201
+ const spaces = await this.curl('/v3/spaces', { names: space, organization_guids: orgGuid });
202
202
  if (!spaces?.resources?.length) {
203
203
  throw `CF space ${bold(space)} not found in org ${bold(org)}!`;
204
204
  }
205
205
 
206
206
  const spaceGuid = spaces.resources[0].guid;
207
207
 
208
- this.spaceInfo = {
208
+ this.#target = {
209
209
  ...target,
210
210
  orgGuid,
211
211
  spaceGuid
212
212
  }
213
213
  }
214
214
 
215
- return this.spaceInfo;
215
+ return this.#target;
216
216
  }
217
217
 
218
- async getServices(app) {
219
- const env = await this.getEnv(app)
218
+ async services(app) {
219
+ const env = await this.env(app)
220
220
  return Object.values(env.system_env_json.VCAP_SERVICES).flat();
221
221
  }
222
222
 
223
- async getEnv(app) {
224
- const { spaceGuid: space_guids, orgGuid: organization_guids } = await this.getCfSpaceInfo();
225
- const apps = await this.cfRequest('/v3/apps', { space_guids, organization_guids, names: app });
223
+ async env(app) {
224
+ const { spaceGuid: space_guids, orgGuid: organization_guids } = await this.target();
225
+ const apps = await this.curl('/v3/apps', { space_guids, organization_guids, names: app });
226
226
  if (apps.resources.length < 1) throw `Cannot find app with name '${app}'`
227
227
  const guid = apps.resources[0].guid;
228
- return await this.cfRequest('/v3/apps/' + guid + '/env', { space_guids, organization_guids, names: app });
228
+ return await this.curl('/v3/apps/' + guid + '/env', { space_guids, organization_guids, names: app });
229
229
  }
230
230
 
231
- async getService(serviceName, showMessage = true) {
232
- showMessage && console.log(`getting service ${bold(serviceName)}`);
233
- const spaceInfo = await this.getCfSpaceInfo();
231
+ async service(serviceName) {
232
+ const target = await this.target();
234
233
 
235
234
  let counter = POLL_COUNTER;
236
235
  while (counter > 0) {
237
236
  counter--;
238
- const serviceInstances = await this.cfRequest('/v3/service_instances', {
237
+ const serviceInstances = await this.curl('/v3/service_instances', {
239
238
  names: serviceName,
240
- space_guids: spaceInfo.spaceGuid,
241
- organization_guids: spaceInfo.orgGuid
239
+ space_guids: target.spaceGuid,
240
+ organization_guids: target.orgGuid
242
241
  });
243
242
  if (!serviceInstances?.resources?.length) {
244
243
  return null;
@@ -269,10 +268,8 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
269
268
  throw `Timeout occurred while getting service ${bold(serviceName)}`;
270
269
  }
271
270
 
272
-
273
271
  async getOrCreateService(serviceOfferingName, planName, serviceName, options) {
274
-
275
- const probeService = await this.getService(serviceName, false);
272
+ const probeService = await this.service(serviceName);
276
273
  if (probeService) {
277
274
  console.log(`getting service ${bold(serviceName)}`);
278
275
  return probeService;
@@ -280,12 +277,12 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
280
277
 
281
278
  console.log(`Creating service ${bold(serviceName)} - please be patient...`);
282
279
 
283
- const spaceInfo = await this.getCfSpaceInfo();
280
+ const target = await this.target();
284
281
 
285
- const servicePlan = await this.cfRequest(`/v3/service_plans`, {
282
+ const servicePlan = await this.curl(`/v3/service_plans`, {
286
283
  names: planName,
287
- space_guids: spaceInfo.spaceGuid,
288
- organization_guids: spaceInfo.orgGuid,
284
+ space_guids: target.spaceGuid,
285
+ organization_guids: target.orgGuid,
289
286
  service_offering_names: serviceOfferingName
290
287
  });
291
288
 
@@ -300,7 +297,7 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
300
297
  relationships: {
301
298
  space: {
302
299
  data: {
303
- guid: spaceInfo.spaceGuid
300
+ guid: target.spaceGuid
304
301
  }
305
302
  },
306
303
  service_plan: {
@@ -315,12 +312,12 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
315
312
  body.parameters = { ...options };
316
313
  }
317
314
 
318
- const postResult = await this.cfRequest('/v3/service_instances', undefined, body);
315
+ const postResult = await this.curl('/v3/service_instances', undefined, body);
319
316
  if (postResult?.errors) {
320
317
  throw postResult.errors[0].detail;
321
318
  }
322
319
 
323
- const newService = await this.getService(serviceName, false);
320
+ const newService = await this.service(serviceName);
324
321
  if (newService) {
325
322
  return newService;
326
323
  }
@@ -328,13 +325,11 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
328
325
  throw `Could not create service ${bold(serviceName)}`;
329
326
  }
330
327
 
331
- async _getServiceKey(serviceInstance, serviceKeyName, showMessage = true) {
332
- showMessage && console.log(`getting service key ${bold(serviceKeyName)}`);
333
-
328
+ async _getServiceKey(serviceInstance, serviceKeyName) {
334
329
  let counter = POLL_COUNTER;
335
330
  while (counter > 0) {
336
331
  counter--;
337
- const bindings = await this.cfRequest(`/v3/service_credential_bindings`, { names: serviceKeyName, service_instance_guids: serviceInstance.guid });
332
+ const bindings = await this.curl(`/v3/service_credential_bindings`, { names: serviceKeyName, service_instance_guids: serviceInstance.guid });
338
333
  if (!bindings?.resources?.length) {
339
334
  return null;
340
335
  }
@@ -347,7 +342,7 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
347
342
  break;
348
343
 
349
344
  case OPERATION_STATE_SUCCEEDED: {
350
- const keyDetails = await this.cfRequest(`/v3/service_credential_bindings/${encodeURIComponent(binding.guid)}/details`);
345
+ const keyDetails = await this.curl(`/v3/service_credential_bindings/${encodeURIComponent(binding.guid)}/details`);
351
346
  return keyDetails.credentials;
352
347
  }
353
348
 
@@ -366,14 +361,14 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
366
361
  throw `Timeout occurred while getting service key ${bold(serviceKeyName)}`;
367
362
  }
368
363
 
369
- async getOrCreateServiceKey(serviceInstance, serviceKeyName, parameters, { silent = false, create = true } = {}) {
370
- const serviceKey = await this._getServiceKey(serviceInstance, serviceKeyName, false);
371
- if (serviceKey || !create) {
372
- if (serviceKey && !silent) DEBUG?.(`getting service key ${bold(serviceKeyName)}`);
364
+ async getOrCreateServiceKey(serviceInstance, serviceKeyName, parameters, { silent = false } = {}) {
365
+ const serviceKey = await this._getServiceKey(serviceInstance, serviceKeyName);
366
+ if (serviceKey) {
367
+ DEBUG?.(`getting service key ${bold(serviceKeyName)}`)
373
368
  return serviceKey;
374
369
  }
375
370
 
376
- if (!silent) console.log(`Creating service key ${bold(serviceKeyName)} - please be patient...`);
371
+ if (!silent) console.log(`creating service key ${bold(serviceKeyName)} - please be patient...`);
377
372
 
378
373
  const body = {
379
374
  type: 'key',
@@ -388,16 +383,41 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
388
383
  parameters
389
384
  }
390
385
 
391
- const postResult = await this.cfRequest('/v3/service_credential_bindings', undefined, body);
386
+ const postResult = await this.curl('/v3/service_credential_bindings', undefined, body);
392
387
  if (postResult?.errors) {
393
388
  throw postResult.errors[0].detail;
394
389
  }
395
390
 
396
- const newServiceKey = await this._getServiceKey(serviceInstance, serviceKeyName, false);
391
+ const newServiceKey = await this._getServiceKey(serviceInstance, serviceKeyName);
397
392
  if (newServiceKey) {
398
393
  return newServiceKey;
399
394
  }
400
395
 
401
- throw `Could not create service key ${bold(serviceKeyName)}`;
396
+ throw `could not create service key ${bold(serviceKeyName)}`;
397
+ }
398
+
399
+ async org(orgName) {
400
+ const result = await this.curl('/v3/organizations', { names: orgName })
401
+ return result?.resources?.[0]
402
+ }
403
+
404
+ async space(orgGuid, spaceName) {
405
+ const result = await this.curl('/v3/spaces', { names: spaceName, organization_guids: orgGuid })
406
+ return result?.resources?.find(r => r.relationships.organization.data.guid === orgGuid)
407
+ }
408
+
409
+ async instance(spaceGuid, instanceName) {
410
+ const result = await this.curl('/v3/service_instances', { names: instanceName, space_guids: spaceGuid })
411
+ return result?.resources?.find(r => !!r.relationships.space.data.guid)
412
+ }
413
+
414
+ async plan(planGuid) {
415
+ const result = await this.curl('/v3/service_plans', { guids: planGuid })
416
+ return result?.resources?.[0]
417
+ }
418
+
419
+ async offering(offeringGuid) {
420
+ const result = await this.curl('/v3/service_offerings', { guids: offeringGuid })
421
+ return result?.resources?.[0]
402
422
  }
403
423
  }
@@ -1,5 +1,4 @@
1
1
  const cp = require('child_process');
2
- const os = require('os');
3
2
  const cds = require('../cds');
4
3
  const LOG = cds.log('cli'), DEBUG = cds.debug('cli');
5
4
 
@@ -102,7 +101,7 @@ class LogBuffer {
102
101
  this.result = this.result + chunkStr;
103
102
  }
104
103
 
105
- const seq = chunkStr.split(os.EOL);
104
+ const seq = chunkStr.split(/\r?\n/); // more robust than os.EOL
106
105
  this.buffer = this.buffer + seq.shift();
107
106
  for (const line of seq) {
108
107
  this.flush();
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
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "8.7.3",
3
+ "version": "8.8.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@sap/cds-dk",
9
- "version": "8.7.3",
9
+ "version": "8.8.0",
10
10
  "license": "SEE LICENSE IN LICENSE",
11
11
  "dependencies": {
12
12
  "@cap-js/asyncapi": "^1.0.0",
@@ -54,7 +54,7 @@
54
54
  }
55
55
  },
56
56
  "node_modules/@cap-js/openapi": {
57
- "version": "1.1.2",
57
+ "version": "1.2.0",
58
58
  "license": "SEE LICENSE IN LICENSE",
59
59
  "dependencies": {
60
60
  "pluralize": "^8.0.0"
@@ -121,7 +121,7 @@
121
121
  }
122
122
  },
123
123
  "node_modules/@eslint/core": {
124
- "version": "0.11.0",
124
+ "version": "0.12.0",
125
125
  "license": "Apache-2.0",
126
126
  "dependencies": {
127
127
  "@types/json-schema": "^7.0.15"
@@ -131,7 +131,7 @@
131
131
  }
132
132
  },
133
133
  "node_modules/@eslint/eslintrc": {
134
- "version": "3.2.0",
134
+ "version": "3.3.0",
135
135
  "license": "MIT",
136
136
  "dependencies": {
137
137
  "ajv": "^6.12.4",
@@ -152,7 +152,7 @@
152
152
  }
153
153
  },
154
154
  "node_modules/@eslint/js": {
155
- "version": "9.20.0",
155
+ "version": "9.21.0",
156
156
  "license": "MIT",
157
157
  "engines": {
158
158
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -166,10 +166,10 @@
166
166
  }
167
167
  },
168
168
  "node_modules/@eslint/plugin-kit": {
169
- "version": "0.2.6",
169
+ "version": "0.2.7",
170
170
  "license": "Apache-2.0",
171
171
  "dependencies": {
172
- "@eslint/core": "^0.11.0",
172
+ "@eslint/core": "^0.12.0",
173
173
  "levn": "^0.4.1"
174
174
  },
175
175
  "engines": {
@@ -217,7 +217,7 @@
217
217
  }
218
218
  },
219
219
  "node_modules/@humanwhocodes/retry": {
220
- "version": "0.4.1",
220
+ "version": "0.4.2",
221
221
  "license": "Apache-2.0",
222
222
  "engines": {
223
223
  "node": ">=18.18"
@@ -228,7 +228,7 @@
228
228
  }
229
229
  },
230
230
  "node_modules/@sap/cds": {
231
- "version": "8.7.2",
231
+ "version": "8.8.0",
232
232
  "license": "SEE LICENSE IN LICENSE",
233
233
  "dependencies": {
234
234
  "@sap/cds-compiler": ">=5.1",
@@ -258,7 +258,7 @@
258
258
  }
259
259
  },
260
260
  "node_modules/@sap/cds-compiler": {
261
- "version": "5.7.4",
261
+ "version": "5.8.0",
262
262
  "license": "SEE LICENSE IN LICENSE",
263
263
  "dependencies": {
264
264
  "antlr4": "4.9.3"
@@ -273,7 +273,7 @@
273
273
  }
274
274
  },
275
275
  "node_modules/@sap/cds-fiori": {
276
- "version": "1.3.0",
276
+ "version": "1.4.0",
277
277
  "license": "SEE LICENSE IN LICENSE",
278
278
  "peerDependencies": {
279
279
  "@sap/cds": ">=7.6",
@@ -294,7 +294,7 @@
294
294
  }
295
295
  },
296
296
  "node_modules/@sap/cds-mtxs": {
297
- "version": "2.5.1",
297
+ "version": "2.6.0",
298
298
  "license": "SEE LICENSE IN LICENSE",
299
299
  "dependencies": {
300
300
  "@sap/hdi-deploy": ">=4",
@@ -306,11 +306,11 @@
306
306
  }
307
307
  },
308
308
  "node_modules/@sap/eslint-plugin-cds": {
309
- "version": "3.1.2",
309
+ "version": "3.2.0",
310
310
  "license": "See LICENSE file",
311
311
  "dependencies": {
312
312
  "@sap/cds": ">=7",
313
- "semver": "^7.3.4"
313
+ "semver": "^7.7.1"
314
314
  },
315
315
  "engines": {
316
316
  "node": ">=18"
@@ -342,10 +342,10 @@
342
342
  }
343
343
  },
344
344
  "node_modules/@sap/hdi-deploy": {
345
- "version": "5.3.2",
345
+ "version": "5.4.1",
346
346
  "license": "See LICENSE file",
347
347
  "dependencies": {
348
- "@sap/hdi": "^4.6.0",
348
+ "@sap/hdi": "^4.7.0",
349
349
  "@sap/xsenv": "^5.2.0",
350
350
  "async": "^3.2.6",
351
351
  "dotenv": "^16.4.5",
@@ -353,7 +353,7 @@
353
353
  "micromatch": "^4.0.8"
354
354
  },
355
355
  "engines": {
356
- "node": "^18.0.0 || ^20.0.0"
356
+ "node": ">=18.x"
357
357
  },
358
358
  "peerDependencies": {
359
359
  "@sap/hana-client": "^2 >= 2.6",
@@ -474,7 +474,7 @@
474
474
  "license": "MIT"
475
475
  },
476
476
  "node_modules/axios": {
477
- "version": "1.7.9",
477
+ "version": "1.8.1",
478
478
  "license": "MIT",
479
479
  "dependencies": {
480
480
  "follow-redirects": "^1.15.6",
@@ -919,19 +919,19 @@
919
919
  }
920
920
  },
921
921
  "node_modules/eslint": {
922
- "version": "9.20.1",
922
+ "version": "9.21.0",
923
923
  "license": "MIT",
924
924
  "dependencies": {
925
925
  "@eslint-community/eslint-utils": "^4.2.0",
926
926
  "@eslint-community/regexpp": "^4.12.1",
927
- "@eslint/config-array": "^0.19.0",
928
- "@eslint/core": "^0.11.0",
929
- "@eslint/eslintrc": "^3.2.0",
930
- "@eslint/js": "9.20.0",
931
- "@eslint/plugin-kit": "^0.2.5",
927
+ "@eslint/config-array": "^0.19.2",
928
+ "@eslint/core": "^0.12.0",
929
+ "@eslint/eslintrc": "^3.3.0",
930
+ "@eslint/js": "9.21.0",
931
+ "@eslint/plugin-kit": "^0.2.7",
932
932
  "@humanfs/node": "^0.16.6",
933
933
  "@humanwhocodes/module-importer": "^1.0.1",
934
- "@humanwhocodes/retry": "^0.4.1",
934
+ "@humanwhocodes/retry": "^0.4.2",
935
935
  "@types/estree": "^1.0.6",
936
936
  "@types/json-schema": "^7.0.15",
937
937
  "ajv": "^6.12.4",
@@ -1283,15 +1283,15 @@
1283
1283
  }
1284
1284
  },
1285
1285
  "node_modules/get-intrinsic": {
1286
- "version": "1.2.7",
1286
+ "version": "1.3.0",
1287
1287
  "license": "MIT",
1288
1288
  "dependencies": {
1289
- "call-bind-apply-helpers": "^1.0.1",
1289
+ "call-bind-apply-helpers": "^1.0.2",
1290
1290
  "es-define-property": "^1.0.1",
1291
1291
  "es-errors": "^1.3.0",
1292
- "es-object-atoms": "^1.0.0",
1292
+ "es-object-atoms": "^1.1.1",
1293
1293
  "function-bind": "^1.1.2",
1294
- "get-proto": "^1.0.0",
1294
+ "get-proto": "^1.0.1",
1295
1295
  "gopd": "^1.2.0",
1296
1296
  "has-symbols": "^1.1.0",
1297
1297
  "hasown": "^2.0.2",
@@ -2432,7 +2432,7 @@
2432
2432
  "optional": true
2433
2433
  },
2434
2434
  "node_modules/ws": {
2435
- "version": "8.18.0",
2435
+ "version": "8.18.1",
2436
2436
  "license": "MIT",
2437
2437
  "engines": {
2438
2438
  "node": ">=10.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "8.7.3",
3
+ "version": "8.8.0",
4
4
  "description": "Command line client and development toolkit for the SAP Cloud Application Programming Model",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [
File without changes