@sap/cds-dk 10.0.6 → 10.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/bin/add.js +7 -23
  3. package/bin/bind.js +10 -0
  4. package/bin/build.js +20 -34
  5. package/bin/cds.js +1 -1
  6. package/bin/compile/index.js +15 -41
  7. package/bin/completion/completionFs.js +11 -8
  8. package/bin/completion/index.js +592 -80
  9. package/bin/completion/scripts/cds.fish +568 -0
  10. package/bin/completion/scripts/cds.ps1 +329 -0
  11. package/bin/completion/scripts/cds.sh +261 -0
  12. package/bin/completion/scripts/cds.zsh +230 -0
  13. package/bin/completion/templates/cds-tmpl.fish +65 -0
  14. package/bin/completion/templates/cds-tmpl.ps1 +208 -0
  15. package/bin/completion/templates/cds-tmpl.sh +93 -0
  16. package/bin/completion/templates/cds-tmpl.zsh +74 -0
  17. package/bin/deploy/index.js +7 -1
  18. package/bin/deploy/options.js +25 -0
  19. package/{lib → bin}/deploy/to-hana/hana.js +8 -8
  20. package/{lib → bin}/deploy/to-hana/hdiDeployUtil.js +1 -1
  21. package/bin/deploy/{to-hana.js → to-hana/index.js} +1 -1
  22. package/{lib → bin}/deploy/to-hana/mtaUtil.js +1 -1
  23. package/bin/deploy/to-sql.js +0 -2
  24. package/bin/export.js +35 -5
  25. package/bin/help/export.md +3 -0
  26. package/bin/help.js +1 -5
  27. package/bin/import.js +6 -0
  28. package/bin/init.js +5 -24
  29. package/bin/inspect.js +6 -0
  30. package/bin/login.js +26 -20
  31. package/bin/pull.js +19 -6
  32. package/bin/push.js +19 -6
  33. package/bin/up.js +14 -11
  34. package/{lib → bin}/upgrade/constants.js +1 -1
  35. package/bin/{upgrade.js → upgrade/index.js} +2 -2
  36. package/{lib → bin}/upgrade/preflight/consistency.js +1 -1
  37. package/{lib → bin}/upgrade/preflight/index.js +1 -1
  38. package/{lib → bin}/upgrade/steps/analyze/index.js +1 -1
  39. package/{lib → bin}/upgrade/steps/install.js +1 -1
  40. package/{lib → bin}/upgrade/steps/openrewrite.js +1 -1
  41. package/{lib → bin}/upgrade/steps/removals.js +1 -1
  42. package/{lib → bin}/upgrade/steps/rewrite.js +1 -1
  43. package/{lib → bin}/upgrade/steps/verify.js +1 -1
  44. package/{lib/upgrade/index.js → bin/upgrade/upgrade.js} +2 -2
  45. package/{lib → bin}/upgrade/util/pom.js +2 -2
  46. package/{lib → bin}/upgrade/util/prompt.js +1 -1
  47. package/bin/util/containerize.js +8 -4
  48. package/bin/version.js +10 -1
  49. package/{lib → bin}/watch/hot-reload.js +1 -1
  50. package/bin/{watch.js → watch/index.js} +17 -9
  51. package/{lib → bin}/watch/livereload-connect.js +1 -1
  52. package/{lib → bin}/watch/vite-watch.js +1 -1
  53. package/{lib → bin}/watch/watched.js +2 -2
  54. package/lib/build/constants.js +1 -1
  55. package/lib/build/plugins/fiori/index.js +31 -2
  56. package/lib/build/plugins/internal.js +4 -2
  57. package/lib/build/plugins/mtx-sidecar/index.js +7 -8
  58. package/lib/build/plugins/nodejs/index.js +64 -7
  59. package/lib/compile/to_asyncapi.js +1 -1
  60. package/lib/env/schema.js +16 -0
  61. package/lib/env/schemas/cds-rc.js +1 -76
  62. package/lib/import/importapi.js +2 -3
  63. package/lib/import/message.js +0 -2
  64. package/lib/init/constants.js +2 -1
  65. package/lib/init/index.js +2 -2
  66. package/lib/init/merge.js +123 -280
  67. package/lib/init/plugin.js +3 -1
  68. package/lib/init/projectReader.js +22 -6
  69. package/lib/init/registries/xs-app.js +3 -5
  70. package/lib/init/template/ai/files/mta.yaml.hbs +1 -1
  71. package/lib/init/template/ai/index.js +14 -2
  72. package/lib/init/template/ams/index.js +2 -2
  73. package/lib/init/template/app-frontend/files/containerize.yaml.hbs +2 -2
  74. package/lib/init/template/app-frontend/index.js +4 -4
  75. package/lib/init/template/application-logging/index.js +2 -2
  76. package/lib/init/template/approuter/files/package.json +1 -1
  77. package/lib/init/template/approuter/index.js +8 -3
  78. package/lib/init/template/attachments/files/package.json.hbs +1 -1
  79. package/lib/init/template/attachments/index.js +2 -2
  80. package/lib/init/template/audit-logging/index.js +2 -2
  81. package/lib/init/template/cloud-logging/files/package.json.hbs +4 -4
  82. package/lib/init/template/cloud-logging/index.js +2 -2
  83. package/lib/init/template/completion/completionSetup.js +36 -39
  84. package/lib/init/template/connectivity/index.js +2 -2
  85. package/lib/init/template/data/as-csv.js +15 -5
  86. package/lib/init/template/data/as-json.js +114 -42
  87. package/lib/init/template/data/index.js +16 -2
  88. package/lib/init/template/destination/index.js +2 -2
  89. package/lib/init/template/dynatrace/files/package.json.hbs +3 -3
  90. package/lib/init/template/dynatrace/index.js +2 -2
  91. package/lib/init/template/enterprise-messaging/index.js +2 -2
  92. package/lib/init/template/enterprise-messaging-shared/index.js +2 -2
  93. package/lib/init/template/extensibility/files/mta.yaml.hbs +11 -0
  94. package/lib/init/template/extensibility/files/xs-security.json.hbs +1 -1
  95. package/lib/init/template/extensibility/index.js +3 -3
  96. package/lib/init/template/github-actions/index.js +2 -2
  97. package/lib/init/template/hana/index.js +3 -2
  98. package/lib/init/template/hana-serverless/files/db/package.json +14 -0
  99. package/lib/init/template/hana-serverless/files/identity.values.yaml.hbs +10 -0
  100. package/lib/init/template/hana-serverless/files/mta.yaml.hbs +64 -0
  101. package/lib/init/template/hana-serverless/files/package.json.hbs +35 -0
  102. package/lib/init/template/hana-serverless/files/src/.hdiconfig +139 -0
  103. package/lib/init/template/hana-serverless/files/undeploy.json +7 -0
  104. package/lib/init/template/hana-serverless/files/values.yaml.hbs +18 -0
  105. package/lib/init/template/hana-serverless/index.js +184 -0
  106. package/lib/init/template/html5-repo/files/containerize.yaml.hbs +2 -2
  107. package/lib/init/template/html5-repo/files/mta.yaml.hbs +5 -0
  108. package/lib/init/template/html5-repo/index.js +18 -14
  109. package/lib/init/template/http/index.js +123 -26
  110. package/lib/init/template/ias/index.js +2 -2
  111. package/lib/init/template/kafka/index.js +2 -2
  112. package/lib/init/template/kyma/index.js +7 -5
  113. package/lib/init/template/malware-scanner/index.js +2 -2
  114. package/lib/init/template/mta/files/mta.yaml.hbs +1 -1
  115. package/lib/init/template/multitenancy/index.js +18 -3
  116. package/lib/init/template/nodejs/index.js +4 -2
  117. package/lib/init/template/notifications/index.js +2 -2
  118. package/lib/init/template/portal/files/mta.yaml.hbs +4 -0
  119. package/lib/init/template/portal/index.js +13 -8
  120. package/lib/init/template/postgres/index.js +2 -2
  121. package/lib/init/template/redis/index.js +2 -2
  122. package/lib/init/template/sample/files/db/currencies.cds +2 -0
  123. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Authors.csv +1 -1
  124. package/lib/init/template/tiny-sample/files/nodejs/db/data/CatalogService.Books.csv +2 -2
  125. package/lib/init/template/workzone-standard/index.js +5 -5
  126. package/lib/init/template/xsuaa/index.js +20 -2
  127. package/lib/lint/eslintHelp.json +1 -1
  128. package/lib/mtx/auth_manager.js +103 -37
  129. package/lib/mtx/params.js +3 -2
  130. package/lib/mtx/settings_manager.js +45 -28
  131. package/lib/mtx/tenant_action.js +0 -1
  132. package/lib/mtx/util/credentials.js +62 -0
  133. package/node_modules/@cap-js/openapi/lib/compile/csdl.js +3 -3
  134. package/node_modules/@cap-js/openapi/lib/compile/csdl2openapi.js +111 -103
  135. package/node_modules/@cap-js/openapi/lib/compile/diagram.js +4 -4
  136. package/node_modules/@cap-js/openapi/lib/compile/types.d.ts +3 -1
  137. package/node_modules/@cap-js/openapi/package.json +3 -3
  138. package/node_modules/@sap/cds/CHANGELOG.md +64 -0
  139. package/node_modules/@sap/cds/app/index.js +5 -4
  140. package/node_modules/@sap/cds/eslint.config.mjs +1 -0
  141. package/node_modules/@sap/cds/lib/compile/for/nodejs.js +1 -0
  142. package/node_modules/@sap/cds/lib/compile/minify.js +14 -10
  143. package/node_modules/@sap/cds/lib/env/cds-env.js +1 -1
  144. package/node_modules/@sap/cds/lib/env/cds-requires.js +22 -11
  145. package/node_modules/@sap/cds/lib/env/defaults.js +4 -1
  146. package/node_modules/@sap/cds/lib/env/schemas/cds-rc.js +5 -2
  147. package/node_modules/@sap/cds/lib/plugins.js +9 -5
  148. package/node_modules/@sap/cds/lib/req/spawn.js +1 -1
  149. package/node_modules/@sap/cds/lib/srv/srv-dispatch.js +1 -1
  150. package/node_modules/@sap/cds/lib/srv/srv-models.js +1 -1
  151. package/node_modules/@sap/cds/lib/utils/cds-utils.js +2 -1
  152. package/node_modules/@sap/cds/libx/_runtime/common/generic/assert.js +2 -3
  153. package/node_modules/@sap/cds/libx/_runtime/common/generic/auth/restrict.js +8 -1
  154. package/node_modules/@sap/cds/libx/_runtime/common/generic/crud.js +18 -8
  155. package/node_modules/@sap/cds/libx/_runtime/fiori/lean-draft.js +25 -17
  156. package/node_modules/@sap/cds/libx/_runtime/messaging/common-utils/authorizedRequest.js +3 -2
  157. package/node_modules/@sap/cds/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +9 -3
  158. package/node_modules/@sap/cds/libx/_runtime/remote/Service.js +4 -19
  159. package/node_modules/@sap/cds/libx/_runtime/remote/utils/LruCache.js +42 -0
  160. package/node_modules/@sap/cds/libx/_runtime/remote/utils/client.js +14 -4
  161. package/node_modules/@sap/cds/libx/_runtime/remote/utils/cloudSdkProvider.js +2 -9
  162. package/node_modules/@sap/cds/libx/_runtime/remote/utils/destinationClient.js +150 -0
  163. package/node_modules/@sap/cds/libx/_runtime/remote/utils/error.js +13 -0
  164. package/node_modules/@sap/cds/libx/_runtime/remote/utils/fetchClient.js +26 -18
  165. package/node_modules/@sap/cds/libx/_runtime/remote/utils/query.js +34 -23
  166. package/node_modules/@sap/cds/libx/_runtime/remote/utils/token.js +82 -0
  167. package/node_modules/@sap/cds/libx/_runtime/ucl/Service.js +36 -189
  168. package/node_modules/@sap/cds/libx/common/utils/streaming.js +16 -16
  169. package/node_modules/@sap/cds/libx/http/location.js +3 -3
  170. package/node_modules/@sap/cds/libx/odata/middleware/batch.js +16 -10
  171. package/node_modules/@sap/cds/libx/odata/middleware/metadata.js +9 -6
  172. package/node_modules/@sap/cds/libx/odata/middleware/service-document.js +1 -1
  173. package/node_modules/@sap/cds/libx/odata/parse/afterburner.js +19 -3
  174. package/node_modules/@sap/cds/libx/odata/parse/cqn2odata.js +83 -86
  175. package/node_modules/@sap/cds/libx/queue/SchedulingService.js +17 -2
  176. package/node_modules/@sap/cds/libx/queue/processing.js +1 -3
  177. package/node_modules/@sap/cds/libx/rest/RestAdapter.js +3 -7
  178. package/node_modules/@sap/cds/libx/rest/middleware/delete.js +3 -3
  179. package/node_modules/@sap/cds/libx/rest/middleware/parse.js +13 -8
  180. package/node_modules/@sap/cds/libx/rest/middleware/read.js +3 -3
  181. package/node_modules/@sap/cds/libx/rest/middleware/upsert.js +2 -2
  182. package/node_modules/@sap/cds/package.json +1 -1
  183. package/node_modules/@sap/cds-compiler/CHANGELOG.md +60 -8
  184. package/node_modules/@sap/cds-compiler/bin/cdsc.js +2 -2
  185. package/node_modules/@sap/cds-compiler/doc/IncompatibleChanges_v7.md +46 -19
  186. package/node_modules/@sap/cds-compiler/lib/api/main.js +10 -7
  187. package/node_modules/@sap/cds-compiler/lib/api/options.js +8 -2
  188. package/node_modules/@sap/cds-compiler/lib/base/csnRefs.js +5 -2
  189. package/node_modules/@sap/cds-compiler/lib/base/message-registry.js +4 -0
  190. package/node_modules/@sap/cds-compiler/lib/base/optionProcessor.js +12 -0
  191. package/node_modules/@sap/cds-compiler/lib/checks/enricher.js +1 -0
  192. package/node_modules/@sap/cds-compiler/lib/compiler/assert-consistency.js +8 -7
  193. package/node_modules/@sap/cds-compiler/lib/compiler/checks.js +3 -3
  194. package/node_modules/@sap/cds-compiler/lib/compiler/define.js +301 -75
  195. package/node_modules/@sap/cds-compiler/lib/compiler/dictionaries.js +2 -0
  196. package/node_modules/@sap/cds-compiler/lib/compiler/extend.js +40 -26
  197. package/node_modules/@sap/cds-compiler/lib/compiler/populate.js +45 -60
  198. package/node_modules/@sap/cds-compiler/lib/compiler/propagator.js +16 -7
  199. package/node_modules/@sap/cds-compiler/lib/compiler/resolve.js +41 -15
  200. package/node_modules/@sap/cds-compiler/lib/compiler/shared.js +7 -6
  201. package/node_modules/@sap/cds-compiler/lib/compiler/tweak-assocs.js +62 -50
  202. package/node_modules/@sap/cds-compiler/lib/compiler/utils.js +29 -14
  203. package/node_modules/@sap/cds-compiler/lib/edm/edmAnnoPreprocessor.js +6 -5
  204. package/node_modules/@sap/cds-compiler/lib/gen/CdlGrammar.checksum +1 -1
  205. package/node_modules/@sap/cds-compiler/lib/gen/CdlParser.js +1119 -1109
  206. package/node_modules/@sap/cds-compiler/lib/gen/Dictionary.json +19 -3
  207. package/node_modules/@sap/cds-compiler/lib/json/from-csn.js +10 -16
  208. package/node_modules/@sap/cds-compiler/lib/json/to-csn.js +58 -39
  209. package/node_modules/@sap/cds-compiler/lib/main.d.ts +8 -0
  210. package/node_modules/@sap/cds-compiler/lib/main.js +1 -0
  211. package/node_modules/@sap/cds-compiler/lib/model/csnUtils.js +2 -0
  212. package/node_modules/@sap/cds-compiler/lib/parsers/AstBuildingParser.js +2 -1
  213. package/node_modules/@sap/cds-compiler/lib/parsers/XprTree.js +8 -0
  214. package/node_modules/@sap/cds-compiler/lib/render/toCdl.js +22 -1
  215. package/node_modules/@sap/cds-compiler/lib/render/toSql.js +17 -2
  216. package/node_modules/@sap/cds-compiler/lib/render/utils/common.js +6 -1
  217. package/node_modules/@sap/cds-compiler/lib/render/utils/operators.js +27 -0
  218. package/node_modules/@sap/cds-compiler/lib/render/utils/standardDatabaseFunctions.js +125 -3
  219. package/node_modules/@sap/cds-compiler/lib/tool-lib/revealInternalProperties.js +2 -1
  220. package/node_modules/@sap/cds-compiler/lib/transform/db/applyTransformations.js +3 -0
  221. package/node_modules/@sap/cds-compiler/lib/transform/db/associations.js +8 -2
  222. package/node_modules/@sap/cds-compiler/lib/transform/db/backlinks.js +22 -6
  223. package/node_modules/@sap/cds-compiler/lib/transform/db/constraints.js +62 -1
  224. package/node_modules/@sap/cds-compiler/lib/transform/effective/annotations.js +40 -0
  225. package/node_modules/@sap/cds-compiler/lib/transform/effective/main.js +1 -0
  226. package/node_modules/@sap/cds-compiler/lib/transform/forRelationalDB.js +6 -1
  227. package/node_modules/@sap/cds-compiler/lib/transform/translateAssocsToJoins.js +19 -8
  228. package/node_modules/@sap/cds-compiler/package.json +1 -1
  229. package/node_modules/@sap/cds-mtxs/CHANGELOG.md +51 -0
  230. package/node_modules/@sap/cds-mtxs/bin/cds-mtx-migrate.js +32 -48
  231. package/node_modules/@sap/cds-mtxs/bin/cds-mtx.js +35 -16
  232. package/node_modules/@sap/cds-mtxs/cds-plugin.js +18 -0
  233. package/node_modules/@sap/cds-mtxs/lib/migration/migration.js +3 -408
  234. package/node_modules/@sap/cds-mtxs/lib/utils.js +19 -4
  235. package/node_modules/@sap/cds-mtxs/package.json +1 -1
  236. package/node_modules/@sap/cds-mtxs/srv/cf/abstract-provisioning-service.js +6 -7
  237. package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/annotations.js +5 -2
  238. package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/index.js +6 -5
  239. package/node_modules/@sap/cds-mtxs/srv/extensibility-service.cds +0 -11
  240. package/node_modules/@sap/cds-mtxs/srv/extensibility-service.js +34 -35
  241. package/node_modules/@sap/cds-mtxs/srv/jobs-service.js +30 -24
  242. package/node_modules/@sap/cds-mtxs/srv/model-provider.js +19 -4
  243. package/node_modules/@sap/cds-mtxs/srv/plugins/hana/ctnr-mgr-base.js +6 -3
  244. package/node_modules/@sap/cds-mtxs/srv/plugins/hana/hdi.js +5 -3
  245. package/node_modules/@sap/cds-mtxs/srv/plugins/hana/srv-mgr.js +3 -2
  246. package/node_modules/@sap/cds-mtxs/srv/plugins/hana/tms.js +17 -0
  247. package/node_modules/@sap/cds-mtxs/srv/plugins/hana.js +18 -21
  248. package/node_modules/@sap/xsenv/CHANGELOG.md +7 -0
  249. package/node_modules/@sap/xsenv/package.json +10 -7
  250. package/node_modules/body-parser/node_modules/content-type/README.md +2 -0
  251. package/node_modules/body-parser/node_modules/content-type/dist/index.d.ts +20 -0
  252. package/node_modules/body-parser/node_modules/content-type/dist/index.js +20 -14
  253. package/node_modules/body-parser/node_modules/content-type/dist/index.js.map +1 -1
  254. package/node_modules/body-parser/node_modules/content-type/package.json +1 -1
  255. package/node_modules/hdb/cfg/VERSION +1 -1
  256. package/node_modules/hdb/lib/protocol/ClientInfo.js +16 -3
  257. package/node_modules/hdb/lib/protocol/Connection.js +1 -0
  258. package/node_modules/hdb/package.json +3 -2
  259. package/node_modules/negotiator/lib/accept.js +54 -0
  260. package/node_modules/negotiator/lib/charset.js +10 -34
  261. package/node_modules/negotiator/lib/encoding.js +9 -30
  262. package/node_modules/negotiator/lib/language.js +15 -36
  263. package/node_modules/negotiator/lib/mediaType.js +16 -134
  264. package/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
  265. package/node_modules/negotiator/node_modules/content-type/README.md +71 -0
  266. package/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
  267. package/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
  268. package/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
  269. package/node_modules/negotiator/node_modules/content-type/package.json +52 -0
  270. package/node_modules/negotiator/package.json +16 -10
  271. package/node_modules/qs/.github/THREAT_MODEL.md +3 -3
  272. package/node_modules/qs/CHANGELOG.md +15 -0
  273. package/node_modules/qs/README.md +19 -1
  274. package/node_modules/qs/dist/qs.js +21 -21
  275. package/node_modules/qs/eslint.config.mjs +1 -1
  276. package/node_modules/qs/lib/parse.js +3 -4
  277. package/node_modules/qs/lib/stringify.js +23 -8
  278. package/node_modules/qs/lib/utils.js +10 -3
  279. package/node_modules/qs/package.json +4 -4
  280. package/node_modules/qs/test/parse.js +192 -3
  281. package/node_modules/qs/test/stringify.js +317 -0
  282. package/node_modules/qs/test/utils.js +12 -0
  283. package/node_modules/sax/lib/sax.js +27 -9
  284. package/node_modules/sax/package.json +1 -1
  285. package/node_modules/type-is/node_modules/content-type/README.md +2 -0
  286. package/node_modules/type-is/node_modules/content-type/dist/index.d.ts +20 -0
  287. package/node_modules/type-is/node_modules/content-type/dist/index.js +20 -14
  288. package/node_modules/type-is/node_modules/content-type/dist/index.js.map +1 -1
  289. package/node_modules/type-is/node_modules/content-type/package.json +1 -1
  290. package/node_modules/ws/lib/permessage-deflate.js +3 -1
  291. package/node_modules/ws/package.json +1 -1
  292. package/package.json +5 -5
  293. package/bin/completion/scripts/cds-bash.sh +0 -20
  294. package/bin/completion/scripts/cds-fish.fish +0 -13
  295. package/bin/completion/scripts/cds-ps.ps1 +0 -19
  296. package/bin/completion/scripts/cds-zsh.sh +0 -13
  297. package/lib/deploy/index.js +0 -25
  298. package/lib/import/asyncapi/importAsyncAPI.js +0 -33
  299. package/lib/import/asyncapi/index.js +0 -18
  300. package/lib/import/asyncapi/parserContext.js +0 -372
  301. package/lib/import/asyncapi/utils.js +0 -49
  302. package/lib/init/template/nodejs/files/.gitignore.hbs +0 -34
  303. package/node_modules/@sap/cds/libx/_runtime/ucl/queries.js +0 -61
  304. package/node_modules/@sap/cds-mtxs/lib/migration/extension-project-builder.js +0 -129
  305. package/node_modules/@sap/cds-mtxs/lib/migration/extension-project.js +0 -89
  306. package/node_modules/@sap/cds-mtxs/lib/migration/mtx-adapter.js +0 -230
  307. package/node_modules/negotiator/HISTORY.md +0 -114
  308. /package/{lib → bin}/upgrade/context.js +0 -0
  309. /package/{lib → bin}/upgrade/migrations/cds10/migrations.json +0 -0
  310. /package/{lib → bin}/upgrade/preflight/cds-version.js +0 -0
  311. /package/{lib → bin}/upgrade/preflight/project-type.js +0 -0
  312. /package/{lib → bin}/upgrade/steps/analyze/classification.js +0 -0
  313. /package/{lib → bin}/upgrade/steps/analyze/detector.js +0 -0
  314. /package/{lib → bin}/upgrade/steps/analyze/evaluator.js +0 -0
  315. /package/{lib → bin}/upgrade/steps/analyze/reporter.js +0 -0
  316. /package/{lib → bin}/upgrade/steps/analyze/setup.js +0 -0
  317. /package/{lib → bin}/upgrade/steps/analyze/utils/detection.js +0 -0
  318. /package/{lib → bin}/upgrade/steps/analyze/utils/files.js +0 -0
  319. /package/{lib → bin}/upgrade/steps/analyze/utils/grep.js +0 -0
  320. /package/{lib → bin}/upgrade/steps/analyze/utils/yaml.js +0 -0
  321. /package/{lib → bin}/upgrade/util/workspaces.js +0 -0
  322. /package/{lib → bin}/watch/esm-resolve-registration.js +0 -0
  323. /package/{lib → bin}/watch/livereload-server.js +0 -0
  324. /package/{lib → bin}/watch/node-watch.js +0 -0
  325. /package/{lib → bin}/watch/open.js +0 -0
  326. /package/{lib → bin}/watch/watcher.js +0 -0
