@sap/cds-dk 7.9.4 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. package/CHANGELOG.md +2116 -0
  2. package/bin/add.js +18 -52
  3. package/bin/bind.js +2 -2
  4. package/bin/build.js +29 -4
  5. package/bin/cds.js +23 -11
  6. package/bin/compile/index.js +7 -6
  7. package/bin/completion/index.js +70 -46
  8. package/bin/completion/scripts/cds-bash.sh +4 -1
  9. package/bin/completion/scripts/cds-fish.fish +13 -0
  10. package/bin/completion/scripts/cds-ps.ps1 +2 -1
  11. package/bin/completion/scripts/cds-zsh.sh +2 -1
  12. package/bin/deploy/index.js +6 -5
  13. package/bin/deploy/to-sqlite.js +2 -2
  14. package/bin/extend.js +19 -50
  15. package/bin/help.js +2 -2
  16. package/bin/init.js +10 -77
  17. package/bin/login.js +13 -5
  18. package/bin/migrate.js +1 -1
  19. package/bin/pull.js +11 -0
  20. package/bin/repl.js +3 -0
  21. package/bin/test.js +5 -0
  22. package/bin/version.js +7 -7
  23. package/bin/watch.js +2 -11
  24. package/lib/bind/KubernetesApiAccessor.js +10 -9
  25. package/lib/build/buildTaskEngine.js +11 -7
  26. package/lib/build/buildTaskFactory.js +15 -43
  27. package/lib/build/buildTaskProviderFactory.js +16 -44
  28. package/lib/build/constants.js +3 -3
  29. package/lib/build/index.js +2 -2
  30. package/lib/build/{buildPlugin.js → plugin.js} +65 -46
  31. package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/Chart.yaml +1 -1
  32. package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/values.yaml +1 -1
  33. package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/service-instance/Chart.yaml +1 -1
  34. package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/service-instance/templates/_helpers.tpl +7 -0
  35. package/lib/build/plugins/helm/files/subcharts/service-instance/templates/servicebinding.yaml +32 -0
  36. package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/service-instance/templates/serviceinstance.yaml +3 -0
  37. package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/service-instance/values.schema.json +63 -0
  38. package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/service-instance/values.yaml +23 -1
  39. package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/Chart.yaml +1 -1
  40. package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/deployment.yaml +2 -0
  41. package/lib/build/plugins/helm/files/subcharts/web-application/templates/hpa.yaml +22 -0
  42. package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/pod-disruption-budget.yaml +1 -0
  43. package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/values.schema.json +49 -0
  44. package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/values.yaml +8 -2
  45. package/lib/{init/template/helm/files/chart → build/plugins/helm/files}/templates/_deployment_helpers.tpl +5 -0
  46. package/lib/build/plugins/helm/files/templates/_helpers.tpl +13 -0
  47. package/lib/build/plugins/helm/index.js +81 -0
  48. package/lib/build/plugins.js +12 -15
  49. package/lib/build/provider/featureToggleBuildPlugin.js +7 -39
  50. package/lib/build/provider/hana/2migration.js +168 -124
  51. package/lib/build/provider/hana/index.js +20 -25
  52. package/lib/build/provider/hana/template/.hdiconfig-all +3 -0
  53. package/lib/build/provider/hana/template/.hdiconfig-cloud +3 -0
  54. package/lib/build/provider/hana/template/package.json +4 -3
  55. package/lib/build/provider/internalBuildPlugin.js +32 -97
  56. package/lib/build/provider/internalBuildTaskProvider.js +10 -46
  57. package/lib/build/provider/java/index.js +2 -3
  58. package/lib/build/provider/mtx/index.js +5 -4
  59. package/lib/build/provider/mtx-extension/index.js +5 -2
  60. package/lib/build/provider/mtx-sidecar/index.js +2 -2
  61. package/lib/build/util.js +83 -12
  62. package/lib/compile/to-mermaid.js +108 -0
  63. package/lib/compile/to_asyncapi.js +7 -0
  64. package/lib/compile/to_openapi.js +7 -0
  65. package/lib/deploy/to-hana/hana.js +37 -34
  66. package/lib/deploy/to-hana/hdiDeployUtil.js +8 -6
  67. package/lib/env/schemas/cds-rc.js +1 -1
  68. package/lib/import/common.js +2 -2
  69. package/lib/import/odata/metadataconverter/MetadataConverter.js +4 -4
  70. package/lib/import/odata/v2parser.js +83 -197
  71. package/lib/import/odata/v4parser.js +1 -38
  72. package/lib/import/openapi/importOpenAPI.js +164 -42
  73. package/lib/index.js +10 -12
  74. package/lib/init/bas.js +91 -0
  75. package/lib/init/constants.js +16 -10
  76. package/lib/init/index.js +74 -150
  77. package/lib/init/merge.js +12 -20
  78. package/lib/init/mvn.js +103 -150
  79. package/lib/init/plugin.js +11 -7
  80. package/lib/init/projectReader.js +53 -19
  81. package/lib/init/registries/mta.js +51 -6
  82. package/lib/init/template/application-logging/files/package.json.hbs +7 -0
  83. package/lib/init/template/application-logging/index.js +13 -1
  84. package/lib/init/template/approuter/files/containerize.yaml.hbs +10 -0
  85. package/lib/init/template/approuter/files/mta.yaml.hbs +1 -2
  86. package/lib/init/template/approuter/files/values.yaml.hbs +19 -4
  87. package/lib/init/template/approuter/files/xs-app.json.hbs +12 -3
  88. package/lib/init/template/approuter/index.js +29 -25
  89. package/lib/init/template/attachments/files/mta.yaml.hbs +14 -0
  90. package/lib/init/template/attachments/files/package.json.hbs +5 -0
  91. package/lib/init/template/attachments/files/values.yaml +7 -0
  92. package/lib/init/template/attachments/index.js +53 -0
  93. package/lib/init/template/audit-logging/files/mta.yaml.hbs +14 -0
  94. package/lib/init/template/audit-logging/files/package.json.hbs +7 -0
  95. package/lib/init/template/audit-logging/index.js +66 -0
  96. package/lib/init/template/cf-manifest/index.js +4 -0
  97. package/lib/init/template/completion/completionSetup.js +54 -33
  98. package/lib/init/template/completion/index.js +7 -1
  99. package/lib/init/template/connectivity/index.js +4 -4
  100. package/lib/init/template/containerize/files/containerize.yaml.hbs +26 -0
  101. package/lib/init/template/containerize/index.js +24 -0
  102. package/lib/init/template/data/index.js +4 -0
  103. package/lib/init/template/destination/files/mta.yaml.hbs +25 -1
  104. package/lib/init/template/destination/index.js +12 -2
  105. package/lib/init/template/enterprise-messaging/index.js +6 -3
  106. package/lib/init/template/enterprise-messaging-shared/index.js +5 -1
  107. package/lib/init/template/extensibility/files/package.json.hbs +1 -1
  108. package/lib/init/template/extensibility/files/xs-security.json.hbs +2 -8
  109. package/lib/init/template/extensibility/index.js +3 -1
  110. package/lib/init/template/file-based-messaging/index.js +4 -0
  111. package/lib/init/template/h2/index.js +4 -0
  112. package/lib/init/template/hana/files/containerize.yaml.hbs +10 -0
  113. package/lib/init/template/hana/files/db/package.json +3 -2
  114. package/lib/init/template/hana/files/package.json.hbs +4 -7
  115. package/lib/init/template/hana/files/src/.hdiconfig +3 -0
  116. package/lib/init/template/hana/files/values.yaml.hbs +1 -2
  117. package/lib/init/template/hana/index.js +15 -7
  118. package/lib/init/template/helm/files/Chart.yaml.hbs +2 -3
  119. package/lib/init/template/helm/files/values.schema.json +2173 -0
  120. package/lib/init/template/helm/files/values.yaml.hbs +11 -6
  121. package/lib/init/template/helm/index.js +77 -18
  122. package/lib/init/template/helm-unified-runtime/index.js +19 -56
  123. package/lib/init/template/html5-repo/files/app-package.json.hbs +11 -7
  124. package/lib/init/template/html5-repo/files/manifest.json +13 -0
  125. package/lib/init/template/html5-repo/files/mta.yaml.hbs +41 -8
  126. package/lib/init/template/html5-repo/files/ui5-deploy.yaml.hbs +21 -0
  127. package/lib/init/template/html5-repo/files/ui5.yaml.hbs +2 -29
  128. package/lib/init/template/html5-repo/files/values.yaml.hbs +6 -2
  129. package/lib/init/template/html5-repo/files/xs-app.json.hbs +21 -8
  130. package/lib/init/template/html5-repo/index.js +33 -30
  131. package/lib/init/template/http/index.js +36 -12
  132. package/lib/init/template/java/index.js +18 -12
  133. package/lib/init/template/kafka/index.js +4 -0
  134. package/lib/init/template/lint/index.js +7 -2
  135. package/lib/init/template/liquibase/index.js +4 -0
  136. package/lib/init/template/local-messaging/index.js +4 -0
  137. package/lib/init/template/malware-scanner/files/mta.yaml.hbs +14 -0
  138. package/lib/init/template/malware-scanner/files/package.json.hbs +17 -0
  139. package/lib/init/template/malware-scanner/files/values.yaml +7 -0
  140. package/lib/init/template/malware-scanner/index.js +47 -0
  141. package/lib/init/template/mta/files/mta.yaml.hbs +3 -3
  142. package/lib/init/template/mta/files/package.json +1 -1
  143. package/lib/init/template/mta/index.js +4 -0
  144. package/lib/init/template/mtx/index.js +4 -0
  145. package/lib/init/template/multitenancy/files/containerize.yaml.hbs +10 -0
  146. package/lib/init/template/multitenancy/files/mta.yaml.hbs +0 -1
  147. package/lib/init/template/multitenancy/files/package.json.hbs +1 -1
  148. package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +6 -5
  149. package/lib/init/template/multitenancy/files/values.yaml.hbs +14 -24
  150. package/lib/init/template/multitenancy/index.js +50 -28
  151. package/lib/init/template/nodejs/files/eslint.config.mjs +2 -0
  152. package/lib/init/template/nodejs/files/package.json.hbs +2 -3
  153. package/lib/init/template/nodejs/index.js +4 -0
  154. package/lib/init/template/notifications/files/mta.yaml.hbs +2 -2
  155. package/lib/init/template/notifications/index.js +4 -0
  156. package/lib/init/template/pipeline/files/.pipeline/config.yml +1 -1
  157. package/lib/init/template/pipeline/files/Jenkinsfile +1 -1
  158. package/lib/init/template/pipeline/index.js +4 -0
  159. package/lib/init/template/portal/files/mta.yaml.hbs +52 -0
  160. package/lib/init/template/portal/files/package.json.hbs +11 -0
  161. package/lib/init/template/portal/files/package.sidecar.json +7 -0
  162. package/lib/init/template/portal/files/portal-site/CommonDataModel.json.hbs +97 -0
  163. package/lib/init/template/portal/files/portal-site/i18n/catalog.properties +1 -0
  164. package/lib/init/template/portal/files/portal-site/i18n/catalog_de.properties +1 -0
  165. package/lib/init/template/portal/files/portal-site/i18n/group.properties +1 -0
  166. package/lib/init/template/portal/files/portal-site/i18n/group_de.properties +1 -0
  167. package/lib/init/template/portal/index.js +84 -0
  168. package/lib/init/template/postgres/index.js +5 -1
  169. package/lib/init/template/redis-messaging/index.js +4 -0
  170. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_de.properties +3 -0
  171. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_en.properties +3 -0
  172. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +13 -1
  173. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n.properties +2 -2
  174. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_de.properties +3 -0
  175. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_en.properties +4 -0
  176. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +3 -3
  177. package/lib/init/template/sample/files/app/index.html +1 -1
  178. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Authors.csv +5 -5
  179. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books.csv +6 -6
  180. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books_texts.csv +5 -5
  181. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Genres.csv +16 -16
  182. package/lib/init/template/sample/files/nodejs/cat-service.js +2 -2
  183. package/lib/init/template/sample/files/package.json.hbs +3 -0
  184. package/lib/init/template/sample/index.js +16 -9
  185. package/lib/init/template/sqlite/index.js +4 -0
  186. package/lib/init/template/tiny-sample/files/nodejs/db/data/my.bookshop-Books.csv +3 -3
  187. package/lib/init/template/tiny-sample/files/nodejs/srv/cat-service.cds.hbs +1 -1
  188. package/lib/init/template/tiny-sample/index.js +4 -0
  189. package/lib/init/template/toggles/files/package.json.hbs +1 -1
  190. package/lib/init/template/toggles/index.js +4 -0
  191. package/lib/init/template/typer/index.js +4 -0
  192. package/lib/init/template/typescript/files/package.json +12 -0
  193. package/lib/init/template/typescript/files/tsconfig.json +13 -0
  194. package/lib/init/template/typescript/index.js +39 -0
  195. package/lib/init/template/workzone/index.js +5 -0
  196. package/lib/init/template/workzone-standard/files/manifest.json.hbs +6 -0
  197. package/lib/init/template/workzone-standard/index.js +30 -0
  198. package/lib/init/template/xsuaa/files/package.json.hbs +1 -1
  199. package/lib/init/template/xsuaa/files/values.yaml.hbs +3 -2
  200. package/lib/init/template/xsuaa/index.js +11 -7
  201. package/lib/init/validate.js +27 -0
  202. package/lib/lint/generators.js +1 -1
  203. package/lib/lint/io.js +25 -7
  204. package/lib/mtx/auth_manager.js +64 -30
  205. package/lib/mtx/download_legacy.js +2 -2
  206. package/lib/mtx/index.js +0 -4
  207. package/lib/mtx/params.js +32 -38
  208. package/lib/mtx/pull.js +151 -43
  209. package/lib/mtx/push.js +3 -3
  210. package/lib/mtx/settings_manager.js +75 -35
  211. package/lib/mtx/tenant_action.js +1 -1
  212. package/lib/mtx/util/cf.js +1 -1
  213. package/lib/mtx/util/fs.js +26 -1
  214. package/lib/mtx/util/logging.js +7 -3
  215. package/lib/util/command.js +0 -2
  216. package/lib/util/fs.js +2 -3
  217. package/lib/util/question.js +28 -0
  218. package/lib/util/term.js +5 -3
  219. package/npm-shrinkwrap.json +231 -1089
  220. package/package.json +13 -8
  221. package/bin/activate.js +0 -77
  222. package/lib/compile/asyncapi/channels.js +0 -22
  223. package/lib/compile/asyncapi/components/messageTraits/index.js +0 -89
  224. package/lib/compile/asyncapi/components/messages/index.js +0 -173
  225. package/lib/compile/asyncapi/components/schemas/annotations.js +0 -33
  226. package/lib/compile/asyncapi/components/schemas/array.js +0 -8
  227. package/lib/compile/asyncapi/components/schemas/cache.js +0 -14
  228. package/lib/compile/asyncapi/components/schemas/definition.js +0 -187
  229. package/lib/compile/asyncapi/components/schemas/enum.js +0 -16
  230. package/lib/compile/asyncapi/components/schemas/examples.js +0 -10
  231. package/lib/compile/asyncapi/components/schemas/index.js +0 -14
  232. package/lib/compile/asyncapi/components/schemas/localized.js +0 -29
  233. package/lib/compile/asyncapi/components/schemas/primitiveElement.js +0 -41
  234. package/lib/compile/asyncapi/components/schemas/ref.js +0 -45
  235. package/lib/compile/asyncapi/components/schemas/utils.js +0 -108
  236. package/lib/compile/asyncapi/index.js +0 -247
  237. package/lib/compile/asyncapi/message.js +0 -13
  238. package/lib/compile/asyncapi/utils.js +0 -27
  239. package/lib/compile/openapi/csdl2openapi.js +0 -2448
  240. package/lib/compile/openapi/index.js +0 -68
  241. package/lib/init/template/helm/files/chart/templates/NOTES.txt +0 -14
  242. package/lib/init/template/helm/files/chart/templates/_helpers.tpl +0 -28
  243. package/lib/init/template/helm/files/chart/values.root.schema.json +0 -61
  244. package/lib/init/template/helm/files/chart/values.schema.json +0 -366
  245. package/lib/init/template/helm-unified-runtime/files/Chart.yaml.hbs +0 -10
  246. package/lib/init/template/helm-unified-runtime/files/templates/_deployment_helpers.tpl +0 -90
  247. package/lib/init/template/helm-unified-runtime/files/templates/_helpers.tpl +0 -28
  248. package/lib/init/template/helm-unified-runtime/files/values.root.schema.json +0 -61
  249. package/lib/init/template/helm-unified-runtime/files/values.schema.json +0 -366
  250. package/lib/init/template/helm-unified-runtime/files/values.yaml.hbs +0 -26
  251. package/lib/init/template/helm-unified-runtime/package.json +0 -4
  252. package/lib/init/template/multitenancy/files/mtxs-configmap-java-approuter.yaml +0 -20
  253. package/lib/init/template/multitenancy/files/mtxs-configmap-java.yaml +0 -18
  254. package/lib/init/template/multitenancy/files/mtxs-configmap-nodejs-approuter.yaml +0 -12
  255. package/lib/init/template/multitenancy/files/mtxs-configmap-nodejs.yaml +0 -12
  256. package/lib/init/template/multitenancy/files/saas-registry-secret.yaml +0 -9
  257. package/lib/init/template/nodejs/files/eslint.config.js +0 -13
  258. package/lib/init/template/notebook/index.js +0 -11
  259. package/lib/init/template/samples/index.js +0 -9
  260. package/lib/mtx/activate.js +0 -238
  261. package/lib/mtx/extend.js +0 -173
  262. package/lib/util/exec.d.ts +0 -54
  263. package/lib/util/exec.js +0 -211
  264. /package/lib/{init/template/approuter/files → build/plugins/helm/files/plugins/approuter}/approuter-configmap.yaml +0 -0
  265. /package/lib/{init/template/connectivity/files → build/plugins/helm/files/plugins/connectivity}/connectivity-proxy-info.yaml +0 -0
  266. /package/lib/{init/template/html5-repo/files → build/plugins/helm/files/plugins/html5-repo}/html5-apps-deployer-configmap.yaml +0 -0
  267. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/templates/_helpers.tpl +0 -0
  268. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/templates/job.yaml +0 -0
  269. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/templates/secret.yaml +0 -0
  270. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/templates/service-account.yaml +0 -0
  271. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/templates/service-binding.yaml +0 -0
  272. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/values.schema.json +0 -0
  273. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/NOTES.txt +0 -0
  274. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/_api.tpl +0 -0
  275. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/_helpers.tpl +0 -0
  276. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/api-rule.yaml +0 -0
  277. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/network-policy.yaml +0 -0
  278. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/rbac.yaml +0 -0
  279. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/secret.yaml +0 -0
  280. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/service-account.yaml +0 -0
  281. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/service-binding.yaml +0 -0
  282. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/service.yaml +0 -0
  283. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/virtual-service.yaml +0 -0
  284. /package/lib/{init/template/helm-unified-runtime → build/plugins/helm}/files/templates/NOTES.txt +0 -0
  285. /package/lib/init/template/{nodejs/files → java}/.cdsrc.json +0 -0
  286. /package/lib/init/template/tiny-sample/files/nodejs/db/{data-model.cds → schema.cds} +0 -0
