@sap/cds-dk 7.9.6 → 8.0.3

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 (291) hide show
  1. package/CHANGELOG.md +2136 -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 +31 -14
  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 +13 -14
  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 +4 -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 +69 -56
  52. package/lib/build/provider/hana/template/package.json +4 -3
  53. package/lib/build/provider/internalBuildPlugin.js +32 -97
  54. package/lib/build/provider/internalBuildTaskProvider.js +12 -48
  55. package/lib/build/provider/java/index.js +2 -3
  56. package/lib/build/provider/mtx/index.js +5 -4
  57. package/lib/build/provider/mtx-extension/index.js +5 -2
  58. package/lib/build/provider/mtx-sidecar/index.js +2 -2
  59. package/lib/build/provider/nodejs/index.js +4 -3
  60. package/lib/build/util.js +83 -12
  61. package/lib/compile/to-mermaid.js +108 -0
  62. package/lib/compile/to_asyncapi.js +7 -0
  63. package/lib/compile/to_openapi.js +7 -0
  64. package/lib/deploy/to-hana/hana.js +37 -34
  65. package/lib/deploy/to-hana/hdiDeployUtil.js +8 -6
  66. package/lib/env/schemas/cds-rc.js +1 -1
  67. package/lib/import/common.js +2 -2
  68. package/lib/import/odata/metadataconverter/MetadataConverter.js +4 -4
  69. package/lib/import/odata/v2parser.js +83 -197
  70. package/lib/import/odata/v4parser.js +1 -38
  71. package/lib/import/openapi/importOpenAPI.js +164 -42
  72. package/lib/index.js +10 -12
  73. package/lib/init/bas.js +91 -0
  74. package/lib/init/constants.js +21 -11
  75. package/lib/init/index.js +72 -150
  76. package/lib/init/merge.js +12 -20
  77. package/lib/init/mvn.js +103 -150
  78. package/lib/init/plugin.js +11 -7
  79. package/lib/init/projectReader.js +53 -20
  80. package/lib/init/registries/mta.js +51 -6
  81. package/lib/init/template/application-logging/files/package.json.hbs +7 -0
  82. package/lib/init/template/application-logging/index.js +13 -1
  83. package/lib/init/template/approuter/files/containerize.yaml.hbs +10 -0
  84. package/lib/init/template/approuter/files/mta.yaml.hbs +1 -2
  85. package/lib/init/template/approuter/files/values.yaml.hbs +19 -4
  86. package/lib/init/template/approuter/files/xs-app.json.hbs +12 -3
  87. package/lib/init/template/approuter/index.js +29 -25
  88. package/lib/init/template/attachments/files/mta.yaml.hbs +14 -0
  89. package/lib/init/template/attachments/files/package.json.hbs +5 -0
  90. package/lib/init/template/attachments/files/values.yaml +7 -0
  91. package/lib/init/template/attachments/index.js +53 -0
  92. package/lib/init/template/{redis-messaging → audit-logging}/files/mta.yaml.hbs +4 -4
  93. package/lib/init/template/audit-logging/files/package.json.hbs +7 -0
  94. package/lib/init/template/audit-logging/index.js +66 -0
  95. package/lib/init/template/cf-manifest/index.js +4 -0
  96. package/lib/init/template/completion/completionSetup.js +54 -33
  97. package/lib/init/template/completion/index.js +7 -1
  98. package/lib/init/template/connectivity/index.js +4 -4
  99. package/lib/init/template/containerize/files/containerize.yaml.hbs +26 -0
  100. package/lib/init/template/containerize/index.js +24 -0
  101. package/lib/init/template/data/index.js +4 -0
  102. package/lib/init/template/destination/files/mta.yaml.hbs +31 -1
  103. package/lib/init/template/destination/files/package.sidecar.json +7 -0
  104. package/lib/init/template/destination/index.js +22 -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/values.yaml.hbs +1 -2
  116. package/lib/init/template/hana/index.js +15 -7
  117. package/lib/init/template/helm/files/Chart.yaml.hbs +2 -3
  118. package/lib/init/template/helm/files/values.schema.json +2173 -0
  119. package/lib/init/template/helm/files/values.yaml.hbs +11 -6
  120. package/lib/init/template/helm/index.js +77 -18
  121. package/lib/init/template/helm-unified-runtime/index.js +19 -56
  122. package/lib/init/template/html5-repo/files/app-package.json.hbs +11 -7
  123. package/lib/init/template/html5-repo/files/manifest.json +13 -0
  124. package/lib/init/template/html5-repo/files/mta.yaml.hbs +14 -33
  125. package/lib/init/template/html5-repo/files/package.sidecar.json +7 -0
  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 +6 -7
  130. package/lib/init/template/html5-repo/index.js +41 -35
  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/files/mta.yaml.hbs +25 -0
  170. package/lib/init/template/redis/files/package.json.hbs +5 -0
  171. package/lib/init/template/redis/index.js +44 -0
  172. package/lib/init/template/redis-messaging/files/package.json.hbs +0 -3
  173. package/lib/init/template/redis-messaging/index.js +8 -18
  174. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_de.properties +3 -0
  175. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_en.properties +3 -0
  176. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +13 -1
  177. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n.properties +2 -2
  178. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_de.properties +3 -0
  179. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_en.properties +4 -0
  180. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +3 -3
  181. package/lib/init/template/sample/files/app/index.html +1 -1
  182. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Authors.csv +5 -5
  183. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books.csv +6 -6
  184. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books_texts.csv +5 -5
  185. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Genres.csv +16 -16
  186. package/lib/init/template/sample/files/nodejs/cat-service.js +2 -2
  187. package/lib/init/template/sample/files/package.json.hbs +3 -0
  188. package/lib/init/template/sample/index.js +16 -9
  189. package/lib/init/template/sqlite/index.js +4 -0
  190. package/lib/init/template/tiny-sample/files/nodejs/db/data/my.bookshop-Books.csv +3 -3
  191. package/lib/init/template/tiny-sample/files/nodejs/srv/cat-service.cds.hbs +1 -1
  192. package/lib/init/template/tiny-sample/index.js +4 -0
  193. package/lib/init/template/toggles/files/package.json.hbs +1 -1
  194. package/lib/init/template/toggles/index.js +4 -0
  195. package/lib/init/template/typer/index.js +4 -0
  196. package/lib/init/template/typescript/files/package.json +12 -0
  197. package/lib/init/template/typescript/files/tsconfig.json +13 -0
  198. package/lib/init/template/typescript/index.js +39 -0
  199. package/lib/init/template/workzone/index.js +9 -0
  200. package/lib/init/template/workzone-standard/files/manifest.json.hbs +6 -0
  201. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +59 -0
  202. package/lib/init/template/workzone-standard/index.js +46 -0
  203. package/lib/init/template/xsuaa/files/package.json.hbs +1 -1
  204. package/lib/init/template/xsuaa/files/values.yaml.hbs +3 -2
  205. package/lib/init/template/xsuaa/index.js +11 -7
  206. package/lib/init/validate.js +27 -0
  207. package/lib/lint/generators.js +1 -1
  208. package/lib/lint/io.js +25 -7
  209. package/lib/mtx/auth_manager.js +64 -30
  210. package/lib/mtx/download_legacy.js +2 -2
  211. package/lib/mtx/index.js +0 -4
  212. package/lib/mtx/params.js +32 -38
  213. package/lib/mtx/pull.js +151 -43
  214. package/lib/mtx/push.js +3 -3
  215. package/lib/mtx/settings_manager.js +75 -35
  216. package/lib/mtx/tenant_action.js +1 -1
  217. package/lib/mtx/util/cf.js +1 -1
  218. package/lib/mtx/util/fs.js +26 -1
  219. package/lib/mtx/util/logging.js +7 -3
  220. package/lib/util/command.js +0 -2
  221. package/lib/util/fs.js +2 -3
  222. package/lib/util/question.js +28 -0
  223. package/lib/util/term.js +5 -3
  224. package/npm-shrinkwrap.json +225 -1083
  225. package/package.json +13 -8
  226. package/bin/activate.js +0 -77
  227. package/lib/compile/asyncapi/channels.js +0 -22
  228. package/lib/compile/asyncapi/components/messageTraits/index.js +0 -89
  229. package/lib/compile/asyncapi/components/messages/index.js +0 -173
  230. package/lib/compile/asyncapi/components/schemas/annotations.js +0 -33
  231. package/lib/compile/asyncapi/components/schemas/array.js +0 -8
  232. package/lib/compile/asyncapi/components/schemas/cache.js +0 -14
  233. package/lib/compile/asyncapi/components/schemas/definition.js +0 -187
  234. package/lib/compile/asyncapi/components/schemas/enum.js +0 -16
  235. package/lib/compile/asyncapi/components/schemas/examples.js +0 -10
  236. package/lib/compile/asyncapi/components/schemas/index.js +0 -14
  237. package/lib/compile/asyncapi/components/schemas/localized.js +0 -29
  238. package/lib/compile/asyncapi/components/schemas/primitiveElement.js +0 -41
  239. package/lib/compile/asyncapi/components/schemas/ref.js +0 -45
  240. package/lib/compile/asyncapi/components/schemas/utils.js +0 -108
  241. package/lib/compile/asyncapi/index.js +0 -247
  242. package/lib/compile/asyncapi/message.js +0 -13
  243. package/lib/compile/asyncapi/utils.js +0 -27
  244. package/lib/compile/openapi/csdl2openapi.js +0 -2448
  245. package/lib/compile/openapi/index.js +0 -68
  246. package/lib/init/template/helm/files/chart/templates/NOTES.txt +0 -14
  247. package/lib/init/template/helm/files/chart/templates/_helpers.tpl +0 -28
  248. package/lib/init/template/helm/files/chart/values.root.schema.json +0 -61
  249. package/lib/init/template/helm/files/chart/values.schema.json +0 -366
  250. package/lib/init/template/helm-unified-runtime/files/Chart.yaml.hbs +0 -10
  251. package/lib/init/template/helm-unified-runtime/files/templates/_deployment_helpers.tpl +0 -90
  252. package/lib/init/template/helm-unified-runtime/files/templates/_helpers.tpl +0 -28
  253. package/lib/init/template/helm-unified-runtime/files/values.root.schema.json +0 -61
  254. package/lib/init/template/helm-unified-runtime/files/values.schema.json +0 -366
  255. package/lib/init/template/helm-unified-runtime/files/values.yaml.hbs +0 -26
  256. package/lib/init/template/helm-unified-runtime/package.json +0 -4
  257. package/lib/init/template/multitenancy/files/mtxs-configmap-java-approuter.yaml +0 -20
  258. package/lib/init/template/multitenancy/files/mtxs-configmap-java.yaml +0 -18
  259. package/lib/init/template/multitenancy/files/mtxs-configmap-nodejs-approuter.yaml +0 -12
  260. package/lib/init/template/multitenancy/files/mtxs-configmap-nodejs.yaml +0 -12
  261. package/lib/init/template/multitenancy/files/saas-registry-secret.yaml +0 -9
  262. package/lib/init/template/nodejs/files/eslint.config.js +0 -13
  263. package/lib/init/template/notebook/index.js +0 -11
  264. package/lib/init/template/samples/index.js +0 -9
  265. package/lib/mtx/activate.js +0 -238
  266. package/lib/mtx/extend.js +0 -173
  267. package/lib/util/exec.d.ts +0 -54
  268. package/lib/util/exec.js +0 -211
  269. /package/lib/{init/template/approuter/files → build/plugins/helm/files/plugins/approuter}/approuter-configmap.yaml +0 -0
  270. /package/lib/{init/template/connectivity/files → build/plugins/helm/files/plugins/connectivity}/connectivity-proxy-info.yaml +0 -0
  271. /package/lib/{init/template/html5-repo/files → build/plugins/helm/files/plugins/html5-repo}/html5-apps-deployer-configmap.yaml +0 -0
  272. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/templates/_helpers.tpl +0 -0
  273. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/templates/job.yaml +0 -0
  274. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/templates/secret.yaml +0 -0
  275. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/templates/service-account.yaml +0 -0
  276. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/templates/service-binding.yaml +0 -0
  277. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/content-deployment/values.schema.json +0 -0
  278. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/NOTES.txt +0 -0
  279. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/_api.tpl +0 -0
  280. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/_helpers.tpl +0 -0
  281. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/api-rule.yaml +0 -0
  282. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/network-policy.yaml +0 -0
  283. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/rbac.yaml +0 -0
  284. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/secret.yaml +0 -0
  285. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/service-account.yaml +0 -0
  286. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/service-binding.yaml +0 -0
  287. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/service.yaml +0 -0
  288. /package/lib/{init/template/helm/files → build/plugins/helm/files/subcharts}/web-application/templates/virtual-service.yaml +0 -0
  289. /package/lib/{init/template/helm-unified-runtime → build/plugins/helm}/files/templates/NOTES.txt +0 -0
  290. /package/lib/init/template/{nodejs/files → java}/.cdsrc.json +0 -0
  291. /package/lib/init/template/tiny-sample/files/nodejs/db/{data-model.cds → schema.cds} +0 -0