@@ -22,9 +22,10 @@ const authorizedRequest = ({ method, uri, path, oa2, tenant, dataObj, headers, t
22
22
  method
23
23
  }
24
24
 
25
+ // EMIS management API requires a body for write requests, even if empty
25
26
  let data
26
- if (dataObj) {
27
- data = JSON.stringify(dataObj)
27
+ if (dataObj || method === 'PUT' || method === 'POST') {
28
+ data = JSON.stringify(dataObj ?? {})
28
29
  httpOptions.headers['Content-Type'] = 'application/json'
29
30
  httpOptions.headers['Content-Length'] = Buffer.byteLength(data)
30
31
  }
@@ -20,16 +20,18 @@ class EndpointRegistry {
20
20
  this.webhookCallbacks = new Map()
21
21
  this.deployCallbacks = new Map()
22
22
 
23
- const _IS_SECURED = !!(
23
+ const _HAS_APP_AUTH = !!(
24
24
  cds.env.requires.auth &&
25
25
  (cds.env.requires.auth.impl || !(cds.env.requires.auth.kind in { mocked: 1, 'mocked-auth': 1 }))
26
26
  )
27
+ const _HAS_MESSAGING_XSUAA = !!cds.env.requires.messaging?.xsuaa
28
+ const _IS_SECURED = _HAS_APP_AUTH || _HAS_MESSAGING_XSUAA
27
29
  const _IS_PRODUCTION = process.env.NODE_ENV === 'production'
28
30
 
29
31
  cds.app.use(basePath, cds.middlewares.context())
30
32
  if (_IS_SECURED) {
31
33
  // unofficial XSUAA fallback
32
- if (cds.env.requires.messaging?.xsuaa) {
34
+ if (_HAS_MESSAGING_XSUAA) {
33
35
  cds.app.use(basePath, _xsuaa_fallback())
34
36
  } else {
35
37
  cds.app.use(basePath, cds.middlewares.auth())
@@ -38,7 +40,11 @@ class EndpointRegistry {
38
40
  cds.app.use(basePath, (_req, _res, next) => {
39
41
  next(cds.context.user._is_anonymous ? 401 : undefined)
40
42
  })
41
- } else if (_IS_PRODUCTION) LOG.warn('Messaging endpoints not secured')
43
+ } else if (_IS_PRODUCTION) {
44
+ throw new Error(
45
+ 'Messaging endpoints require authentication in production. Configure cds.requires.auth or cds.requires.messaging.xsuaa.'
46
+ )
47
+ }
42
48
 
43
49
  cds.app.use(basePath, express.json({ type: 'application/*+json' }))
44
50
  cds.app.use(basePath, express.json())
@@ -1,10 +1,5 @@
1
1
  const cds = require('../cds')
2
- const {
3
- getCloudSdk,
4
- getCloudSdkConnectivity,
5
- getCloudSdkResilience,
6
- shouldUseCloudSdk
7
- } = require('./utils/cloudSdkProvider')
2
+ const { getCloudSdk, getCloudSdkResilience, shouldUseCloudSdk } = require('./utils/cloudSdkProvider')
8
3
  const { run } = require('./utils/client')
9
4
  const { extractRequestConfig } = require('./utils/query')
10
5
  const { hasAliasedColumns } = require('./utils/data')
@@ -171,13 +166,6 @@ const _isSelectWithAliasedColumns = q => q?.SELECT && !q._transitions && q.SELEC
171
166
 
172
167
  const resolvedTargetOfQuery = q => q?._transitions?.at(-1)?.target
173
168
 
174
- const _resolveSelectionStrategy = selectionStrategy => {
175
- const { DestinationSelectionStrategies } = getCloudSdkConnectivity()
176
- const strategy = DestinationSelectionStrategies[selectionStrategy]
177
- if (typeof strategy !== 'function') cds.error`Unsupported destination selection strategy "${selectionStrategy}".`
178
- return strategy
179
- }
180
-
181
169
  const _getKind = options => {
182
170
  let kind = options.credentials?.kind ?? options.kind
183
171
  if (typeof kind === 'object') {
@@ -206,9 +194,6 @@ class RemoteService extends cds.Service {
206
194
  this.datasource = this.options.datasource
207
195
 
208
196
  this.destinationOptions = this.options.destinationOptions || {}
209
- if (typeof this.destinationOptions.selectionStrategy === 'string') {
210
- this.destinationOptions.selectionStrategy = _resolveSelectionStrategy(this.destinationOptions.selectionStrategy)
211
- }
212
197
 
213
198
  if (this.options.credentials.destination) this.destination = this.options.credentials.destination
214
199
  else this.destination = _getDestination(this.definition?.name ?? this.datasource, this.options.credentials)
@@ -223,7 +208,7 @@ class RemoteService extends cds.Service {
223
208
 
224
209
  // This is required for BAS creating remote services only for events
225
210
  // Middlewares are created at time of the first request, on the fly
226
- if (shouldUseCloudSdk(this.destination)) {
211
+ if (shouldUseCloudSdk()) {
227
212
  this.middlewares = {
228
213
  timeout: getCloudSdkResilience().timeout(this.requestTimeout),
229
214
  csrf: this.csrf && getCloudSdk().csrf(this.csrf)
@@ -249,7 +234,7 @@ class RemoteService extends cds.Service {
249
234
  if (typeof this.destination === 'object' && !this.destination.url)
250
235
  cds.error`"url" or "destination" property must be configured in "credentials" of "${this.name}".`
251
236
 
252
- const requestConfig = extractRequestConfig(req, query, this)
237
+ const requestConfig = extractRequestConfig(req, this)
253
238
  requestConfig.headers = _getHeaders(requestConfig.headers, req)
254
239
 
255
240
  if (this.kind === 'hcql' && req.iterator && !req.objectMode)
@@ -284,7 +269,7 @@ class RemoteService extends cds.Service {
284
269
  if (jwt) additionalOptions.jwt = jwt
285
270
 
286
271
  const csrfConfig = requestConfig._autoBatchedGet ? this.csrfInBatch : this.csrf
287
- if (shouldUseCloudSdk(this.destination)) {
272
+ if (shouldUseCloudSdk()) {
288
273
  // Mount resilience and csrf middlewares for SAP Cloud SDK
289
274
  requestConfig.middleware = [this.middlewares.timeout]
290
275
  if (csrfConfig && this.middlewares.csrf) requestConfig.middleware.push(this.middlewares.csrf)
@@ -0,0 +1,42 @@
1
+ /**
2
+ * LRU cache backed by Map insertion order.
3
+ * On get/set the entry is moved to the MRU end; the LRU entry (first key) is
4
+ * evicted when the map is full. `maxSize = 0` means unlimited.
5
+ */
6
+ class LruCache {
7
+ /** @param {number} [maxSize=0] Maximum number of entries; 0 = unlimited. */
8
+ constructor(maxSize = 0) {
9
+ this._map = new Map()
10
+ this._maxSize = maxSize
11
+ }
12
+
13
+ /** @param {*} key Cache key. */
14
+ get(key) {
15
+ const value = this._map.get(key)
16
+ if (value !== undefined) {
17
+ this._map.delete(key)
18
+ this._map.set(key, value)
19
+ }
20
+ return value
21
+ }
22
+
23
+ /**
24
+ * @param {*} key Cache key. `null` and `undefined` are ignored.
25
+ * @param {*} value Value to store.
26
+ * @param {number} [ttl] Time-to-live in milliseconds. Entry is auto-deleted after this duration.
27
+ */
28
+ set(key, value, ttl) {
29
+ if (key == null) return
30
+ this._map.delete(key)
31
+ if (this._maxSize > 0 && this._map.size >= this._maxSize) this._map.delete(this._map.keys().next().value)
32
+ this._map.set(key, value)
33
+ if (ttl > 0) setTimeout(() => this._map.delete(key), ttl).unref()
34
+ }
35
+
36
+ /** @param {*} key Cache key. */
37
+ delete(key) {
38
+ this._map.delete(key)
39
+ }
40
+ }
41
+
42
+ module.exports = { LruCache }
@@ -1,7 +1,7 @@
1
1
  const cds = require('../../cds')
2
2
  const LOG = cds.log('remote')
3
3
 
4
- const { getCloudSdk, shouldUseCloudSdk } = require('./cloudSdkProvider')
4
+ const { getCloudSdk, getCloudSdkConnectivity, shouldUseCloudSdk } = require('./cloudSdkProvider')
5
5
  const { executeHttpRequest: fetchExecuteHttpRequest } = require('./fetchClient')
6
6
 
7
7
  const SANITIZE_VALUES = process.env.NODE_ENV === 'production' && cds.env.log.sanitize_values !== false
@@ -27,7 +27,6 @@ const _sanitizeHeaders = headers => {
27
27
  }
28
28
 
29
29
  const _executeHttpRequest = async ({ requestConfig, destination, destinationOptions, jwt }) => {
30
- const useCloudSdk = shouldUseCloudSdk(destination)
31
30
  if (typeof destination === 'string') {
32
31
  destination = { destinationName: destination, ...destinationOptions }
33
32
  if (destination.jwt === undefined) destination.jwt = jwt
@@ -48,7 +47,7 @@ const _executeHttpRequest = async ({ requestConfig, destination, destinationOpti
48
47
  const maxBodyLength = cds.env?.remote?.max_body_length
49
48
  if (maxBodyLength) requestConfig.maxBodyLength = maxBodyLength
50
49
 
51
- if (useCloudSdk) {
50
+ if (shouldUseCloudSdk()) {
52
51
  const { executeHttpRequestWithOrigin } = getCloudSdk()
53
52
  // cloud sdk requires a new mechanism to differentiate the priority of headers
54
53
  // "custom" keeps the highest priority as before
@@ -56,6 +55,15 @@ const _executeHttpRequest = async ({ requestConfig, destination, destinationOpti
56
55
  ...requestConfig,
57
56
  headers: { custom: { ...requestConfig.headers } }
58
57
  }
58
+ // selectionStrategy is kept as a plain string in destinationOptions;
59
+ // Cloud SDK needs the corresponding function — resolve it lazily here
60
+ if (typeof destination.selectionStrategy === 'string') {
61
+ const { DestinationSelectionStrategies } = getCloudSdkConnectivity()
62
+ const strategy = DestinationSelectionStrategies[destination.selectionStrategy]
63
+ if (typeof strategy !== 'function')
64
+ cds.error`Unsupported destination selection strategy "${destination.selectionStrategy}".`
65
+ destination = { ...destination, selectionStrategy: strategy }
66
+ }
59
67
  if (LOG._debug) LOG.debug('Executing via @sap-cloud-sdk/http-client.')
60
68
  return executeHttpRequestWithOrigin(destination, sdkConfig, { fetchCsrfToken: false })
61
69
  }
@@ -270,8 +278,10 @@ module.exports.run = async (requestConfig, options) => {
270
278
 
271
279
  if (kind === 'hcql') {
272
280
  if (response.headers?.['content-type']?.includes('application/octet-stream')) return response.data
273
- let { data = [], affected } = response.data
281
+ let { data = [], affected, $count } = response.data
274
282
  if (affected !== undefined) data.affected = affected
283
+ if ($count == null && response.headers['x-total-count']) $count = Number(response.headers['x-total-count'])
284
+ if ($count !== undefined) data.$count = $count
275
285
  return data
276
286
  }
277
287
 
@@ -1,5 +1,4 @@
1
1
  const cds = require('../../cds')
2
- const { findLocalDestinations } = require('./fetchClient')
3
2
 
4
3
  const _require = module => {
5
4
  try {
@@ -39,14 +38,8 @@ const isCloudSdkInstalled = () => {
39
38
  return _cloudSdkInstalled
40
39
  }
41
40
 
42
- const shouldUseCloudSdk = destination => {
43
- // Native node fetch for named destinations is supported only if locally resolvable via process.env.destinations
44
- const _destination = typeof destination === 'string' ? findLocalDestinations(destination)[0] : destination
45
- if (!_destination?.url) return true
46
- if (_destination.authentication && !(_destination.authentication in { NoAuthentication: 1, BasicAuthentication: 1 }))
47
- return true
48
- // here, it would be _possible_ to use native fetch
49
- // explicit config?
41
+ const shouldUseCloudSdk = () => {
42
+ // Explicit config takes priority
50
43
  if (cds.env.remote?.native_fetch != null) return !cds.env.remote.native_fetch
51
44
  // if cloud sdk is there, use it
52
45
  return isCloudSdkInstalled()
@@ -0,0 +1,150 @@
1
+ const cds = require('../../cds')
2
+ const { getToken } = require('./token')
3
+ const { LruCache } = require('./LruCache')
4
+ const { isTimeoutError, getTimeoutError } = require('./error')
5
+
6
+ const LOG = cds.log('remote')
7
+
8
+ const _destinationCache = new LruCache(cds.env.remote.cache_size)
9
+
10
+ let BUFFER_MS
11
+
12
+ const SUPPORTED_AUTH_TYPES = { NoAuthentication: 1, BasicAuthentication: 1, OAuth2ClientCredentials: 1 }
13
+
14
+ const HEADERS_PREFIX = 'URL.headers.'
15
+ const QUERIES_PREFIX = 'URL.queries.'
16
+
17
+ const _decodeJwt = jwt => {
18
+ try {
19
+ const payload = JSON.parse(Buffer.from(jwt.split('.')[1], 'base64url').toString())
20
+ const { zid, app_tid, zone_uuid, user_id, user_uuid } = payload
21
+ return { tenant: zid || app_tid || zone_uuid, user: user_id || user_uuid }
22
+ } catch {
23
+ LOG.warn('Failed to decode JWT payload')
24
+ return {}
25
+ }
26
+ }
27
+
28
+ const _identity = jwt => {
29
+ const { tenant, user } = (jwt ? _decodeJwt(jwt) : cds.context) ?? {}
30
+ return { tenant, user: user?.id ?? user }
31
+ }
32
+
33
+ const _fetchDestination = async (uri, name, token, userJwt) => {
34
+ const headers = { authorization: `Bearer ${token}` }
35
+ // Note: Cloud SDK only sends x-user-token for OAuth2UserTokenExchange/OAuth2SAMLBearerAssertion
36
+ if (userJwt) headers['x-user-token'] = userJwt
37
+ LOG._debug && LOG.debug(`Fetching destination '${name}'`)
38
+
39
+ const { timeout } = cds.env.remote
40
+ const signal = AbortSignal.timeout(cds.utils.ms4(timeout))
41
+ let res
42
+ try {
43
+ res = await fetch(`${uri}/destination-configuration/v1/destinations/${encodeURIComponent(name)}`, {
44
+ headers,
45
+ signal
46
+ })
47
+ } catch (e) {
48
+ if (isTimeoutError(e)) throw getTimeoutError(e, `Fetching destination '${name}'`, timeout)
49
+ throw e
50
+ }
51
+
52
+ if (res.status === 404) return null
53
+ if (!res.ok) throw new Error(`Fetching destination '${name}' failed with status ${res.status}.`)
54
+ return res.json()
55
+ }
56
+
57
+ const _processResponse = ({ destinationConfiguration: dc, authTokens }) => {
58
+ if (!(dc.Authentication in SUPPORTED_AUTH_TYPES))
59
+ LOG.warn(`Auth type ${dc.Authentication} is not yet supported. Falling back to best-effort.`)
60
+
61
+ const authToken = authTokens?.[0]
62
+ const headers = {}
63
+ if (authToken?.error != null) throw new Error(`Destination returned auth token error: ${authToken.error}`)
64
+ if (authToken?.http_header) headers[authToken.http_header.key.toLowerCase()] = authToken.http_header.value
65
+ if (dc['sap-client']) headers['sap-client'] = dc['sap-client']
66
+
67
+ const queryParameters = {}
68
+ for (const [k, v] of Object.entries(dc)) {
69
+ if (k.startsWith(HEADERS_PREFIX)) headers[k.slice(HEADERS_PREFIX.length).toLowerCase()] = v
70
+ else if (k.startsWith(QUERIES_PREFIX)) queryParameters[k.slice(QUERIES_PREFIX.length)] = v
71
+ }
72
+
73
+ const expiresIn = authToken?.expiresIn && Number(authToken.expiresIn)
74
+
75
+ return {
76
+ url: dc.URL,
77
+ authentication: dc.Authentication,
78
+ expiresIn,
79
+ ...(Object.keys(headers).length && { headers }),
80
+ ...(Object.keys(queryParameters).length && { queryParameters })
81
+ }
82
+ }
83
+
84
+ const _lookup = async (creds, destinationName, tenant, jwt) => {
85
+ if (tenant) {
86
+ const subscriberToken = await getToken(creds, tenant)
87
+ const subscriberResult = await _fetchDestination(creds.uri, destinationName, subscriberToken, jwt)
88
+ if (subscriberResult) {
89
+ LOG._debug && LOG.debug(`Destination '${destinationName}' resolved for subscriber tenant '${tenant}'`)
90
+ return _processResponse(subscriberResult)
91
+ }
92
+ LOG._debug &&
93
+ LOG.debug(
94
+ `Destination '${destinationName}' not found for subscriber tenant '${tenant}', falling back to provider`
95
+ )
96
+ }
97
+
98
+ const providerToken = await getToken(creds)
99
+ const providerResult = await _fetchDestination(creds.uri, destinationName, providerToken)
100
+ if (providerResult) {
101
+ LOG._debug && LOG.debug(`Destination '${destinationName}' resolved for provider tenant`)
102
+ return _processResponse(providerResult)
103
+ }
104
+
105
+ throw new Error(`Destination '${destinationName}' not found.`)
106
+ }
107
+
108
+ const getDestination = async (destinationName, { jwt } = {}) => {
109
+ const creds = cds.env.requires?.destinations?.credentials
110
+ if (!creds?.uri || !creds?.clientid) {
111
+ LOG._debug && LOG.debug('No Destination Service credentials found')
112
+ return null
113
+ }
114
+
115
+ LOG._debug && LOG.debug(`Resolving destination '${destinationName}'`)
116
+
117
+ const { tenant, user } = _identity(jwt)
118
+ // JWT present but no tenant claim — skip cache to avoid cross-tenant pollution
119
+ const cacheKey = jwt && !tenant ? undefined : [destinationName, tenant, user].filter(Boolean).join(':')
120
+ if (!cacheKey) LOG._debug && LOG.debug(`Destination '${destinationName}': JWT has no tenant claim, skipping cache`)
121
+
122
+ const cached = _destinationCache.get(cacheKey)
123
+ if (cached) {
124
+ LOG._debug && LOG.debug(`Destination '${destinationName}' served from cache`)
125
+ return cached
126
+ }
127
+
128
+ const promise = _lookup(creds, destinationName, tenant, jwt)
129
+ .then(resolved => {
130
+ if (!resolved) return null
131
+ const { expiresIn = 60 * 60, ...destination } = resolved
132
+ const expiresInMs = expiresIn * 1000
133
+ BUFFER_MS ??= cds.utils.ms4(cds.env.remote.cache_expiry_buffer)
134
+ const ttl = Math.max(0, expiresInMs - BUFFER_MS)
135
+ LOG._debug && LOG.debug(`Destination for '${cacheKey}' cached for ${ttl}ms`)
136
+ _destinationCache.set(cacheKey, destination, ttl)
137
+ return destination
138
+ })
139
+ .catch(err => {
140
+ LOG.warn(`Failed to resolve destination '${destinationName}':`, err.message)
141
+ _destinationCache.delete(cacheKey)
142
+ throw err
143
+ })
144
+
145
+ // cache promise immediately to deduplicate concurrent in-flight requests
146
+ _destinationCache.set(cacheKey, promise)
147
+ return promise
148
+ }
149
+
150
+ module.exports = { getDestination }
@@ -0,0 +1,13 @@
1
+ const isTimeoutError = e => e.name === 'TimeoutError'
2
+
3
+ /**
4
+ * @param {Error} cause
5
+ * @param {string} prefix
6
+ * @param {number|string} timeout
7
+ */
8
+ const getTimeoutError = (cause, prefix, timeout) =>
9
+ Object.assign(new Error(`${prefix} exceeded timeout of ${typeof timeout === 'number' ? `${timeout}ms` : timeout}.`), {
10
+ cause
11
+ })
12
+
13
+ module.exports = { isTimeoutError, getTimeoutError }
@@ -2,6 +2,8 @@ const cds = require('../../cds')
2
2
  const LOG = cds.log('remote')
3
3
 
4
4
  const { Readable } = require('stream')
5
+ const { getDestination } = require('./destinationClient')
6
+ const { isTimeoutError, getTimeoutError } = require('./error')
5
7
 
6
8
  // process.env.destinations — CloudSDK convention for local destination resolution:
7
9
  // a JSON array of { name, url, ... } objects, e.g. for local development or testing.
@@ -23,7 +25,7 @@ const _normalizeAuthentication = destination => {
23
25
  return { ...destination, authentication }
24
26
  }
25
27
 
26
- const _resolveDestination = destination => {
28
+ const _resolveDestination = async destination => {
27
29
  if (destination.url) return _normalizeAuthentication(destination)
28
30
 
29
31
  const matches = findLocalDestinations(destination.destinationName)
@@ -38,7 +40,8 @@ const _resolveDestination = destination => {
38
40
  return _normalizeAuthentication({ ...destination, ...found })
39
41
  }
40
42
 
41
- // REVISIT: resolve named destination via BTP Destination Service
43
+ const fetched = await getDestination(destination.destinationName, { jwt: destination.jwt })
44
+ if (fetched) return _normalizeAuthentication({ ...destination, ...fetched })
42
45
 
43
46
  throw new Error(`Cannot resolve destination '${destination.destinationName}'.`)
44
47
  }
@@ -109,11 +112,19 @@ const _readBody = async (response, responseType) => {
109
112
  */
110
113
  const executeHttpRequest = async (destination, requestConfig) => {
111
114
  // Resolve destination
112
- destination = _resolveDestination(destination)
115
+ destination = await _resolveDestination(destination)
113
116
  const baseUrl = (destination.url || '').replace(/\/$/, '')
114
117
 
115
- const { method = 'GET', url = '', data, responseType, maxBodyLength, timeout, csrf } = requestConfig
116
- const signal = timeout ? AbortSignal.timeout(timeout) : undefined // eslint-disable-line no-undef
118
+ const {
119
+ method = 'GET',
120
+ url = '',
121
+ data,
122
+ responseType,
123
+ maxBodyLength,
124
+ timeout = cds.env.remote.timeout,
125
+ csrf
126
+ } = requestConfig
127
+ const signal = timeout && AbortSignal.timeout(cds.utils.ms4(timeout))
117
128
 
118
129
  // Assemble headers
119
130
  const headers = { ...destination.headers, ...requestConfig.headers }
@@ -122,10 +133,16 @@ const executeHttpRequest = async (destination, requestConfig) => {
122
133
  headers.authorization = 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64')
123
134
  }
124
135
 
125
- const urlWithoutQuery = baseUrl + (url && !url.startsWith('/') ? `/${url}` : url)
136
+ // Build full URL
137
+ let fullUrl = baseUrl + (url && !url.startsWith('/') ? `/${url}` : url)
138
+ const queryParameters = { ...destination.queryParameters, ...requestConfig.params }
139
+ if (Object.keys(queryParameters).length) {
140
+ const qs = new URLSearchParams(Object.entries(queryParameters).map(([k, v]) => [k, String(v)])).toString()
141
+ fullUrl += (fullUrl.includes('?') ? '&' : '?') + qs
142
+ }
126
143
 
127
144
  if (csrf && method !== 'GET' && method !== 'HEAD') {
128
- const result = await _fetchCsrfToken(urlWithoutQuery, headers, csrf, signal)
145
+ const result = await _fetchCsrfToken(fullUrl.split('?')[0], headers, csrf, signal)
129
146
  if (result?.token) headers['x-csrf-token'] = result.token
130
147
  if (result?.cookie) headers.cookie = headers.cookie ? `${headers.cookie};${result.cookie}` : result.cookie
131
148
  }
@@ -143,14 +160,6 @@ const executeHttpRequest = async (destination, requestConfig) => {
143
160
  throw new Error(`Request body length ${len} exceeds configured max_body_length ${maxBodyLength}.`)
144
161
  }
145
162
 
146
- // Build full URL
147
- const { queryParameters } = destination
148
- let fullUrl = urlWithoutQuery
149
- if (queryParameters && Object.keys(queryParameters).length) {
150
- const qs = new URLSearchParams(Object.entries(queryParameters).map(([k, v]) => [k, String(v)])).toString()
151
- fullUrl += (fullUrl.includes('?') ? '&' : '?') + qs
152
- }
153
-
154
163
  if (LOG._debug) LOG.debug(`${method} ${fullUrl}`)
155
164
 
156
165
  // Execute request
@@ -158,9 +167,8 @@ const executeHttpRequest = async (destination, requestConfig) => {
158
167
  try {
159
168
  response = await fetch(fullUrl, { method, headers, body, signal })
160
169
  } catch (e) {
161
- const timedOut = e.name === 'TimeoutError'
162
- const msg = timedOut ? `Exceeded timeout of ${timeout}ms.` : e.message
163
- throw Object.assign(new Error(msg), { cause: e })
170
+ if (isTimeoutError(e)) throw getTimeoutError(e, 'Remote request', timeout)
171
+ throw e
164
172
  }
165
173
 
166
174
  // Parse response
@@ -25,38 +25,39 @@ const _cqnWithPublicEntries = query => {
25
25
  return Object.fromEntries(Object.entries(query).filter(([key]) => !key.startsWith('_')))
26
26
  }
27
27
 
28
- const _cqnToHcqlRequestConfig = cqn => {
28
+ const _extractHcqlRequestConfig = cqn => {
29
+ if (cqn.flat) cqn.flat()
30
+
29
31
  if (cqn.SELECT && cqn.SELECT.from) return { method: 'POST', data: { SELECT: _cqnWithPublicEntries(cqn.SELECT) } }
30
32
  if (cqn.INSERT && cqn.INSERT.into) return { method: 'POST', data: { INSERT: _cqnWithPublicEntries(cqn.INSERT) } }
31
33
  if (cqn.UPDATE && cqn.UPDATE.entity) return { method: 'POST', data: { UPDATE: _cqnWithPublicEntries(cqn.UPDATE) } }
32
34
  if (cqn.UPSERT && cqn.UPSERT.into) return { method: 'POST', data: { UPSERT: _cqnWithPublicEntries(cqn.UPSERT) } }
33
35
  if (cqn.DELETE && cqn.DELETE.from) return { method: 'POST', data: { DELETE: _cqnWithPublicEntries(cqn.DELETE) } }
34
36
 
35
- cds.error(400, 'Invalid CQN object can not be processed.', JSON.stringify(cqn), _cqnToHcqlRequestConfig)
37
+ cds.error(400, 'Invalid CQN object can not be processed.', JSON.stringify(cqn), _extractHcqlRequestConfig)
36
38
  }
37
39
 
38
- const _cqnToRequestConfig = (query, service, req) => {
39
- const { kind, model } = service
40
+ const _extractODataRequestConfig = (req, srv) => {
41
+ if (req.query.UPDATE?.where) cds.error(400, 'Filtering is not supported to specify the subject of UPDATE requests')
42
+ if (req.query.DELETE?.where) cds.error(400, 'Filtering is not supported to specify the subject of DELETE requests')
40
43
 
41
- const queryObject = cds.odata.urlify(query, { kind, model, method: req.method })
44
+ const queryObject = cds.odata.urlify(req, srv)
42
45
 
43
46
  const requestConfig = {
44
47
  method: queryObject.method,
45
- url: queryObject.path
46
- // REVISIT: remove when we can assume that number of remote services running Okra is negligible
47
- // ugly workaround for Okra not allowing spaces in ( x eq 1 )
48
- .replace(/\( /g, '(')
49
- .replace(/ \)/g, ')')
48
+ // REVISIT: remove when we can assume that number of remote services running Okra is negligible
49
+ // ugly workaround for Okra not allowing spaces in ( x eq 1 )
50
+ url: queryObject.path.replace(/\( /g, '(').replace(/ \)/g, ')')
50
51
  }
51
52
 
52
53
  if (queryObject.method !== 'GET' && queryObject.method !== 'HEAD') {
53
- requestConfig.data = kind === 'odata-v2' ? convertV2PayloadData(queryObject.body, req.target) : queryObject.body
54
+ requestConfig.data = srv.kind === 'odata-v2' ? convertV2PayloadData(queryObject.body, req.target) : queryObject.body
54
55
  }
55
56
 
56
57
  return requestConfig
57
58
  }
58
59
 
59
- const _stringToRequestConfig = (query, data, target) => {
60
+ const _createODataRequestConfig = (query, data, target, srvKind) => {
60
61
  const cleanQuery = query.trim()
61
62
  const blankIndex = cleanQuery.substring(0, 8).indexOf(' ')
62
63
 
@@ -66,13 +67,13 @@ const _stringToRequestConfig = (query, data, target) => {
66
67
  }
67
68
 
68
69
  if (data && requestConfig.method !== 'GET' && requestConfig.method !== 'HEAD') {
69
- requestConfig.data = this.kind === 'odata-v2' ? Object.assign({}, convertV2PayloadData(data, target)) : data
70
+ requestConfig.data = srvKind === 'odata-v2' ? Object.assign({}, convertV2PayloadData(data, target)) : data
70
71
  }
71
72
 
72
73
  return requestConfig
73
74
  }
74
75
 
75
- const _pathToRequestConfig = (method, path, data, target, srvName) => {
76
+ const _pathToRequestConfig = (method, path, data, target, srvName, srvKind) => {
76
77
  let url = path
77
78
 
78
79
  if (!url.startsWith('/')) {
@@ -88,7 +89,7 @@ const _pathToRequestConfig = (method, path, data, target, srvName) => {
88
89
 
89
90
  const requestConfig = { method, url }
90
91
  if (data && requestConfig.method !== 'GET' && requestConfig.method !== 'HEAD') {
91
- requestConfig.data = this.kind === 'odata-v2' ? Object.assign({}, convertV2PayloadData(data, target)) : data
92
+ requestConfig.data = srvKind === 'odata-v2' ? Object.assign({}, convertV2PayloadData(data, target)) : data
92
93
  }
93
94
 
94
95
  return requestConfig
@@ -101,12 +102,22 @@ const _hasHeader = (headers, header) =>
101
102
 
102
103
  const KINDS_SUPPORTING_BATCH = { odata: true, 'odata-v2': true, 'odata-v4': true }
103
104
 
104
- const _extractRequestConfig = (req, query, service) => {
105
- if (service.kind === 'hcql' && typeof query === 'object') return _cqnToHcqlRequestConfig(query)
106
- if (typeof query === 'object') return _cqnToRequestConfig(query, service, req)
107
- if (typeof query === 'string') return _stringToRequestConfig(query, req.data, req.target)
108
- //> no model, no service.definition
109
- return _pathToRequestConfig(req.method, req.path, req.data, req.target, service.definition?.name || service.namespace)
105
+ const _extractRequestConfig = (req, srv) => {
106
+ // REVISIT: Split off the other 'kind's here? odata / odata-v4, odata-v2, rest ...
107
+
108
+ if (srv.kind === 'hcql' && typeof req.query === 'object') return _extractHcqlRequestConfig(req.query)
109
+ if (typeof req.query === 'object') return _extractODataRequestConfig(req, srv)
110
+ if (typeof req.query === 'string') return _createODataRequestConfig(req.query, req.data, req.target, srv.kind)
111
+
112
+ // > no model, no service.definition
113
+ return _pathToRequestConfig(
114
+ req.method,
115
+ req.path,
116
+ req.data,
117
+ req.target,
118
+ srv.definition?.name || srv.namespace,
119
+ srv.kind
120
+ )
110
121
  }
111
122
 
112
123
  const _batchEnvelope = (requestConfig, maxGetUrlLength) => {
@@ -137,8 +148,8 @@ const _batchEnvelope = (requestConfig, maxGetUrlLength) => {
137
148
  requestConfig.data = batchedRequest
138
149
  }
139
150
 
140
- module.exports.extractRequestConfig = (req, query, service) => {
141
- const requestConfig = _extractRequestConfig(req, query, service)
151
+ module.exports.extractRequestConfig = (req, service) => {
152
+ const requestConfig = _extractRequestConfig(req, service)
142
153
 
143
154
  if (service.kind === 'odata-v2' && req.event === 'READ' && requestConfig.url?.match(/(\/any\()|(\/all\()/)) {
144
155
  req.reject(501, 'Lambda expressions are not supported in OData v2')