package/lib/mtx/pull.js CHANGED
@@ -2,7 +2,7 @@ const cds = require ('../cds')
2
2
  const { local, tar } = cds.utils;
3
3
 
4
4
  const fs = require('fs');
5
- const { join } = require('path');
5
+ const { join, dirname } = require('path');
6
6
  const axios = require('axios');
7
7
 
8
8
  const { login } = require('./auth_manager');
@@ -10,73 +10,181 @@ const { login } = require('./auth_manager');
10
10
  require('../util/axios').pruneErrors();
11
11
  const { getMessage } = require('./util/logging');
12
12
  const { handleHttpError } = require('./util/errors');
13
+ const { grepQR } = require('./util/fs');
14
+ const { escapeRegex } = require('./util/strings');
13
15
 
14
- module.exports = class Pull {
16
+ function tabSize(jsonString, defaultSize) {
17
+ return /^ +/m.exec(jsonString)?.[0].length ?? defaultSize;
18
+ }
15
19
 
16
- static async run(paramValues) {
17
- if (!tar) {
18
- throw `cds pull requires @sap/cds version >= 6.2. Current version is ${cds.version}\n`;
20
+ function getPackageContentsAndTabSize(pkgPath, defaultContents = undefined, defaultTabSize = 0) {
21
+ try {
22
+ const contents = fs.readFileSync(pkgPath, { encoding: 'utf-8' });
23
+ const tab = tabSize(contents, defaultTabSize);
24
+ return [JSON.parse(contents), tab];
25
+ } catch (error) {
26
+ if (defaultContents) {
27
+ return [defaultContents, defaultTabSize];
19
28
  }
29
+ throw getMessage('package.json missing or unreadable', { error });
30
+ }
31
+ }
32
+
33
+ function writeObj(obj, objPath, tabSize) {
34
+ if (!fs.existsSync(dirname(objPath))) {
35
+ fs.mkdirSync(dirname(objPath), { recursive: true });
36
+ }
37
+ fs.writeFileSync(objPath, JSON.stringify(obj, undefined, tabSize), { encoding: 'utf-8' });
38
+ }
39
+
40
+ const INVALID_BASE_MODEL_NAME = /^[._]/; // See https://docs.npmjs.com/cli/v10/configuring-npm/package-json#name
20
41
 
42
+
43
+ module.exports = class Pull {
44
+
45
+ static async run(paramValues) {
21
46
  const params = await login(paramValues);
22
- const url = params.get('appUrl')
23
- const projectFolder = params.get('projectFolder');
24
- const env = cds.env.for('cds', projectFolder);
25
- const target = join(projectFolder, 'node_modules', this.getAppPackageName(env));
26
- const subdomain = params.get('subdomain') // REVISIT: Why are subdomains not transparently encoded in URLs?
47
+ return new Pull(params).run();
48
+ }
49
+
50
+ params;
51
+ projectFolder;
52
+ env;
53
+ baseModelName;
54
+ packagePath;
55
+ baseModelFolder;
56
+ #amendPackage = {};
57
+ #baseModelWorkspace;
58
+ #packageContentsAndTabSize;
59
+
60
+ static get DEFAULTS() {
61
+ return {
62
+ tabSize: 2,
63
+ baseModelFolder: '.base',
64
+ baseModelName: 'base-model'
65
+ };
66
+ }
67
+
68
+ constructor(params) {
69
+ this.params = params;
70
+ this.projectFolder = params.get('projectFolder');
71
+ this.env = cds.env.for('cds', this.projectFolder);
72
+ this.baseModelName = this.env.extends;
73
+ this.packagePath = join(this.projectFolder, 'package.json');
74
+ this.baseModelFolder = join(this.projectFolder, this.baseModelWorkspace);
75
+ }
27
76
 
28
- this.amendPackageJson(projectFolder, env);
77
+ async run() {
78
+ console.log(`Pulling app base model`, {
79
+ from: this.params.get('appUrl'),
80
+ to: local(this.baseModelFolder),
81
+ ...(this.params.has('subdomain') && { subdomain: this.params.get('subdomain') })
82
+ });
29
83
 
30
- console.log(`Pulling app base model`, Object.assign({ from: url, to: local(target) }, subdomain && {subdomain}))
84
+ this.validateBaseModelName();
31
85
 
32
- fs.rmSync(target, { force: true, recursive: true });
33
- fs.mkdirSync(target, { recursive: true });
86
+ fs.rmSync(this.baseModelFolder, { force: true, recursive: true });
87
+ fs.mkdirSync(this.baseModelFolder, { recursive: true });
34
88
 
35
- const baseModelTgz = await this.getTgz(params);
36
- await tar.xz(baseModelTgz).to(target);
89
+ const baseModelTgz = await this.pullTgz();
90
+ await tar.xz(baseModelTgz).to(this.baseModelFolder);
91
+ Pull.transformBasePackage(this.baseModelFolder, this.baseModelName);
92
+ this.amendExtPackage();
37
93
 
38
- console.log(`Finished. Refer to the base model like so: using from '${this.getAppPackageName(env)}'`);
94
+ console.log(`Finished. Refer to the base model like so: using from '${this.baseModelName}'`);
95
+
96
+ const baseModelSymlink = join(this.projectFolder, 'node_modules', this.baseModelName);
97
+ if (!fs.statSync(baseModelSymlink, { throwIfNoEntry: false })?.isSymbolicLink()) {
98
+ console.log(`Note: the base model has been pulled to an NPM workspace in folder '${local(this.baseModelFolder)}'. Run 'npm install' to install it.`);
99
+ }
39
100
  }
40
101
 
41
- static getAppPackageName(env) {
42
- return env.extends || '_base';
102
+ get baseModelWorkspace() {
103
+ const defaultFolder = Pull.DEFAULTS.baseModelFolder;
104
+ if (!this.#baseModelWorkspace) {
105
+ if (this.packageContents.workspaces) {
106
+ if (this.packageContents.workspaces.includes(defaultFolder)) {
107
+ this.#baseModelWorkspace = defaultFolder;
108
+ } else {
109
+ this.#baseModelWorkspace = this.packageContents.workspaces
110
+ .map(ws => String(ws))
111
+ .find(ws => {
112
+ try {
113
+ return require(join(this.projectFolder, ws, 'package.json')).name === this.baseModelName;
114
+ } catch (e) { /* ignore */ }
115
+ }) ?? defaultFolder;
116
+ }
117
+ } else {
118
+ this.#amendPackage.workspaces = [defaultFolder];
119
+ this.#baseModelWorkspace = defaultFolder;
120
+ }
121
+ }
122
+ return this.#baseModelWorkspace;
123
+ }
124
+
125
+ validateBaseModelName() {
126
+ if (!this.baseModelName) {
127
+ throw getMessage(`Missing configuration 'cds.extends' in package.json`);
128
+ }
129
+ if (this.baseModelName.match(INVALID_BASE_MODEL_NAME)) {
130
+ console.warn(`Invalid base-model name '${this.baseModelName}': must not start with '_' or '.'. ` +
131
+ `Using '${Pull.DEFAULTS.baseModelName}' instead.`);
132
+ const invalidNameInUse = grepQR(this.projectFolder, ['.cds', '.csn'], new RegExp(`\\b${escapeRegex(this.baseModelName)}\\b`));
133
+ if (invalidNameInUse) {
134
+ console.error(`Some model files seem to reference the invalid name. Please update them manually.`);
135
+ }
136
+ this.baseModelName = Pull.DEFAULTS.baseModelName;
137
+ this.#amendPackage.extends = this.baseModelName;
138
+ }
43
139
  }
44
140
 
45
- static async getTgz(params) {
46
- const pullUrl = `${(params.get('appUrl'))}/-/cds/extensibility/pull`;
47
- const options = { ...params.get('reqAuth'), responseType: 'arraybuffer' };
141
+ async pullTgz() {
142
+ const pullUrl = `${(this.params.get('appUrl'))}/-/cds/extensibility/pull`;
143
+ const options = { ...this.params.get('reqAuth'), responseType: 'arraybuffer' };
48
144
 
49
145
  return axios.post(pullUrl, {}, options)
50
146
  .then(response => Buffer.from(response.data))
51
- .catch(error => handleHttpError(error, params, { url: pullUrl }));
147
+ .catch(error => handleHttpError(error, this.params, { url: pullUrl }));
52
148
  }
53
149
 
54
- static getPackageJsonIndentation(packageContents) {
55
- return /^ +/m.exec(packageContents)?.[0].length ?? 2;
56
- }
150
+ static transformBasePackage(baseModelFolder, baseModelName) {
151
+ const baseRcPath = join(baseModelFolder, '.cdsrc.json');
152
+ const baseRc = getPackageContentsAndTabSize(baseRcPath, {}, this.DEFAULTS.tabSize)[0];
153
+ fs.rmSync(baseRcPath, { force: true });
154
+
155
+ const basePkgPath = join(baseModelFolder, 'package.json');
156
+ const basePkg = {
157
+ ...getPackageContentsAndTabSize(basePkgPath, {}, this.DEFAULTS.tabSize)[0],
158
+ name: baseModelName,
159
+ cds: baseRc
160
+ };
57
161
 
58
- static formatPackageJson(newObj, oldContents) {
59
- return JSON.stringify(newObj, undefined, this.getPackageJsonIndentation(oldContents));
162
+ writeObj(basePkg, basePkgPath, this.DEFAULTS.tabSize);
60
163
  }
61
164
 
62
- static amendPackageJson(projectFolder, env) {
63
- if ('extends' in env) {
165
+ amendExtPackage() {
166
+ if (Object.keys(this.#amendPackage).length === 0) {
64
167
  return;
65
168
  }
66
169
  console.log(`Amending extension package.json with project configuration`);
67
- const packageJson = join(projectFolder, 'package.json');
68
- let packageContents;
69
- try {
70
- packageContents = JSON.parse(fs.readFileSync(packageJson, { encoding: 'utf-8' }));
71
- } catch (error) {
72
- throw getMessage('package.json missing or unreadable', { error });
170
+ fs.writeFileSync(this.packagePath, JSON.stringify({
171
+ ...this.packageContents,
172
+ ...this.#amendPackage
173
+ }, undefined, this.tabSize));
174
+ this.#packageContentsAndTabSize = undefined;
175
+ }
176
+
177
+ get packageContents() {
178
+ if (!this.#packageContentsAndTabSize) {
179
+ this.#packageContentsAndTabSize = getPackageContentsAndTabSize(this.packagePath, undefined, Pull.DEFAULTS.tabSize);
73
180
  }
74
- fs.writeFileSync(packageJson, this.formatPackageJson({
75
- ...packageContents,
76
- cds: {
77
- ...(packageContents.cds ?? {}),
78
- extends: this.getAppPackageName(projectFolder) // Default unless configured through current process.env
79
- }
80
- }));
181
+ return this.#packageContentsAndTabSize[0];
182
+ }
183
+
184
+ get tabSize() {
185
+ if (!this.#packageContentsAndTabSize) {
186
+ this.#packageContentsAndTabSize = getPackageContentsAndTabSize(this.packagePath, undefined, Pull.DEFAULTS.tabSize);
187
+ }
188
+ return this.#packageContentsAndTabSize[1];
81
189
  }
82
190
  }
package/lib/mtx/push.js CHANGED
@@ -1,4 +1,4 @@
1
- const cds = require ('../cds'), { local } = cds.utils;
1
+ const { utils: { local }} = require ('../cds');
2
2
  const { join } = require('path');
3
3
  const { readFileSync, existsSync } = require('fs');
4
4
  const axios = require('axios');
@@ -33,7 +33,7 @@ module.exports = class Push {
33
33
  const { src, content } = await this.readSource(params, pushOptions);
34
34
  const url = params.get('appUrl');
35
35
  const subdomain = params.get('subdomain');
36
- const target = Object.assign({ url }, subdomain && { subdomain });
36
+ const target = { url, ...subdomain && { subdomain } };
37
37
 
38
38
  console.log(`\nPushing extension '${extensionName}' from`, { src }, 'to', target);
39
39
  const push = await this.pushTgz(params, content, extensionName);
@@ -51,7 +51,7 @@ module.exports = class Push {
51
51
  src = local(src);
52
52
  } else {
53
53
  if (!schemaRegex.test(src)) {
54
- throw getMessage(`Nonexistent path: ${src}`, { command: 'push' });
54
+ throw getMessage(`Non-existent path: ${src}`, { command: 'push' });
55
55
  }
56
56
  try {
57
57
  content = (await axios.get(src, { responseEncoding: 'base64' })).data;
@@ -8,6 +8,7 @@ const cds = require('../cds');
8
8
  const { requireGlobal } = require('./util/dependencies');
9
9
  const { getAppFromSuggestions, getSubdomain } = require('./util/cf');
10
10
  const isBas = require('./util/env');
11
+ const { getMessage } = require('./util/logging');
11
12
  const Question = require('./util/question');
12
13
  const { httpSchema, httpsSchema, schemaRegex, localhostRegex } = require('./util/urls');
13
14
  const { capitalize } = require('./util/strings');
@@ -34,8 +35,9 @@ const KEYRING_DESC = {
34
35
  };
35
36
 
36
37
  const MTX_FULLY_QUALIFIED = 'com.sap.cds.mtx';
37
- const OAUTH_PATH_OLD_MTX = '/mtx/v1/oauth/token';
38
- const OAUTH_PATH_STREAMLINED_MTX = '/-/cds/login/token';
38
+ const OAUTH_PATH_LEGACY = '/mtx/v1/oauth/token';
39
+ const OAUTH_PATH = '/-/cds/login/token';
40
+ const OAUTH_META_PATH = '/-/cds/login/authorization-metadata';
39
41
 
40
42
  const SETTINGS_DIR = path.join(os.homedir(), CONFIG_SUBDIRS[os.platform()] || '', MTX_FULLY_QUALIFIED);
41
43
  const SETTINGS_FILE = 'projects.json';
@@ -92,30 +94,46 @@ async function getSubdomainFromCfAndNotify(appName) {
92
94
  return subdomain;
93
95
  }
94
96
 
95
- async function getTokenBaseUrl(params, renewUrl) {
97
+ function appUrlWith(pathname, params) {
98
+ const url = new URL(params.get('appUrl'));
99
+ url.pathname = url.pathname.replace(/\/*$/, pathname);
100
+ return url.toString();
101
+ }
102
+
103
+ async function getTokenUrl(params, renewUrl) {
96
104
  if (params.has('tokenUrl') && !renewUrl) {
97
105
  return params.get('tokenUrl');
98
106
  }
99
107
  if (!params.has('appUrl')) {
100
108
  throw 'Failed to determine token URL: app URL not given';
101
109
  }
102
- const appUrlWith = pathname => {
103
- const url = new URL(params.get('appUrl'));
104
- url.pathname = url.pathname.replace(/\/*$/, pathname);
105
- return url.toString();
106
- };
110
+ return appUrlWith(OAUTH_PATH, params);
111
+ }
107
112
 
108
- let path = OAUTH_PATH_OLD_MTX;
113
+ async function fetchPasscodeUrl(params) {
114
+ const url = appUrlWith(OAUTH_META_PATH, params);
115
+ DEBUG?.(`Trying to get passcode URL from GET`, url);
116
+ let responseData;
109
117
  try {
110
- await axios.head(appUrlWith(OAUTH_PATH_STREAMLINED_MTX));
111
- path = OAUTH_PATH_STREAMLINED_MTX;
118
+ const response = await axios.get(url /* no auth required */);
119
+ responseData = response.data;
112
120
  } catch (error) {
113
- if (![401, 404].includes(error.status)) {
114
- throw error;
121
+ if (error.status === 404) {
122
+ DEBUG?.(`Request unsupported by server, ignoring (${error.message.replace(/ *Details:.*/s, '')})`);
123
+ } else {
124
+ DEBUG?.(getMessage(`Getting passcode URL failed`, { error }));
115
125
  }
116
- DEBUG?.(`Falling back to legacy token URL: ${error.message}`);
126
+ return undefined;
127
+ }
128
+ DEBUG?.(`Received ${JSON.stringify(responseData)}`);
129
+ if (responseData.includes?.('<html')) {
130
+ throw 'HTML response received. Check if route to MTX is configured correctly in App Router.';
117
131
  }
118
- return appUrlWith(path);
132
+ if (responseData.signed_metadata) {
133
+ DEBUG?.(`Unsupported signed metadata received. Since these should take precedence, ignoring response.`);
134
+ return undefined;
135
+ }
136
+ return responseData.passcode_url;
119
137
  }
120
138
 
121
139
  function getKeyringAccountName(params) {
@@ -135,7 +153,7 @@ function logConfigPaths() {
135
153
  DEBUG?.(`Authentication data is stored in ${CONFIG.paths.auth} (${TOKEN_STORAGE_DESC.plain}) or ${CONFIG.keyringDesignation} (${TOKEN_STORAGE_DESC.keyring})`);
136
154
  }
137
155
 
138
- function notifyLoggedIn(params) {
156
+ function notifyLogin(params) {
139
157
  return params.get('renewLogin')
140
158
  ? console.log.bind(console)
141
159
  : DEBUG;
@@ -201,8 +219,13 @@ class SettingsManager {
201
219
  const loadedSettings = await this._loadFromFile(projectFolder);
202
220
  DEBUG?.(`Loaded project settings for ${params.get('projectFolder')}: ${loadedSettings.format()}`);
203
221
 
222
+ const clashing = ['passcode', 'clientid', 'username'].filter(param => params.has(param));
223
+ if (clashing.length > 1) {
224
+ throw `Conflicting parameters: ${clashing.join(', ')}. Please provide only one of them.`;
225
+ }
226
+
204
227
  if (params.has('passcode') && loadedSettings.has('username')) {
205
- console.log(`Unsetting username${(loadedSettings.has('isEmptyPassword') ? ' and empty-password hint' : '')}`);
228
+ console.log(`Discarding saved username${(loadedSettings.has('isEmptyPassword') ? ' and empty-password hint' : '')}: passcode given`);
206
229
  loadedSettings.delete('username');
207
230
  loadedSettings.delete('isEmptyPassword');
208
231
  }
@@ -219,6 +242,23 @@ class SettingsManager {
219
242
  }
220
243
 
221
244
  static async updateUrls(params, logout, loadedSettings) {
245
+ function secure(url, label) {
246
+ if (!url) {
247
+ return url;
248
+ }
249
+ if (url.startsWith(httpSchema) && !localhostRegex.test(url)) {
250
+ url = url.replace(httpSchema, httpsSchema);
251
+ DEBUG?.(`Replaced HTTP with HTTPS in ${label}: ${url}`);
252
+ } else if (!schemaRegex.test(url)) {
253
+ if (localhostRegex.test(url)) {
254
+ url = httpSchema + url;
255
+ } else {
256
+ url = httpsSchema + url;
257
+ }
258
+ DEBUG?.(`Added schema to ${label}: ${url}`);
259
+ }
260
+ return url;
261
+ }
222
262
  async function updateAppUrl() {
223
263
  if (!logout && !params.has('appUrl')) {
224
264
  // Get appUrl from CF
@@ -231,30 +271,23 @@ class SettingsManager {
231
271
  }
232
272
 
233
273
  // Prefix URL schema
234
- let appUrl = params.get('appUrl');
274
+ let appUrl = secure(params.get('appUrl'), 'app URL');
235
275
  if (appUrl.endsWith('/')) {
236
276
  appUrl = appUrl.replace(/\/+$/, '');
237
277
  DEBUG?.(`Removed trailing slash from app URL: ${appUrl}`);
238
278
  }
239
- if (!schemaRegex.test(appUrl)) {
240
- if (localhostRegex.test(appUrl)) {
241
- appUrl = httpSchema + appUrl;
242
- } else {
243
- appUrl = httpsSchema + appUrl;
244
- }
245
- DEBUG?.(`Added schema to app URL: ${appUrl}`);
246
- }
247
279
  params.set('appUrl', appUrl);
248
280
  if (params.get('appUrl') !== loadedSettings.get('appUrl')) {
249
- DEBUG?.(`Updated app URL from '${loadedSettings.get('appUrl')}' (loaded) to '${appUrl}'`);
281
+ DEBUG?.(`Updated app URL from loaded value '${loadedSettings.get('appUrl')}'`);
250
282
  }
283
+ console.log(`App URL: ${params.get('appUrl')}`);
251
284
  }
252
285
  async function updateTokenUrl() {
253
- const tokenUrl = params.get('tokenUrl');
286
+ const tokenUrl = secure(params.get('tokenUrl'), 'token URL');
254
287
  const renewUrl = !logout && !params.get('skipToken')
255
- && (!tokenUrl || tokenUrl.includes('//-/cds') || tokenUrl.includes(OAUTH_PATH_OLD_MTX) || params.get('appUrl') !== loadedSettings.get('appUrl'));
288
+ && (!tokenUrl || tokenUrl.includes('//-/cds') || tokenUrl.includes(OAUTH_PATH_LEGACY) || params.get('appUrl') !== loadedSettings.get('appUrl'));
256
289
  if (renewUrl) {
257
- const tokenUrl = await getTokenBaseUrl(params, renewUrl);
290
+ const tokenUrl = await getTokenUrl(params, renewUrl);
258
291
  params.set('tokenUrl', tokenUrl);
259
292
  DEBUG?.(`Updated token URL from '${loadedSettings.get('tokenUrl')}' (loaded) to '${tokenUrl}'`);
260
293
  }
@@ -317,7 +350,13 @@ class SettingsManager {
317
350
  }
318
351
  }
319
352
  async function addPasscode() {
320
- const prompt = `Passcode${params.get('passcodeUrl') ? ' (visit ' + params.get('passcodeUrl') + ' to generate)' : ''}: `;
353
+ if (!params.has('passcodeUrl')) {
354
+ const passcodeUrl = await fetchPasscodeUrl(params);
355
+ if (passcodeUrl) {
356
+ params.set('passcodeUrl', passcodeUrl);
357
+ }
358
+ }
359
+ const prompt = `Passcode${params.has('passcodeUrl') ? ' (visit ' + params.get('passcodeUrl') + ' to generate)' : ''}: `;
321
360
  params.set('passcode', (await Question.askQuestion(prompt, undefined, true)).trim());
322
361
  console.log();
323
362
  if (params.get('passcode') === '') {
@@ -515,7 +554,7 @@ class SettingsManager {
515
554
  (allAuthValues[appUrl] || (allAuthValues[appUrl] = {}))[params.get('subdomain')] = authValues;
516
555
  await fs.promises.mkdir(path.dirname(CONFIG.paths.auth), { recursive: true })
517
556
  await fs.promises.writeFile(CONFIG.paths.auth, JSON.stringify(allAuthValues, null, 2));
518
- notifyLoggedIn(params)?.(`Saved authentication data to ${location}`);
557
+ notifyLogin(params)?.(`Saved authentication data to ${location}`);
519
558
  } else if (allAuthValues[appUrl]) {
520
559
  delete allAuthValues[appUrl][params.get('subdomain')];
521
560
  if (Object.keys(allAuthValues[appUrl]).length === 0) {
@@ -525,7 +564,7 @@ class SettingsManager {
525
564
  await fs.promises.writeFile(CONFIG.paths.auth, JSON.stringify(allAuthValues, null, 2));
526
565
  console.log('Deleted authentication data from', location);
527
566
  } else {
528
- notifyLoggedIn(params)?.(`No authentication data to delete from ${location}`);
567
+ notifyLogin(params)?.(`No authentication data to delete from ${location}`);
529
568
  }
530
569
  }
531
570
 
@@ -538,7 +577,7 @@ class SettingsManager {
538
577
 
539
578
  if (auth !== null) {
540
579
  await keytar.setPassword(MTX_FULLY_QUALIFIED, getKeyringAccountName(params), JSON.stringify(auth));
541
- notifyLoggedIn(params)?.(`Saved authentication data to ${location}`);
580
+ notifyLogin(params)?.(`Saved authentication data to ${location}`);
542
581
  } else {
543
582
  await keytar.deletePassword(MTX_FULLY_QUALIFIED, getKeyringAccountName(params));
544
583
  console.log('Deleted authentication data from', location);
@@ -663,5 +702,6 @@ class SettingsManager {
663
702
 
664
703
  module.exports = {
665
704
  SettingsManager,
666
- other
705
+ other,
706
+ notifyLogin
667
707
  };
@@ -23,7 +23,7 @@ module.exports = class TenantAction {
23
23
  */
24
24
  constructor(command) {
25
25
  if (!(command in COMMANDS)) {
26
- throw new Error(`invalid command: ${command}`);
26
+ throw `invalid command: ${command}`;
27
27
  }
28
28
  this.command = command;
29
29
  this.gerund = command.replace(/e$/, 'ing');
@@ -9,7 +9,7 @@ const { schemaRegex } = require('./urls');
9
9
  const DEBUG = cds.debug('cli');
10
10
 
11
11
  function runCf(args, callback) {
12
- const env = Object.assign({}, process.env, { LC_ALL: 'en_US.UTF-8' }); // enforce English headings
12
+ const env = { ...process.env, LC_ALL: 'en_US.UTF-8' }; // enforce English headings
13
13
  return execFile('cf', args, { env }, callback);
14
14
  }
15
15
 
@@ -72,11 +72,36 @@ function isParent(maybeParent, maybeChild) {
72
72
  return ! path.relative(maybeParent, maybeChild).startsWith('..');
73
73
  }
74
74
 
75
+ /**
76
+ * Searches for a regular expression in a directory tree
77
+ * @param folder directory to search in
78
+ * @param extensions file extensions to search for (use lower-case strings)
79
+ * @param regex regular expression to search for
80
+ * @return {boolean} true if the regular expression was found in a file
81
+ */
82
+ function grepQR(folder, extensions, regex) {
83
+ for (const dirent of fs.readdirSync(folder, { withFileTypes: true })) {
84
+ if (dirent.isFile() &&
85
+ extensions.some(x => dirent.name.toLowerCase().endsWith(x))) {
86
+ try {
87
+ if (regex.test(fs.readFileSync(path.join(folder, dirent.name), { encoding: 'utf-8' }))) {
88
+ return true;
89
+ }
90
+ } catch (error) { /* ignore */ }
91
+ }
92
+ if (dirent.isDirectory() && grepQR(path.join(folder, dirent.name), extensions, regex)) {
93
+ return true;
94
+ }
95
+ }
96
+ return false;
97
+ }
98
+
75
99
 
76
100
  module.exports = {
77
101
  collectFileContent,
78
102
  collectFiles,
79
103
  toUnixPath,
80
104
  normalizePath,
81
- isParent
105
+ isParent,
106
+ grepQR
82
107
  };
@@ -1,4 +1,4 @@
1
- /* eslint-disable no-console */
1
+
2
2
 
3
3
  function getMessage(errorText, { error, command, help = command && getCommandHelp(command) } = {}) {
4
4
  let message = errorText ? errorText.trim() : '';
@@ -16,8 +16,12 @@ function getMessage(errorText, { error, command, help = command && getCommandHel
16
16
  if (includeStack) {
17
17
  message += (oriMsg ? '\n' : '') + error.stack;
18
18
  }
19
- if (debug && error.cause) {
20
- message += '\nCaused by: ' + getMessage('', { error: error.cause }).replace(/^(?= *at)/gm, ' ');
19
+ if (error.cause) {
20
+ if (debug) {
21
+ message += '\nCaused by: ' + getMessage('', { error: error.cause }).replace(/^(?= *at)/gm, ' ');
22
+ } else if (error.cause.status) {
23
+ message += `\nHTTP status: ${error.cause.status}`;
24
+ }
21
25
  }
22
26
  }
23
27
  if (typeof help === 'string') {
@@ -1,5 +1,3 @@
1
- // REVISIT: Consolidate with lib/util/exec.js
2
-
3
1
  const cp = require('child_process');
4
2
  const os = require('os');
5
3
  const cds = require('../cds');
package/lib/util/fs.js CHANGED
@@ -1,6 +1,5 @@
1
- const { yaml: YAML } = require('@sap/cds-foss')
1
+ const { read, write, yaml: YAML } = require('../cds').utils
2
2
  const JSONC = require('./jsonc');
3
- const { read, write } = require('../cds').utils;
4
3
 
5
4
  // REVISIT: Consider merging with template util
6
5
 
@@ -28,7 +27,7 @@ module.exports = new class FsUtil {
28
27
  }
29
28
 
30
29
  async writeYAML(dst, yaml) {
31
- const content = YAML.stringify(yaml, { collectionStyle: 'block', lineWidth: 120 })
30
+ const content = YAML.stringify(yaml, { collectionStyle: 'block', lineWidth: 150 })
32
31
  await write(content).to(dst)
33
32
  }
34
33
 
@@ -0,0 +1,28 @@
1
+ const readline = require('readline')
2
+ async function ask4(...args) {
3
+ const answers = []
4
+ const rl = readline.createInterface({
5
+ input: process.stdin,
6
+ output: process.stdout
7
+ })
8
+
9
+ async function askQuestion(questionParam) {
10
+ return new Promise(function (resolve) {
11
+ rl.question(`${questionParam}`, function (answer) {
12
+ answers.push(answer);
13
+ resolve();
14
+ });
15
+ });
16
+ }
17
+
18
+ for (const idx in args) {
19
+ await askQuestion(args[idx])
20
+ }
21
+
22
+ rl.close()
23
+ return answers
24
+ }
25
+
26
+ module.exports = {
27
+ ask4
28
+ }
package/lib/util/term.js CHANGED
@@ -9,13 +9,15 @@ const t = module.exports.codes = {
9
9
  red: '\x1b[91m', // Bright Foreground Red
10
10
  green: '\x1b[32m', // Foreground Green
11
11
  blue: '\x1b[34m', // Foreground Blue
12
+ yellow:'\x1b[33m', // Foreground Yellow
12
13
  orange: '\x1b[38;2;255;140;0m' // darker orange, works with bright and dark background
13
14
  }
14
15
 
15
- Object.defineProperty(module.exports, 'isTTY', { get: () => process.stdin.isTTY && process.stderr.isTTY })
16
-
16
+ Object.defineProperty(module.exports, 'isTTY', { get: () => process.stdout.isTTY && process.stderr.isTTY })
17
+ const useColor = module.exports.isTTY && process.env.NO_COLOR !== 'true' || (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== 'false')
18
+ module.exports.colors = useColor
17
19
  const as = module.exports.as = (codes, o) => {
18
- return module.exports.isTTY && !process.env.NO_COLOR ? (codes + o + t.reset) : ('' + o)
20
+ return useColor ? (codes + o + t.reset) : ('' + o)
19
21
  }
20
22
 
21
23
  const asErr = module.exports.error = o => DEBUG ? o : as(t.red + t.bold, o)