@@ -23,13 +23,13 @@ module.exports = class Download {
23
23
  }
24
24
 
25
25
  console.log(`Downloading migrated @sap/mtx extension project(s)`);
26
- const downloadedTgz = await this.getTgz(params);
26
+ const downloadedTgz = await this.pullTgz(params);
27
27
  await fs.promises.writeFile(downloadPath, downloadedTgz);
28
28
 
29
29
  console.log(`Finished.`);
30
30
  }
31
31
 
32
- static async getTgz(params) {
32
+ static async pullTgz(params) {
33
33
  const tagRule = params.get('tagRule');
34
34
  const defaultTag = params.get('defaultTag');
35
35
  const downloadUrl = `${(params.get('appUrl'))}/-/cds/extensibility/getMigratedProjects`;
package/lib/mtx/index.js CHANGED
@@ -1,12 +1,8 @@
1
- const Activate = require('./activate');
2
1
  const AuthManager = require('./auth_manager');
3
- const Extend = require('./extend');
4
2
  const { SettingsManager } = require('./settings_manager');
5
3
  const Question = require('./util/question');
6
4
 
7
5
  module.exports = {
8
- extend: Extend.run,
9
- activate: Activate.run,
10
6
  login: AuthManager.login,
11
7
  logout: AuthManager.logout,
12
8
  question: Question,
package/lib/mtx/params.js CHANGED
@@ -36,7 +36,7 @@ class Param {
36
36
  * @param options constructor options
37
37
  */
38
38
  constructor(name,
39
- val,
39
+ val = undefined,
40
40
  options = {}) {
41
41
 
42
42
  this.persist = Persistence.none;
@@ -44,6 +44,11 @@ class Param {
44
44
  this.abbreviate = false;
45
45
  this.internal = false;
46
46
  this.allowedValues = null;
47
+ this.type = val === undefined
48
+ ? this.allowedValues
49
+ ? typeof this.allowedValues[0]
50
+ : 'string' // default
51
+ : typeof val;
47
52
 
48
53
  this.name = name;
49
54
  this._val = val;
@@ -102,12 +107,8 @@ class Param {
102
107
  if (val === undefined) {
103
108
  return; // Allow `undefined` always.
104
109
  }
105
- let expectedType = typeof this.val;
106
- if (expectedType === 'undefined' && this.allowedValues) {
107
- expectedType = typeof this.allowedValues[0];
108
- }
109
- if (typeof val !== expectedType) {
110
- throw `Failed to set '${(this.name)}' = ${JSON.stringify(val)}: expected value of type ${expectedType}`;
110
+ if (typeof val !== this.type) {
111
+ throw `Failed to set '${(this.name)}' = ${JSON.stringify(val)}: expected value of type ${this.type}`;
111
112
  }
112
113
  if (this.allowedValues && !(this.allowedValues).includes(val)) {
113
114
  throw `Failed to set '${(this.name)}' = ${JSON.stringify(val)}: expected one of ${JSON.stringify(this.allowedValues)}`;
@@ -125,7 +126,7 @@ class Param {
125
126
  ? undefined
126
127
  : this.obfuscate === ObfuscationLevel.none && (!this.abbreviate || process.env.DEBUG)
127
128
  ? this.val
128
- : this.obfuscate === ObfuscationLevel.partial && process.env.DEBUG && typeof this.val === 'string'
129
+ : this.obfuscate === ObfuscationLevel.partial && process.env.DEBUG && this.type === 'string'
129
130
  ? this.val.slice(0, DISCLOSURE_LENGTH) + '...' + this.val.slice(-DISCLOSURE_LENGTH)
130
131
  : '...';
131
132
  }
@@ -161,48 +162,44 @@ class BasicParams {
161
162
  constructor() {
162
163
  // GENERAL PARAMS
163
164
  // - non-persisted
164
- this.projectFolder = new Param('projectFolder', '');
165
+ this.projectFolder = new Param('projectFolder');
165
166
  this.directory = this.projectFolder.newAlias('directory');
166
167
 
167
- this.tagRule = new Param('tagRule', '');
168
- this.defaultTag = new Param('defaultTag', '');
169
-
170
- // - non-persisted, internal
171
- this.reqAuth = new Param('reqAuth', null, { obfuscate: ObfuscationLevel.full, internal: true });
172
- this.clearOtherTokenStorage = new Param('clearOtherTokenStorage', false, { internal: true });
173
-
174
168
  // - persisted
175
- this.appUrl = new Param('appUrl', '', { persist: Persistence.setting }); // any trailing slash (/) is removed
169
+ this.appUrl = new Param('appUrl', undefined, { persist: Persistence.setting }); // any trailing slash (/) is removed
176
170
  this.url = this.appUrl.newAlias('url');
177
171
  this.from = this.appUrl.newAlias('from');
178
172
  this.to = this.appUrl.newAlias('to');
179
173
  this.at = this.appUrl.newAlias('at');
180
- this.subdomain = new Param('subdomain', '', { persist: Persistence.setting }); // only needed to fetch token
181
- this.passcodeUrl = new Param('passcodeUrl', '', { persist: Persistence.setting, abbreviate: true });
174
+ this.subdomain = new Param('subdomain', undefined, { persist: Persistence.setting }); // only needed to fetch token
175
+ this.passcodeUrl = new Param('passcodeUrl', undefined, { persist: Persistence.setting, abbreviate: true });
182
176
  this.passcode_url = this.passcodeUrl.newAlias('passcode_url');
183
- this.tokenUrl = new Param('tokenUrl', '', { persist: Persistence.setting, abbreviate: true })
177
+ this.tokenUrl = new Param('tokenUrl', undefined, { persist: Persistence.setting, abbreviate: true })
184
178
 
185
179
  // AUTH PARAMS
186
180
  // - non-persisted
187
- this.passcode = new Param('passcode', '', { obfuscate: ObfuscationLevel.partial });
181
+ this.passcode = new Param('passcode', undefined, { obfuscate: ObfuscationLevel.partial });
182
+ this.password = new Param('password', undefined, { obfuscate: ObfuscationLevel.full });
183
+ this.clientid = new Param('clientid', undefined);
184
+ this.clientsecret = new Param('clientsecret', undefined, { obfuscate: ObfuscationLevel.partial });
185
+ this.key = new Param('key', undefined, { obfuscate: ObfuscationLevel.partial }); // private key of client certificate
188
186
  this.tokenStorage = new Param('tokenStorage', undefined, { allowedValues: ['plain', 'keyring'] });
187
+ this.clearOtherTokenStorage = new Param('clearOtherTokenStorage', false, { internal: true });
189
188
  this.plain = new Param('plain', false, { internal: true }); // for backward compatibility - superseded by tokenStorage
190
189
  this.saveData = new Param('saveData', false, { internal: true });
191
- this.renewLogin = new Param('renewLogin', false);
192
- this.password = new Param('password', '', { obfuscate: ObfuscationLevel.full });
193
- this.clientid = new Param('clientid', '');
194
- this.clientsecret = new Param('clientsecret', '', { obfuscate: ObfuscationLevel.partial });
195
- this.key = new Param('key', '', { obfuscate: ObfuscationLevel.partial }); // private key of client certificate
190
+ this.renewLogin = new Param('renewLogin', false, { internal: true });
191
+ this.reqAuth = new Param('reqAuth', undefined, { internal: true, type: 'object', obfuscate: ObfuscationLevel.full });
196
192
 
197
193
  // - persisted
198
- this.username = new Param('username', '', { persist: Persistence.setting }); // only saved against localhost (non-productive); as setting due to ambiguous app URL
194
+ this.username = new Param('username', undefined, { persist: Persistence.setting }); // only saved against localhost (non-productive); as setting due to ambiguous app URL
199
195
  this.isEmptyPassword = new Param('isEmptyPassword', false, { internal: true, persist: Persistence.setting }); // extra param so we can ensure we never save a password
200
- this.token = new Param('token', '', { persist: Persistence.auth, obfuscate: ObfuscationLevel.partial });
196
+ this.token = new Param('token', undefined, { persist: Persistence.auth, obfuscate: ObfuscationLevel.partial });
201
197
  this.tokenExpirationDate = new Param('tokenExpirationDate', Number.MAX_SAFE_INTEGER, { persist: Persistence.auth });
202
- this.refreshToken = new Param('refreshToken', '', { persist: Persistence.auth, obfuscate: ObfuscationLevel.partial });
198
+ this.refreshToken = new Param('refreshToken', undefined, { persist: Persistence.auth, obfuscate: ObfuscationLevel.partial });
203
199
  }
204
200
  }
205
201
 
202
+ // TODO pass these as extra arguments to lib functions
206
203
  /**
207
204
  * All known params including those needed for specific commands.
208
205
  */
@@ -211,28 +208,25 @@ class AllParams extends BasicParams {
211
208
  super();
212
209
 
213
210
  // `extend` PARAMS
214
- this.force = new Param('force', false);
215
- this.templates = new Param('templates', false);
216
- this["download-migrated-projects"] = new Param('download-migrated-projects', false)
211
+ this.defaultTag = new Param('defaultTag', undefined);
212
+ this.tagRule = new Param('tagRule', undefined);
217
213
 
218
- // `activate` PARAMS
219
- this.undeploy = new Param('undeploy', false);
220
- this.wsdl = new Param('wsdl', false);
214
+ // `push` PARAMS
221
215
  this.sync = new Param('sync', false);
222
216
  this.async = new Param('async', false);
223
217
 
224
218
  // `subscribe` PARAMS
225
- this.body = new Param('body', '', { persist: Persistence.none });
219
+ this.body = new Param('body', undefined, { internal: true, persist: Persistence.none });
226
220
 
227
221
  // `login` PARAMS
228
222
  this.skipToken = new Param('skipToken', false, { internal: true });
223
+ this.saveRefreshToken = new Param('saveRefreshToken', false, { internal: true });
229
224
 
230
225
  // `logout` PARAMS
231
226
  this.deleteSettings = new Param('deleteSettings', false);
232
227
  this["delete-settings"] = this.deleteSettings.newAlias('delete-settings');
233
228
  this.clearInvalid = new Param('clearInvalid', false);
234
229
  this["clear-invalid"] = this.clearInvalid.newAlias('clear-invalid');
235
-
236
230
  }
237
231
  }
238
232
 
@@ -411,7 +405,7 @@ class ParamCollection {
411
405
  }
412
406
 
413
407
  /**
414
- * Return an object mapping each of the contained params' names to its corresponding value.
408
+ * Return an object mapping the names of each of the contained params to their corresponding values.
415
409
  * @param persistence the type of persistence to filter for (omit to disable filtering)
416
410
  */
417
411
  toEntries(persistence) {
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
  };