@sap/cds-dk 10.0.7 → 10.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/bin/add.js +7 -23
  3. package/bin/bind.js +14 -0
  4. package/bin/build.js +21 -34
  5. package/bin/cds.js +1 -1
  6. package/bin/compile/index.js +16 -41
  7. package/bin/completion/completionFs.js +11 -8
  8. package/bin/completion/index.js +603 -80
  9. package/bin/completion/scripts/cds.fish +620 -0
  10. package/bin/completion/scripts/cds.ps1 +403 -0
  11. package/bin/completion/scripts/cds.sh +327 -0
  12. package/bin/completion/scripts/cds.zsh +296 -0
  13. package/bin/completion/templates/cds-tmpl.fish +65 -0
  14. package/bin/completion/templates/cds-tmpl.ps1 +217 -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 +11 -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/env.js +4 -1
  25. package/bin/export.js +44 -6
  26. package/bin/help/export.md +3 -0
  27. package/bin/help.js +1 -5
  28. package/bin/import.js +9 -0
  29. package/bin/init.js +6 -24
  30. package/bin/inspect.js +8 -0
  31. package/bin/lint.js +5 -3
  32. package/bin/login.js +26 -20
  33. package/bin/minify.js +6 -1
  34. package/bin/pull.js +19 -6
  35. package/bin/push.js +19 -6
  36. package/bin/repl.js +11 -2
  37. package/bin/run.js +12 -1
  38. package/bin/serve.js +13 -2
  39. package/bin/up.js +17 -11
  40. package/{lib → bin}/upgrade/constants.js +1 -1
  41. package/bin/{upgrade.js → upgrade/index.js} +2 -2
  42. package/{lib → bin}/upgrade/preflight/consistency.js +1 -1
  43. package/{lib → bin}/upgrade/preflight/index.js +1 -1
  44. package/{lib → bin}/upgrade/steps/analyze/index.js +1 -1
  45. package/{lib → bin}/upgrade/steps/install.js +1 -1
  46. package/{lib → bin}/upgrade/steps/openrewrite.js +1 -1
  47. package/{lib → bin}/upgrade/steps/removals.js +1 -1
  48. package/{lib → bin}/upgrade/steps/rewrite.js +1 -1
  49. package/{lib → bin}/upgrade/steps/verify.js +1 -1
  50. package/{lib/upgrade/index.js → bin/upgrade/upgrade.js} +2 -2
  51. package/{lib → bin}/upgrade/util/pom.js +2 -2
  52. package/{lib → bin}/upgrade/util/prompt.js +1 -1
  53. package/bin/util/containerize.js +10 -4
  54. package/bin/version.js +10 -1
  55. package/{lib → bin}/watch/hot-reload.js +1 -1
  56. package/bin/{watch.js → watch/index.js} +24 -9
  57. package/{lib → bin}/watch/livereload-connect.js +1 -1
  58. package/{lib → bin}/watch/vite-watch.js +1 -1
  59. package/{lib → bin}/watch/watched.js +2 -2
  60. package/lib/build/constants.js +1 -1
  61. package/lib/build/plugins/fiori/index.js +31 -2
  62. package/lib/build/plugins/internal.js +4 -2
  63. package/lib/build/plugins/mtx-sidecar/index.js +7 -8
  64. package/lib/build/plugins/nodejs/index.js +64 -7
  65. package/lib/build/util.js +2 -4
  66. package/lib/compile/to_asyncapi.js +1 -1
  67. package/lib/env/schema.js +16 -0
  68. package/lib/env/schemas/cds-rc.js +1 -76
  69. package/lib/import/importapi.js +2 -3
  70. package/lib/import/message.js +0 -2
  71. package/lib/init/constants.js +2 -1
  72. package/lib/init/index.js +2 -2
  73. package/lib/init/merge.js +123 -280
  74. package/lib/init/plugin.js +3 -1
  75. package/lib/init/projectReader.js +22 -6
  76. package/lib/init/registries/xs-app.js +3 -5
  77. package/lib/init/template/ai/files/mta.yaml.hbs +1 -1
  78. package/lib/init/template/ai/index.js +14 -2
  79. package/lib/init/template/ams/index.js +2 -2
  80. package/lib/init/template/app-frontend/files/containerize.yaml.hbs +2 -2
  81. package/lib/init/template/app-frontend/index.js +4 -4
  82. package/lib/init/template/application-logging/index.js +2 -2
  83. package/lib/init/template/approuter/files/package.json +1 -1
  84. package/lib/init/template/approuter/index.js +8 -3
  85. package/lib/init/template/attachments/files/package.json.hbs +1 -1
  86. package/lib/init/template/attachments/index.js +2 -2
  87. package/lib/init/template/audit-logging/index.js +2 -2
  88. package/lib/init/template/cloud-logging/files/package.json.hbs +4 -4
  89. package/lib/init/template/cloud-logging/index.js +2 -2
  90. package/lib/init/template/completion/completionSetup.js +36 -39
  91. package/lib/init/template/connectivity/index.js +2 -2
  92. package/lib/init/template/data/as-csv.js +15 -5
  93. package/lib/init/template/data/as-json.js +117 -42
  94. package/lib/init/template/data/index.js +16 -2
  95. package/lib/init/template/destination/index.js +2 -2
  96. package/lib/init/template/dynatrace/files/package.json.hbs +3 -3
  97. package/lib/init/template/dynatrace/index.js +2 -2
  98. package/lib/init/template/enterprise-messaging/index.js +2 -2
  99. package/lib/init/template/enterprise-messaging-shared/index.js +2 -2
  100. package/lib/init/template/extensibility/files/mta.yaml.hbs +11 -0
  101. package/lib/init/template/extensibility/files/xs-security.json.hbs +1 -1
  102. package/lib/init/template/extensibility/index.js +3 -3
  103. package/lib/init/template/github-actions/index.js +2 -2
  104. package/lib/init/template/hana/index.js +3 -2
  105. package/lib/init/template/hana-serverless/files/db/package.json +14 -0
  106. package/lib/init/template/hana-serverless/files/identity.values.yaml.hbs +10 -0
  107. package/lib/init/template/hana-serverless/files/mta.yaml.hbs +64 -0
  108. package/lib/init/template/hana-serverless/files/package.json.hbs +35 -0
  109. package/lib/init/template/hana-serverless/files/src/.hdiconfig +139 -0
  110. package/lib/init/template/hana-serverless/files/undeploy.json +7 -0
  111. package/lib/init/template/hana-serverless/files/values.yaml.hbs +18 -0
  112. package/lib/init/template/hana-serverless/index.js +184 -0
  113. package/lib/init/template/html5-repo/files/containerize.yaml.hbs +2 -2
  114. package/lib/init/template/html5-repo/files/mta.yaml.hbs +5 -0
  115. package/lib/init/template/html5-repo/index.js +18 -14
  116. package/lib/init/template/http/index.js +123 -26
  117. package/lib/init/template/ias/index.js +2 -2
  118. package/lib/init/template/kafka/index.js +2 -2
  119. package/lib/init/template/kyma/index.js +7 -5
  120. package/lib/init/template/malware-scanner/index.js +2 -2
  121. package/lib/init/template/mta/files/mta.yaml.hbs +1 -1
  122. package/lib/init/template/multitenancy/index.js +18 -3
  123. package/lib/init/template/notifications/index.js +2 -2
  124. package/lib/init/template/portal/files/mta.yaml.hbs +4 -0
  125. package/lib/init/template/portal/index.js +13 -8
  126. package/lib/init/template/postgres/index.js +2 -2
  127. package/lib/init/template/redis/index.js +2 -2
  128. package/lib/init/template/sample/files/db/currencies.cds +2 -0
  129. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Authors.csv +1 -1
  130. package/lib/init/template/tiny-sample/files/nodejs/db/data/CatalogService.Books.csv +2 -2
  131. package/lib/init/template/workzone-standard/index.js +5 -5
  132. package/lib/init/template/xsuaa/index.js +20 -2
  133. package/lib/lint/eslintHelp.json +1 -1
  134. package/lib/mtx/auth_manager.js +103 -37
  135. package/lib/mtx/params.js +3 -2
  136. package/lib/mtx/settings_manager.js +45 -28
  137. package/lib/mtx/tenant_action.js +0 -1
  138. package/lib/mtx/util/credentials.js +62 -0
  139. package/node_modules/@cap-js/db-service/CHANGELOG.md +17 -0
  140. package/node_modules/@cap-js/db-service/lib/common/generic-pool.js +4 -7
  141. package/node_modules/@cap-js/db-service/lib/cqn2pql.js +1 -1
  142. package/node_modules/@cap-js/db-service/lib/cqn2sql.js +15 -11
  143. package/node_modules/@cap-js/db-service/lib/cqn4sql.js +119 -19
  144. package/node_modules/@cap-js/db-service/lib/infer/index.js +27 -16
  145. package/node_modules/@cap-js/db-service/lib/recursion-guard.js +40 -0
  146. package/node_modules/@cap-js/db-service/lib/search.js +1 -1
  147. package/node_modules/@cap-js/db-service/package.json +1 -1
  148. package/node_modules/@cap-js/sqlite/CHANGELOG.md +19 -0
  149. package/node_modules/@cap-js/sqlite/lib/SQLiteService.js +110 -14
  150. package/node_modules/@cap-js/sqlite/package.json +3 -3
  151. package/node_modules/@sap/cds/CHANGELOG.md +57 -0
  152. package/node_modules/@sap/cds/app/index.js +5 -4
  153. package/node_modules/@sap/cds/eslint.config.mjs +1 -0
  154. package/node_modules/@sap/cds/lib/compile/minify.js +14 -10
  155. package/node_modules/@sap/cds/lib/env/cds-env.js +1 -1
  156. package/node_modules/@sap/cds/lib/env/cds-requires.js +22 -11
  157. package/node_modules/@sap/cds/lib/env/defaults.js +4 -1
  158. package/node_modules/@sap/cds/lib/env/schemas/cds-rc.js +5 -2
  159. package/node_modules/@sap/cds/lib/plugins.js +9 -5
  160. package/node_modules/@sap/cds/lib/req/spawn.js +1 -1
  161. package/node_modules/@sap/cds/lib/srv/srv-dispatch.js +1 -1
  162. package/node_modules/@sap/cds/lib/srv/srv-models.js +1 -1
  163. package/node_modules/@sap/cds/lib/utils/cds-utils.js +2 -1
  164. package/node_modules/@sap/cds/libx/_runtime/common/generic/assert.js +2 -3
  165. package/node_modules/@sap/cds/libx/_runtime/common/generic/auth/restrict.js +8 -1
  166. package/node_modules/@sap/cds/libx/_runtime/common/generic/crud.js +18 -8
  167. package/node_modules/@sap/cds/libx/_runtime/fiori/lean-draft.js +25 -17
  168. package/node_modules/@sap/cds/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +9 -3
  169. package/node_modules/@sap/cds/libx/_runtime/remote/Service.js +4 -19
  170. package/node_modules/@sap/cds/libx/_runtime/remote/utils/LruCache.js +42 -0
  171. package/node_modules/@sap/cds/libx/_runtime/remote/utils/client.js +14 -4
  172. package/node_modules/@sap/cds/libx/_runtime/remote/utils/cloudSdkProvider.js +2 -9
  173. package/node_modules/@sap/cds/libx/_runtime/remote/utils/destinationClient.js +150 -0
  174. package/node_modules/@sap/cds/libx/_runtime/remote/utils/error.js +13 -0
  175. package/node_modules/@sap/cds/libx/_runtime/remote/utils/fetchClient.js +26 -18
  176. package/node_modules/@sap/cds/libx/_runtime/remote/utils/query.js +34 -23
  177. package/node_modules/@sap/cds/libx/_runtime/remote/utils/token.js +82 -0
  178. package/node_modules/@sap/cds/libx/_runtime/ucl/Service.js +36 -189
  179. package/node_modules/@sap/cds/libx/common/utils/streaming.js +16 -16
  180. package/node_modules/@sap/cds/libx/http/location.js +3 -3
  181. package/node_modules/@sap/cds/libx/odata/middleware/batch.js +16 -10
  182. package/node_modules/@sap/cds/libx/odata/middleware/metadata.js +9 -6
  183. package/node_modules/@sap/cds/libx/odata/middleware/service-document.js +1 -1
  184. package/node_modules/@sap/cds/libx/odata/parse/afterburner.js +19 -3
  185. package/node_modules/@sap/cds/libx/odata/parse/cqn2odata.js +83 -86
  186. package/node_modules/@sap/cds/libx/queue/SchedulingService.js +17 -2
  187. package/node_modules/@sap/cds/libx/queue/processing.js +1 -3
  188. package/node_modules/@sap/cds/libx/rest/RestAdapter.js +3 -7
  189. package/node_modules/@sap/cds/libx/rest/middleware/delete.js +3 -3
  190. package/node_modules/@sap/cds/libx/rest/middleware/parse.js +13 -8
  191. package/node_modules/@sap/cds/libx/rest/middleware/read.js +3 -3
  192. package/node_modules/@sap/cds/libx/rest/middleware/upsert.js +2 -2
  193. package/node_modules/@sap/cds/package.json +1 -1
  194. package/node_modules/@sap/cds-compiler/CHANGELOG.md +75 -8
  195. package/node_modules/@sap/cds-compiler/bin/cdsc.js +2 -2
  196. package/node_modules/@sap/cds-compiler/doc/IncompatibleChanges_v7.md +46 -19
  197. package/node_modules/@sap/cds-compiler/lib/api/main.js +10 -7
  198. package/node_modules/@sap/cds-compiler/lib/api/options.js +8 -2
  199. package/node_modules/@sap/cds-compiler/lib/base/csnRefs.js +5 -2
  200. package/node_modules/@sap/cds-compiler/lib/base/message-registry.js +4 -0
  201. package/node_modules/@sap/cds-compiler/lib/base/optionProcessor.js +12 -0
  202. package/node_modules/@sap/cds-compiler/lib/checks/enricher.js +1 -0
  203. package/node_modules/@sap/cds-compiler/lib/compiler/assert-consistency.js +8 -7
  204. package/node_modules/@sap/cds-compiler/lib/compiler/checks.js +3 -3
  205. package/node_modules/@sap/cds-compiler/lib/compiler/define.js +301 -75
  206. package/node_modules/@sap/cds-compiler/lib/compiler/dictionaries.js +2 -0
  207. package/node_modules/@sap/cds-compiler/lib/compiler/extend.js +40 -26
  208. package/node_modules/@sap/cds-compiler/lib/compiler/populate.js +45 -60
  209. package/node_modules/@sap/cds-compiler/lib/compiler/propagator.js +16 -7
  210. package/node_modules/@sap/cds-compiler/lib/compiler/resolve.js +48 -18
  211. package/node_modules/@sap/cds-compiler/lib/compiler/shared.js +7 -6
  212. package/node_modules/@sap/cds-compiler/lib/compiler/tweak-assocs.js +62 -50
  213. package/node_modules/@sap/cds-compiler/lib/compiler/utils.js +29 -14
  214. package/node_modules/@sap/cds-compiler/lib/edm/edmAnnoPreprocessor.js +6 -5
  215. package/node_modules/@sap/cds-compiler/lib/gen/CdlGrammar.checksum +1 -1
  216. package/node_modules/@sap/cds-compiler/lib/gen/CdlParser.js +1119 -1109
  217. package/node_modules/@sap/cds-compiler/lib/gen/Dictionary.json +19 -3
  218. package/node_modules/@sap/cds-compiler/lib/json/from-csn.js +10 -16
  219. package/node_modules/@sap/cds-compiler/lib/json/to-csn.js +58 -39
  220. package/node_modules/@sap/cds-compiler/lib/main.d.ts +8 -0
  221. package/node_modules/@sap/cds-compiler/lib/main.js +1 -0
  222. package/node_modules/@sap/cds-compiler/lib/model/csnUtils.js +4 -0
  223. package/node_modules/@sap/cds-compiler/lib/parsers/AstBuildingParser.js +2 -1
  224. package/node_modules/@sap/cds-compiler/lib/parsers/XprTree.js +8 -0
  225. package/node_modules/@sap/cds-compiler/lib/render/toCdl.js +22 -1
  226. package/node_modules/@sap/cds-compiler/lib/render/toSql.js +17 -2
  227. package/node_modules/@sap/cds-compiler/lib/render/utils/common.js +6 -1
  228. package/node_modules/@sap/cds-compiler/lib/render/utils/operators.js +27 -0
  229. package/node_modules/@sap/cds-compiler/lib/render/utils/standardDatabaseFunctions.js +125 -3
  230. package/node_modules/@sap/cds-compiler/lib/tool-lib/revealInternalProperties.js +2 -1
  231. package/node_modules/@sap/cds-compiler/lib/transform/db/applyTransformations.js +15 -0
  232. package/node_modules/@sap/cds-compiler/lib/transform/db/associations.js +10 -2
  233. package/node_modules/@sap/cds-compiler/lib/transform/db/backlinks.js +22 -6
  234. package/node_modules/@sap/cds-compiler/lib/transform/db/constraints.js +62 -1
  235. package/node_modules/@sap/cds-compiler/lib/transform/db/flattening.js +4 -3
  236. package/node_modules/@sap/cds-compiler/lib/transform/effective/annotations.js +40 -0
  237. package/node_modules/@sap/cds-compiler/lib/transform/effective/flattening.js +17 -1
  238. package/node_modules/@sap/cds-compiler/lib/transform/effective/main.js +20 -0
  239. package/node_modules/@sap/cds-compiler/lib/transform/fioriTreeViews.js +12 -1
  240. package/node_modules/@sap/cds-compiler/lib/transform/forRelationalDB.js +7 -2
  241. package/node_modules/@sap/cds-compiler/lib/transform/odata/adaptAnnotationRefs.js +11 -5
  242. package/node_modules/@sap/cds-compiler/lib/transform/translateAssocsToJoins.js +48 -10
  243. package/node_modules/@sap/cds-compiler/package.json +1 -1
  244. package/node_modules/@sap/cds-mtxs/CHANGELOG.md +49 -0
  245. package/node_modules/@sap/cds-mtxs/bin/cds-mtx-migrate.js +26 -5
  246. package/node_modules/@sap/cds-mtxs/bin/cds-mtx.js +37 -16
  247. package/node_modules/@sap/cds-mtxs/lib/utils.js +23 -7
  248. package/node_modules/@sap/cds-mtxs/package.json +1 -1
  249. package/node_modules/@sap/cds-mtxs/srv/cf/abstract-provisioning-service.js +63 -8
  250. package/node_modules/@sap/cds-mtxs/srv/cf/saas-provisioning-service.js +1 -1
  251. package/node_modules/@sap/cds-mtxs/srv/cf/sms-provisioning-service.js +23 -3
  252. package/node_modules/@sap/cds-mtxs/srv/extensibility/compat/update.js +15 -0
  253. package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/annotations.js +2 -2
  254. package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/config.js +2 -0
  255. package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/index.js +2 -0
  256. package/node_modules/@sap/cds-mtxs/srv/extensibility/linter/protected-namespace.js +45 -0
  257. package/node_modules/@sap/cds-mtxs/srv/extensibility-service.js +30 -16
  258. package/node_modules/@sap/cds-mtxs/srv/jobs-service.js +49 -10
  259. package/node_modules/@sap/cds-mtxs/srv/model-provider.js +28 -4
  260. package/node_modules/@sap/cds-mtxs/srv/plugins/common.js +19 -12
  261. package/node_modules/@sap/cds-mtxs/srv/plugins/hana/ctnr-mgr-base.js +1 -1
  262. package/node_modules/@sap/cds-mtxs/srv/plugins/hana/hdi-ctnr-mgr.js +6 -3
  263. package/node_modules/@sap/cds-mtxs/srv/plugins/hana/serverless.js +271 -0
  264. package/node_modules/@sap/cds-mtxs/srv/plugins/hana.js +7 -10
  265. package/node_modules/@sap/cds-mtxs/srv/worker/job-worker-pool.js +212 -0
  266. package/node_modules/@sap/cds-mtxs/srv/worker/job-worker.js +61 -0
  267. package/node_modules/@sap/xsenv/CHANGELOG.md +7 -0
  268. package/node_modules/@sap/xsenv/package.json +10 -7
  269. package/node_modules/body-parser/node_modules/content-type/README.md +2 -0
  270. package/node_modules/body-parser/node_modules/content-type/dist/index.d.ts +20 -0
  271. package/node_modules/body-parser/node_modules/content-type/dist/index.js +20 -14
  272. package/node_modules/body-parser/node_modules/content-type/dist/index.js.map +1 -1
  273. package/node_modules/body-parser/node_modules/content-type/package.json +1 -1
  274. package/node_modules/hdb/cfg/VERSION +1 -1
  275. package/node_modules/hdb/lib/protocol/ClientInfo.js +16 -3
  276. package/node_modules/hdb/lib/protocol/Connection.js +1 -0
  277. package/node_modules/hdb/package.json +3 -2
  278. package/node_modules/negotiator/lib/accept.js +54 -0
  279. package/node_modules/negotiator/lib/charset.js +10 -34
  280. package/node_modules/negotiator/lib/encoding.js +9 -30
  281. package/node_modules/negotiator/lib/language.js +15 -36
  282. package/node_modules/negotiator/lib/mediaType.js +16 -134
  283. package/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
  284. package/node_modules/negotiator/node_modules/content-type/README.md +71 -0
  285. package/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
  286. package/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
  287. package/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
  288. package/node_modules/negotiator/node_modules/content-type/package.json +52 -0
  289. package/node_modules/negotiator/package.json +16 -10
  290. package/node_modules/qs/.github/THREAT_MODEL.md +3 -3
  291. package/node_modules/qs/CHANGELOG.md +15 -0
  292. package/node_modules/qs/README.md +19 -1
  293. package/node_modules/qs/dist/qs.js +21 -21
  294. package/node_modules/qs/eslint.config.mjs +1 -1
  295. package/node_modules/qs/lib/parse.js +3 -4
  296. package/node_modules/qs/lib/stringify.js +23 -8
  297. package/node_modules/qs/lib/utils.js +10 -3
  298. package/node_modules/qs/package.json +4 -4
  299. package/node_modules/qs/test/parse.js +192 -3
  300. package/node_modules/qs/test/stringify.js +317 -0
  301. package/node_modules/qs/test/utils.js +12 -0
  302. package/node_modules/type-is/node_modules/content-type/README.md +2 -0
  303. package/node_modules/type-is/node_modules/content-type/dist/index.d.ts +20 -0
  304. package/node_modules/type-is/node_modules/content-type/dist/index.js +20 -14
  305. package/node_modules/type-is/node_modules/content-type/dist/index.js.map +1 -1
  306. package/node_modules/type-is/node_modules/content-type/package.json +1 -1
  307. package/node_modules/ws/lib/permessage-deflate.js +3 -1
  308. package/node_modules/ws/package.json +1 -1
  309. package/package.json +2 -2
  310. package/bin/completion/scripts/cds-bash.sh +0 -20
  311. package/bin/completion/scripts/cds-fish.fish +0 -13
  312. package/bin/completion/scripts/cds-ps.ps1 +0 -19
  313. package/bin/completion/scripts/cds-zsh.sh +0 -13
  314. package/lib/deploy/index.js +0 -25
  315. package/lib/import/asyncapi/importAsyncAPI.js +0 -33
  316. package/lib/import/asyncapi/index.js +0 -18
  317. package/lib/import/asyncapi/parserContext.js +0 -454
  318. package/lib/import/asyncapi/utils.js +0 -49
  319. package/node_modules/@sap/cds/libx/_runtime/ucl/queries.js +0 -61
  320. package/node_modules/negotiator/HISTORY.md +0 -114
  321. /package/{lib → bin}/upgrade/context.js +0 -0
  322. /package/{lib → bin}/upgrade/migrations/cds10/migrations.json +0 -0
  323. /package/{lib → bin}/upgrade/preflight/cds-version.js +0 -0
  324. /package/{lib → bin}/upgrade/preflight/project-type.js +0 -0
  325. /package/{lib → bin}/upgrade/steps/analyze/classification.js +0 -0
  326. /package/{lib → bin}/upgrade/steps/analyze/detector.js +0 -0
  327. /package/{lib → bin}/upgrade/steps/analyze/evaluator.js +0 -0
  328. /package/{lib → bin}/upgrade/steps/analyze/reporter.js +0 -0
  329. /package/{lib → bin}/upgrade/steps/analyze/setup.js +0 -0
  330. /package/{lib → bin}/upgrade/steps/analyze/utils/detection.js +0 -0
  331. /package/{lib → bin}/upgrade/steps/analyze/utils/files.js +0 -0
  332. /package/{lib → bin}/upgrade/steps/analyze/utils/grep.js +0 -0
  333. /package/{lib → bin}/upgrade/steps/analyze/utils/yaml.js +0 -0
  334. /package/{lib → bin}/upgrade/util/workspaces.js +0 -0
  335. /package/{lib → bin}/watch/esm-resolve-registration.js +0 -0
  336. /package/{lib → bin}/watch/livereload-server.js +0 -0
  337. /package/{lib → bin}/watch/node-watch.js +0 -0
  338. /package/{lib → bin}/watch/open.js +0 -0
  339. /package/{lib → bin}/watch/watcher.js +0 -0
  340. /package/node_modules/@sap/cds-mtxs/srv/worker/{pool.js → model-worker-pool.js} +0 -0
  341. /package/node_modules/@sap/cds-mtxs/srv/worker/{load.js → model-worker.js} +0 -0
@@ -7,7 +7,7 @@ modules:
7
7
  builder: proxy-unified-runtime-dmz.int.repositories.cloud.sap/builder/noble
8
8
  {{/hasKymaUnifiedRuntime}}
9
9
  {{^hasKymaUnifiedRuntime}}
10
- type: sapse/application-content-deployer-buildpack:1.4.0
10
+ type: sapse/application-content-deployer-buildpack:1.5.0
11
11
  builder: builder-jammy-base
12
12
  {{/hasKymaUnifiedRuntime}}
13
- path: app/html5-deployer
13
+ path: {{& html5DeployerPath}}
@@ -1,7 +1,7 @@
1
1
  const { readProject } = require('../../projectReader')
2
2
  const { merge } = require('../../merge')
3
3
  const { srv4, appDeployer, appFront, xsuaa } = require('../../registries/mta')
4
- const { odataApi } = require('../../registries/xs-app')
4
+ const { srvApi } = require('../../registries/xs-app')
5
5
  const { renderAndCopy } = require('../../template')
6
6
  const { sh:$, path:{join}, fs:{promises:{rename}}, write, read, exists } = require('../../../cds').utils
7
7
 
@@ -50,8 +50,8 @@ module.exports = class extends require('../../plugin') {
50
50
 
51
51
  }
52
52
 
53
- async combine() {
54
- const project = readProject()
53
+ async combine(facet) {
54
+ const project = readProject(facet)
55
55
  const {
56
56
  srv, appPath, apps,
57
57
  hasMta, addMta, hasXsuaa, addSample, addApprouter, addWorkzoneStandard, addXsuaa, addIas,
@@ -61,7 +61,7 @@ module.exports = class extends require('../../plugin') {
61
61
  await Promise.all(apps.map(async ({ app }) => {
62
62
  project.app = app
63
63
  project.hasIndexHtml = exists(join(appPath, app, 'webapp/index.html'))
64
- const additions = [odataApi]
64
+ const additions = [srvApi]
65
65
  await merge(__dirname, 'files/xs-app.json.hbs').into(join(appPath, app, 'xs-app.json'), { with: project, additions })
66
66
  }))
67
67
 
@@ -26,8 +26,8 @@ module.exports = class ApplicationLoggingTemplate extends require('../../plugin'
26
26
  if (isJava) await mvn.add('APPLICATION_LOGGING')
27
27
  }
28
28
 
29
- async combine() {
30
- const project = readProject()
29
+ async combine(facet) {
30
+ const project = readProject(facet)
31
31
  const { addMta, hasMta, hasApprouter, isJava, addMultitenancy, hasMultitenancy, srv } = project
32
32
 
33
33
  if (addMta || (addMultitenancy && hasMta)) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "approuter",
3
3
  "dependencies": {
4
- "@sap/approuter": "^22.0.0"
4
+ "@sap/approuter": "^23.0.0"
5
5
  },
6
6
  "scripts": {
7
7
  "start": "node node_modules/@sap/approuter/approuter.js"
@@ -31,8 +31,8 @@ module.exports = class ApprouterTemplate extends require('../../plugin') {
31
31
  await merge(__dirname, 'files/default-env.json').into(approuterPath + '/default-env.json')
32
32
  }
33
33
 
34
- async combine() {
35
- const project = readProject()
34
+ async combine(facet) {
35
+ const project = readProject(facet)
36
36
  const {
37
37
  isNodejs, isJava,
38
38
  addIas, addXsuaa, hasXsuaa, hasIas, addMultitenancy, hasMTXRoute, hasMTXRouteJava,
@@ -51,6 +51,7 @@ module.exports = class ApprouterTemplate extends require('../../plugin') {
51
51
  if (hasMultitenancy) modules.push(mtxSidecar)
52
52
  const additions = [...modules, ...apis]
53
53
  const relationships = []
54
+ const deletions = []
54
55
  if (hasXsuaa) {
55
56
  relationships.push({
56
57
  insert: [xsuaa, 'name'],
@@ -62,9 +63,13 @@ module.exports = class ApprouterTemplate extends require('../../plugin') {
62
63
  insert: [ias, 'name'],
63
64
  into: [approuter, 'requires', 'name']
64
65
  })
66
+ deletions.push({
67
+ item: { in: [approuter, 'requires'], where: { 'name': 'srv-api' } },
68
+ key: 'properties.name'
69
+ })
65
70
  }
66
71
  await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', {
67
- with: project, additions, relationships,
72
+ with: project, additions, relationships, deletions,
68
73
  })
69
74
  }
70
75
 
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "dependencies": {
3
- "@cap-js/attachments": "^3"
3
+ "@cap-js/attachments": "^4"
4
4
  }
5
5
  }
@@ -25,8 +25,8 @@ module.exports = class AttachmentsTemplate extends require('../../plugin') {
25
25
  if (isJava) await mvn.add('attachments')
26
26
  }
27
27
 
28
- async combine() {
29
- const project = readProject()
28
+ async combine(facet) {
29
+ const project = readProject(facet)
30
30
  const { addMta, addKyma, srv } = project
31
31
 
32
32
  if (addMta) {
@@ -32,8 +32,8 @@ module.exports = class AuditLoggingTemplate extends require('../../plugin') {
32
32
  if (isJava) await mvn.add('audit_logging')
33
33
  }
34
34
 
35
- async combine() {
36
- const project = readProject()
35
+ async combine(facet) {
36
+ const project = readProject(facet)
37
37
  const { addMta, addKyma, addMultitenancy, srv } = project
38
38
  const { plan = 'premium' } = cds.cli.options
39
39
  project.servicePlan = plan
@@ -1,11 +1,11 @@
1
1
  {
2
2
  {{#withTelemetry}}
3
3
  "dependencies": {
4
- "@cap-js/telemetry": ">=0",
4
+ "@cap-js/telemetry": "^2",
5
5
  "@grpc/grpc-js": "^1",
6
- "@opentelemetry/exporter-trace-otlp-grpc": "<0.200",
7
- "@opentelemetry/exporter-metrics-otlp-grpc": "<0.200",
8
- "@opentelemetry/host-metrics": "<0.200"
6
+ "@opentelemetry/exporter-trace-otlp-grpc": ">=0.200",
7
+ "@opentelemetry/exporter-metrics-otlp-grpc": ">=0.200",
8
+ "@opentelemetry/instrumentation-host-metrics": ">=0.2"
9
9
  },
10
10
  {{/withTelemetry}}
11
11
  "cds": {
@@ -38,8 +38,8 @@ module.exports = class extends require('../../plugin') {
38
38
  }
39
39
  }
40
40
 
41
- async combine() {
42
- const project = readProject()
41
+ async combine(facet) {
42
+ const project = readProject(facet)
43
43
  const { hasMta, addMta, addKyma, addApprouter, isJava, addMultitenancy, srv } = project
44
44
 
45
45
  if (addMta || (addMultitenancy || addApprouter) && hasMta) {
@@ -16,26 +16,33 @@ const SUPPORTED_SHELLS = [...SUPPORTED_LINUX_SHELLS, ...SUPPORTED_WINDOWS_SHELLS
16
16
  module.exports = new class CompletionSetup {
17
17
  supportedShells = SUPPORTED_SHELLS
18
18
 
19
- async setup(addEntry, options) {
20
- const shell = options.shell
19
+ resolveShell(options) {
20
+ return options?.shell
21
21
  || (process.env.MSYSTEM?.includes('MINGW64') ? 'gitbash' : process.env.SHELL?.match(/(bash|fish|zsh)/)?.[0])
22
22
  || (process.env.PSModulePath ? 'ps' : null);
23
+ }
24
+
25
+ async setup(addEntry, options) {
26
+ const shell = this.resolveShell(options);
23
27
 
24
28
  switch (shell) {
25
29
  case 'bash':
26
30
  case 'fish':
27
31
  case 'gitbash':
28
32
  case 'zsh':
29
- return this.setupLinuxLike(addEntry, shell);
33
+ await this.setupLinuxLike(addEntry, shell);
34
+ return shell;
30
35
  case 'ps':
31
- return this.setupPs(addEntry);
36
+ await this.setupPs(addEntry);
37
+ return shell;
32
38
  case null:
33
39
  try {
34
40
  const shellType = this.determineShellLinux();
35
41
  if (!shellType) {
36
42
  throw `Unsupported shell.\nSupported shells are: ${SUPPORTED_SHELLS.join(', ')}.`;
37
43
  }
38
- return this.setupLinuxLike(addEntry, shellType);
44
+ await this.setupLinuxLike(addEntry, shellType);
45
+ return shellType;
39
46
  } catch (err) {
40
47
  throw `Error during completion setup: ${err}`;
41
48
  }
@@ -95,7 +102,7 @@ module.exports = new class CompletionSetup {
95
102
 
96
103
  profileFile = path.join(os.homedir(), profileFile);
97
104
 
98
- await this.changeProfile(profileFile, shell, addEntry);
105
+ await this.changeProfile(profileFile, shell, addEntry, this.resolveScriptPath(shell));
99
106
 
100
107
  const relProfilePath = path.relative(os.homedir(), profileFile);
101
108
  console.log(`\nSource your shell using\n${bold(`source ~/${relProfilePath}`)}\nto activate the changes.\n`);
@@ -113,55 +120,45 @@ module.exports = new class CompletionSetup {
113
120
  throw err;
114
121
  }
115
122
 
116
- await this.changeProfile(profilePath, 'ps', addEntry);
123
+ await this.changeProfile(profilePath, 'ps', addEntry, this.resolveScriptPath('ps'));
117
124
  console.log(`\nSource your shell using\n${bold('. $PROFILE')}\nto activate the changes.`);
118
125
  }
119
126
 
120
- getScript(shell) {
127
+ // Returns the absolute path to the pre-generated cache file bundled with this package.
128
+ resolveScriptPath(shell) {
129
+ const cacheFiles = { bash: 'cds.sh', gitbash: 'cds.sh', fish: 'cds.fish', ps: 'cds.ps1', zsh: 'cds.zsh' };
130
+ return path.join(__dirname, '../../../../bin/completion/scripts', cacheFiles[shell]);
131
+ }
132
+
133
+ // Generates the snippet written to the user's shell profile. The script path is hardcoded at
134
+ // install time to avoid spawning a subprocess on every shell startup.
135
+ getScript(shell, scriptPath) {
121
136
  switch (shell) {
122
137
  case 'bash':
123
138
  case 'gitbash':
124
- return `
125
- CDS_PROFILE=$(cds completion --shell ${shell} --profile 2> /dev/null) || CDS_PROFILE=""
126
- if [ -r "$CDS_PROFILE" ]; then
127
- . "$CDS_PROFILE"
128
- fi
129
- `
139
+ return `# cds shell completion script (path resolved at install time)
140
+ _p="${scriptPath}" && [ -r "$_p" ] && . "$_p"`
130
141
  case 'fish':
131
- return `
142
+ return `# cds shell completion script (path resolved at install time)
132
143
  if type "cds" >/dev/null 2>&1
133
- set -l CDS_PROFILE (cds completion --shell fish --profile 2> /dev/null)
134
- if test -e "$CDS_PROFILE"
135
- source "$CDS_PROFILE"
136
- end
137
- end
138
- `
144
+ set -l _p "${scriptPath}"
145
+ test -r "$_p" && source "$_p"
146
+ end`
139
147
  case 'ps':
140
- return `
141
- try {
142
- $CDS_PROFILE = cds completion --shell ps --profile 2> $null
143
- if (Test-Path -Path "$CDS_PROFILE" -PathType Leaf) {
144
- . "$CDS_PROFILE"
145
- }
146
- } catch {}
147
- `
148
-
148
+ return `# cds shell completion script (path resolved at install time)
149
+ $_p = "${scriptPath}"; if (Test-Path -Path "$_p" -PathType Leaf) { . "$_p" }`
149
150
  case 'zsh':
150
- return `
151
- # compinit can be removed here if loaded elsewhere in the script
151
+ return `# compinit can be removed here if loaded elsewhere in the script
152
152
  autoload -Uz compinit && compinit
153
153
 
154
- CDS_PROFILE=$(cds completion --shell zsh --profile 2> /dev/null) || CDS_PROFILE=""
155
- if [ -r "$CDS_PROFILE" ]; then
156
- . "$CDS_PROFILE"
157
- fi
158
- `
154
+ # cds shell completion script (path resolved at install time)
155
+ _p="${scriptPath}" && [ -r "$_p" ] && \\. "$_p"`
159
156
  default:
160
157
  throw `Unsupported shell: ${shell}`;
161
158
  }
162
159
  }
163
160
 
164
- async changeProfile(profileFile, shell, addEntry) {
161
+ async changeProfile(profileFile, shell, addEntry, scriptPath) {
165
162
  let profile = '';
166
163
  let profileFileExists = true;
167
164
  try {
@@ -181,7 +178,7 @@ fi
181
178
  profile = profile + `
182
179
 
183
180
  # cds completion start
184
- ${this.getScript(shell)}
181
+ ${this.getScript(shell, scriptPath)}
185
182
  # cds completion end`;
186
183
  changed = true;
187
184
  }
@@ -18,8 +18,8 @@ module.exports = class ConnectivityTemplate extends require('../../plugin') {
18
18
  await merge(__dirname, 'files', 'package.json.hbs').into(configFile, { with: project })
19
19
  }
20
20
 
21
- async combine() {
22
- const project = readProject()
21
+ async combine(facet) {
22
+ const project = readProject(facet)
23
23
  const { srv, addKyma, addMta } = project
24
24
 
25
25
  if (addMta) {
@@ -4,10 +4,10 @@ const { elementsSorter } = require('./as-json')
4
4
  * Serializes the given JSON data to CSV format
5
5
  * @param {object} data JSON data to serialize
6
6
  * @param {object} csn the model
7
- * @param {{ headerOnly: boolean, separator: boolean }} options
7
+ * @param {{ headerOnly: boolean, keysOnly: boolean, separator: boolean }} options
8
8
  * @returns {Promise<string>} CSV string
9
9
  */
10
- module.exports = async (data={}, csn, { headerOnly=false, separator=',' }={}) => {
10
+ module.exports = async (data={}, csn, { headerOnly=false, keysOnly=false, separator=',' }={}) => {
11
11
 
12
12
  // We might need multiple passes if data for associations/compositions is created on the fly.
13
13
  // So loop as long as there is some non-string data left.
@@ -32,6 +32,15 @@ module.exports = async (data={}, csn, { headerOnly=false, separator=',' }={}) =>
32
32
  if (data[t] === undefined) {
33
33
  data[t] = value // Add object for a new entity. Next pass will process it.
34
34
  }
35
+ // managed to-one composition: emit the foreign-key column(s) on the parent, like an association
36
+ if (!element.on && element.keys) {
37
+ const targetRecord = Array.isArray(value) ? value[0] : value
38
+ const fk = {}
39
+ if (targetRecord) for (const k of element.keys) fk[k.ref[k.ref.length - 1]] = targetRecord[k.ref[k.ref.length - 1]]
40
+ const {headers: fkHeaders, records: fkRecords} = resolveStruct(fk, name)
41
+ if (i === 0) header.push(...fkHeaders)
42
+ record.push(...(keysOnly && !element.key ? fkRecords.map(() => '') : fkRecords))
43
+ }
35
44
  }
36
45
  else if (element._type === 'cds.Association') {
37
46
  if (element.on) {
@@ -42,18 +51,19 @@ module.exports = async (data={}, csn, { headerOnly=false, separator=',' }={}) =>
42
51
  if (i === 0) {
43
52
  header.push(...fkHeaders)
44
53
  }
45
- record.push(...fkRecords)
54
+ // foreign keys of key associations are part of the primary key and must be kept
55
+ record.push(...(keysOnly && !element.key ? fkRecords.map(() => '') : fkRecords))
46
56
  }
47
57
  else if (element.is_struct && element.type !== 'cds.Map') {
48
58
  const {headers: structHeaders, records: structRecords} = resolveStruct(value, name);
49
59
  if (i === 0) {
50
60
  header.push(...structHeaders)
51
61
  }
52
- record.push(...structRecords)
62
+ record.push(...(keysOnly && !element.key ? structRecords.map(() => '') : structRecords))
53
63
  }
54
64
  else {
55
65
  if (i === 0) header.push(name)
56
- record.push(processValue(value))
66
+ record.push(keysOnly && !element.key ? '' : processValue(value))
57
67
  }
58
68
  }
59
69
  records.push(record.join(separator))
@@ -4,6 +4,8 @@ const cds = require('../../../cds')
4
4
 
5
5
  let counter = Math.round(Math.random() * 10000) // Default random seed // Non-deterministic behaviour
6
6
  const MAX_ASSOC_DEPTH = 5
7
+ let compositionRecords = 2 // number of records generated per to-many composition
8
+ let requestedRecords = 1 // number of records requested per entity, used to spread association keys
7
9
 
8
10
  /**
9
11
  * Creates JSON test data for the given entities
@@ -16,6 +18,8 @@ const MAX_ASSOC_DEPTH = 5
16
18
  */
17
19
  const asJson = module.exports = async (data, csn, numRecords=1, options={}) => {
18
20
  let { referenceData, contentType, artifact='entity' } = options
21
+ compositionRecords = Number(options.compositionRecords) > 0 ? Number(options.compositionRecords) : 2
22
+ requestedRecords = Number(numRecords) > 0 ? Number(numRecords) : 1
19
23
 
20
24
  // if no data is given, use all data
21
25
  if (!data) {
@@ -120,8 +124,11 @@ function dataForField(element, artifact, csn, record, data, parent, depth, refer
120
124
 
121
125
  if (element.items?.elements) // `array of` / `many` with complex type
122
126
  return [ fillRecord({}, record['$id'], element.items, csn, data, null, depth+1, referenceData) ]
123
- else if (element.items?.type) // `array of` / `many` with simple type
124
- return [ dataForField(element.items, artifact, csn, record, data, null, depth+1, referenceData) ]
127
+ else if (element.items?.type) { // `array of` / `many` with simple type
128
+ // `@assert.range` sits on the outer element, not on items, so carry it over
129
+ const items = element['@assert.range'] ? { ...element.items, '@assert.range': element['@assert.range'] } : element.items
130
+ return [ dataForField(items, artifact, csn, record, data, null, depth+1, referenceData) ]
131
+ }
125
132
 
126
133
  if (element.is_struct) {
127
134
  let struct = {}
@@ -134,11 +141,18 @@ function dataForField(element, artifact, csn, record, data, parent, depth, refer
134
141
 
135
142
  const type = element._type || element.type
136
143
  switch (type) {
137
- case 'cds.Time':
138
- return '22:59:53'
144
+ case 'cds.Time': {
145
+ // vary time across records instead of a fixed constant
146
+ const pad = n => String(n).padStart(2, '0')
147
+ const hh = pad(randomIntInRange({ from: 0, to: 23 }))
148
+ const mm = pad(randomIntInRange({ from: 0, to: 59 }))
149
+ const ss = pad(randomIntInRange({ from: 0, to: 59 }))
150
+ return `${hh}:${mm}:${ss}`
151
+ }
139
152
 
140
153
  case 'cds.Timestamp': {
141
154
  const range = {from: new Date('2000-01-01'), to: new Date('2024-01-01')}
155
+ annotatedDateRange(element, range) // honor @assert.range bounds
142
156
  return randomDateInRange(range).toISOString()
143
157
  }
144
158
  case 'cds.Date': {
@@ -228,18 +242,23 @@ function dataForField(element, artifact, csn, record, data, parent, depth, refer
228
242
  case 'cds.hana.REAL':
229
243
  case 'cds.Decimal': { // Decimal(9,5), Decimal(9), Decimal(precision:5), Decimal(2,2)
230
244
  const scale = element.scale ?? (element.precision ? 0 : 2) // scale is 0 if only precision is given
231
- //10^x would allow one digit more than allowed, that's why -1
232
- const prec = element.precision ? Math.min(Math.pow(10, element.precision - scale) - 1, Number.MAX_SAFE_INTEGER) : 100
245
+ // largest value representable with the given precision/scale, e.g. Decimal(2,2) -> 0.99, Decimal(4,2) -> 99.99
246
+ const prec = element.precision ? Math.min(Math.pow(10, element.precision - scale) - Math.pow(10, -scale), Number.MAX_SAFE_INTEGER) : 100
233
247
  const range = { from: 0, to: prec }
234
248
  annotatedIntRange(element, range)
235
249
  if (scale === 0) return randomIntInRange(range)
236
- const intPart = randomIntInRange(range)
237
- const frac = Math.floor(randomUnit() * Math.pow(10, scale))
238
- return +(intPart + frac / Math.pow(10, scale)).toFixed(scale)
250
+ const factor = Math.pow(10, scale)
251
+ range.from = Math.ceil(range.from * factor) / factor
252
+ range.to = Math.floor(range.to * factor) / factor
253
+ // draw uniformly across the range and round to scale, so annotated bounds are never exceeded
254
+ const val = +randomDecimalInRange(range).toFixed(scale)
255
+ return +Math.min(Math.max(val, range.from), range.to).toFixed(scale)
239
256
  }
240
257
  case 'cds.Double': {
241
258
  const range = {from: 0, to: 100}
242
- return randomDecimalInRange(range)
259
+ annotatedIntRange(element, range) // honor @assert.range bounds
260
+ const val = randomDecimalInRange(range)
261
+ return Math.min(Math.max(val, range.from), range.to)
243
262
  }
244
263
  case 'cds.Composition': {
245
264
  const target = csn.definitions[element.target]
@@ -248,7 +267,7 @@ function dataForField(element, artifact, csn, record, data, parent, depth, refer
248
267
  let targetRecords = [] // in findKeysInOnCondition target records are linked to the entity, we don't want to overwrite any existing data
249
268
  if (data[target.name]) data[target.name].deleteReference = true // for json composites references are nested and will be deleted when writing to file
250
269
 
251
- const targetRecordCount = element.is2many ? 2 : 1 // TODO: make this configurable
270
+ const targetRecordCount = element.is2many ? compositionRecords : 1
252
271
  const targetRecordID = randomNumber()
253
272
  for (let i = 0; i < targetRecordCount; i++) {
254
273
  const newRecord = fillRecord({}, targetRecordID+i, target, csn, data, artifact, depth+1, referenceData)
@@ -291,31 +310,52 @@ function dataForField(element, artifact, csn, record, data, parent, depth, refer
291
310
  return
292
311
 
293
312
  if (targetRecords.length > 0) { // use record generated by following association
294
- targetRecord = targetRecords.at(-1) // make sure the localized record references the newly generated texts
313
+ // spread across fully-built targets so composite association keys vary; fall back to the last (still-building) record for circular refs
314
+ const targetKeys = Object.keys(target.elements).filter(k => target.elements[k].key)
315
+ const ready = targetRecords.filter(r => targetKeys.every(k => r[k] !== undefined))
316
+ targetRecord = ready.length ? ready[pickIndex(record['$id'], element.name, ready.length)] : targetRecords.at(-1)
295
317
  } else {
296
- targetRecord = fillRecord({}, randomNumber(), target, csn, data, artifact, depth+1, referenceData)
297
- if (targetRecord) {
298
- targetRecords.push(targetRecord)
299
- uniqRecords(targetRecords, target)
318
+ // a key association needs distinct targets across rows, else the composite key collapses to one row;
319
+ // seed a pool up to the requested record count so later rows can spread across it
320
+ let poolSize = element.key && target.name !== artifact.name ? requestedRecords : 1
321
+ // a self-referential link (2+ key associations to the same target) needs at least 2 targets to pair distinct rows
322
+ const keyAssocsToTarget = Object.values(artifact.elements).filter(e => e.key && e.target === element.target).length
323
+ if (keyAssocsToTarget > 1) poolSize = Math.max(poolSize, keyAssocsToTarget)
324
+ for (let p = 0; p < poolSize; p++) {
325
+ const rec = fillRecord({}, randomNumber(), target, csn, data, artifact, depth+1, referenceData)
326
+ if (rec) targetRecords.push(rec)
300
327
  }
328
+ uniqRecords(targetRecords, target)
329
+ targetRecord = targetRecords.length ? targetRecords[pickIndex(record['$id'], element.name, targetRecords.length)] : undefined
301
330
  if (parent && (parent.name != artifact.name)) data[target.name].keepReference = true
302
331
  }
303
332
  }
304
333
 
305
- // TODO: handle foreign keys correctly
334
+ // build the foreign key from all of the target's keys, recursing into keys that are themselves associations
306
335
  const assoc = {}
307
336
  Object.keys(target.elements)
308
337
  .filter(keyName => target.elements[keyName].key)
309
- .forEach(keyName => assoc[keyName] = targetRecord[keyName])
338
+ .forEach(keyName => {
339
+ const keyEl = target.elements[keyName]
340
+ assoc[keyName] = keyEl.target
341
+ ? dataForField(keyEl, target, csn, targetRecord, data, artifact, depth+1, referenceData)
342
+ : targetRecord[keyName]
343
+ })
310
344
  return assoc
311
345
  }
312
346
 
313
347
  case 'cds.hana.BINARY':
314
348
  case 'cds.Binary':
315
- case 'cds.LargeBinary': // TODO add binary
316
- return
317
- case 'cds.Vector': // TODO add Vector
318
- return
349
+ case 'cds.LargeBinary': {
350
+ const bytes = element.length ? Math.min(element.length, 16) : 8
351
+ const buf = Buffer.alloc(bytes)
352
+ for (let i = 0; i < bytes; i++) buf[i] = randomNumber() % 256
353
+ return buf.toString('base64')
354
+ }
355
+ case 'cds.Vector': {
356
+ const dim = element.dimension ?? element.length ?? 3
357
+ return JSON.stringify(Array.from({ length: dim }, () => +randomUnit().toFixed(6)))
358
+ }
319
359
  default: {
320
360
  console.error(`Unknown type ${element._type} (${element.type}) in '${artifact.name}:${element.name}'`)
321
361
  return
@@ -391,7 +431,8 @@ function findKeysInOnCondition(assocElement, target) {
391
431
  targetKey = ref[1]
392
432
  }
393
433
  else if (ref[0] === '$self') {
394
- ownKey = Object.keys(assocElement.parent.keys)[0] // TODO composite keys
434
+ // structured foreign key: carry over the parent's full (possibly composite) key set
435
+ ownKey = Object.keys(assocElement.parent.keys || {})[0]
395
436
  ownKeyStructured = true
396
437
  }
397
438
  else if (assocElement.parent.elements[ref[0]]) {
@@ -407,7 +448,8 @@ function uniqRecords(records, artifact) {
407
448
  if (keys.length === 0) keys.push('$id') // no keys (e.g. in actions) -> use record's $id
408
449
  const keySet = new Set()
409
450
  for (let i = 0; i < records.length; i++) {
410
- const key = keys.map(k => records[i][k]).join('|')
451
+ // JSON-serialize so association-typed keys (object foreign keys) compare by value, not '[object Object]'
452
+ const key = keys.map(k => JSON.stringify(records[i][k])).join('|')
411
453
  if (keySet.has(key)) {
412
454
  // console.debug('Removing', keys, key, 'from', artifact.name)
413
455
  records.splice(i, 1)
@@ -420,29 +462,60 @@ function uniqRecords(records, artifact) {
420
462
 
421
463
  function annotatedDateRange(el, dateRange) {
422
464
  if (el['@assert.range']?.length === 2) {
423
- const dateFrom = new Date(el['@assert.range'][0])
424
- const dateTo = new Date(el['@assert.range'][1])
425
- if (dateRange.from < dateFrom) {
426
- dateRange.from = dateFrom
427
- }
428
- if (dateRange.to > dateTo) {
429
- dateRange.to = dateTo
465
+ let from = dateBound(el['@assert.range'][0], true)
466
+ let to = dateBound(el['@assert.range'][1], false)
467
+ const YEAR = 10 * 365 * 24 * 60 * 60 * 1000 // anchor window for open-ended bounds
468
+ if (from === Infinity || from === -Infinity) from = to instanceof Date ? new Date(to.valueOf() - YEAR) : dateRange.from
469
+ if (to === Infinity || to === -Infinity) to = from instanceof Date ? new Date(from.valueOf() + YEAR) : dateRange.to
470
+ // use annotated bounds directly, so ranges outside the default window don't invert
471
+ if (from instanceof Date && !isNaN(from)) dateRange.from = from
472
+ if (to instanceof Date && !isNaN(to)) dateRange.to = to
473
+ }
474
+ }
475
+
476
+ // resolve one date @assert.range bound: ISO string, open-interval xpr {xpr:[y,'-',m,'-',d]}, or infinity {'=':'_'}
477
+ function dateBound(bound, isLower) {
478
+ if (bound && typeof bound === 'object') {
479
+ if (bound.val === '_' || bound['='] === '_') return isLower ? -Infinity : Infinity
480
+ if (Array.isArray(bound.xpr)) {
481
+ const nums = bound.xpr.filter(p => p && typeof p.val === 'number').map(p => p.val)
482
+ if (nums.length === 3) return new Date(Date.UTC(nums[0], nums[1] - 1, nums[2]))
430
483
  }
484
+ return undefined
431
485
  }
486
+ return new Date(bound)
432
487
  }
433
488
 
434
489
  function annotatedIntRange(el, intRange) {
435
490
  if (el['@assert.range']?.length === 2) {
436
- const rangeFrom = el['@assert.range'][0]
437
- const rangeTo = el['@assert.range'][1]
438
- if (Number.isInteger(rangeFrom) && intRange.from < rangeFrom) {
439
- intRange.from = rangeFrom
440
- }
441
- if (Number.isInteger(rangeTo) && intRange.to > rangeTo) {
442
- intRange.to = rangeTo
443
- }
491
+ let from = rangeBound(el['@assert.range'][0], true)
492
+ let to = rangeBound(el['@assert.range'][1], false)
493
+ // infinity on one side: anchor a finite window to the other bound so the span stays valid
494
+ if (from === Infinity || from === -Infinity) from = to !== undefined && isFinite(to) ? to - 100 : intRange.from
495
+ if (to === Infinity || to === -Infinity) to = from !== undefined && isFinite(from) ? from + 100 : intRange.to
496
+ // use annotated bounds directly, so ranges outside the default [0,100] don't invert
497
+ if (from !== undefined) intRange.from = from
498
+ if (to !== undefined) intRange.to = to
499
+ }
500
+ }
501
+
502
+ // pick a pool index that varies per association name, so multiple associations to the same
503
+ // target on one record (e.g. a self-referential link a<->a) land on different rows
504
+ function pickIndex(id, name, len) {
505
+ let h = 0
506
+ for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) | 0
507
+ return (((id + Math.abs(h)) % len) + len) % len
508
+ }
509
+
510
+ // resolve one @assert.range bound: plain number, open bound {'=':true,val}, or infinity {'=':'_'}
511
+ function rangeBound(bound, isLower) {
512
+ if (typeof bound === 'number') return bound
513
+ if (bound && typeof bound === 'object') {
514
+ if (bound.val === '_' || bound['='] === '_') return isLower ? -Infinity : Infinity
515
+ const n = typeof bound.val === 'string' ? Number(bound.val) : bound.val // decimal open bounds arrive as string literals
516
+ if (typeof n === 'number' && !isNaN(n)) return isLower ? n + 1e-9 : n - 1e-9 // open bound: nudge inward
444
517
  }
445
- //TODO : decimalRange
518
+ return undefined
446
519
  }
447
520
 
448
521
  const langList = ['ar', 'cs', 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja', 'ko', 'ms', 'nl', 'pl', 'pt', 'ro', 'ru', 'sv', 'th', 'tr', 'zh']
@@ -539,9 +612,9 @@ function addSemantics(csn) {
539
612
 
540
613
  function randomDateInRange(dateRange) {
541
614
  const dateFrom = dateRange.from
542
- const returnDate = new Date(dateFrom.getUTCFullYear(), dateFrom.getUTCMonth(), dateFrom.getUTCDate())//needed to avoid overwriting the original dateFrom when generating a random Date
615
+ const returnDate = new Date(Date.UTC(dateFrom.getUTCFullYear(), dateFrom.getUTCMonth(), dateFrom.getUTCDate())) // build in UTC to match the UTC mutation below, avoiding a timezone off-by-one-day
543
616
  const diffTime = dateRange.to.valueOf() - dateRange.from.valueOf();
544
- const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
617
+ const diffDays = Math.max(1, Math.ceil(diffTime / (1000 * 60 * 60 * 24))); // at least 1 day so a single-day range never yields NaN
545
618
  const step = randomNumber() % diffDays
546
619
  returnDate.setUTCDate(returnDate.getUTCDate() + step);
547
620
  return returnDate
@@ -552,6 +625,8 @@ function randomDecimalInRange({ from: min, to: max }) {
552
625
  }
553
626
 
554
627
  function randomIntInRange({ from: min, to: max }) {
628
+ min = Math.ceil(min) // round fractional bounds inward so the result stays an integer within range
629
+ max = Math.floor(max)
555
630
  const span = Math.max(0, max - min + 1)
556
631
  return min + Math.floor(randomUnit() * span)
557
632
  }