@sap/cds-dk 10.0.7 → 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 (302) hide show
  1. package/CHANGELOG.md +94 -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/notifications/index.js +2 -2
  117. package/lib/init/template/portal/files/mta.yaml.hbs +4 -0
  118. package/lib/init/template/portal/index.js +13 -8
  119. package/lib/init/template/postgres/index.js +2 -2
  120. package/lib/init/template/redis/index.js +2 -2
  121. package/lib/init/template/sample/files/db/currencies.cds +2 -0
  122. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Authors.csv +1 -1
  123. package/lib/init/template/tiny-sample/files/nodejs/db/data/CatalogService.Books.csv +2 -2
  124. package/lib/init/template/workzone-standard/index.js +5 -5
  125. package/lib/init/template/xsuaa/index.js +20 -2
  126. package/lib/lint/eslintHelp.json +1 -1
  127. package/lib/mtx/auth_manager.js +103 -37
  128. package/lib/mtx/params.js +3 -2
  129. package/lib/mtx/settings_manager.js +45 -28
  130. package/lib/mtx/tenant_action.js +0 -1
  131. package/lib/mtx/util/credentials.js +62 -0
  132. package/node_modules/@sap/cds/CHANGELOG.md +57 -0
  133. package/node_modules/@sap/cds/app/index.js +5 -4
  134. package/node_modules/@sap/cds/eslint.config.mjs +1 -0
  135. package/node_modules/@sap/cds/lib/compile/minify.js +14 -10
  136. package/node_modules/@sap/cds/lib/env/cds-env.js +1 -1
  137. package/node_modules/@sap/cds/lib/env/cds-requires.js +22 -11
  138. package/node_modules/@sap/cds/lib/env/defaults.js +4 -1
  139. package/node_modules/@sap/cds/lib/env/schemas/cds-rc.js +5 -2
  140. package/node_modules/@sap/cds/lib/plugins.js +9 -5
  141. package/node_modules/@sap/cds/lib/req/spawn.js +1 -1
  142. package/node_modules/@sap/cds/lib/srv/srv-dispatch.js +1 -1
  143. package/node_modules/@sap/cds/lib/srv/srv-models.js +1 -1
  144. package/node_modules/@sap/cds/lib/utils/cds-utils.js +2 -1
  145. package/node_modules/@sap/cds/libx/_runtime/common/generic/assert.js +2 -3
  146. package/node_modules/@sap/cds/libx/_runtime/common/generic/auth/restrict.js +8 -1
  147. package/node_modules/@sap/cds/libx/_runtime/common/generic/crud.js +18 -8
  148. package/node_modules/@sap/cds/libx/_runtime/fiori/lean-draft.js +25 -17
  149. package/node_modules/@sap/cds/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +9 -3
  150. package/node_modules/@sap/cds/libx/_runtime/remote/Service.js +4 -19
  151. package/node_modules/@sap/cds/libx/_runtime/remote/utils/LruCache.js +42 -0
  152. package/node_modules/@sap/cds/libx/_runtime/remote/utils/client.js +14 -4
  153. package/node_modules/@sap/cds/libx/_runtime/remote/utils/cloudSdkProvider.js +2 -9
  154. package/node_modules/@sap/cds/libx/_runtime/remote/utils/destinationClient.js +150 -0
  155. package/node_modules/@sap/cds/libx/_runtime/remote/utils/error.js +13 -0
  156. package/node_modules/@sap/cds/libx/_runtime/remote/utils/fetchClient.js +26 -18
  157. package/node_modules/@sap/cds/libx/_runtime/remote/utils/query.js +34 -23
  158. package/node_modules/@sap/cds/libx/_runtime/remote/utils/token.js +82 -0
  159. package/node_modules/@sap/cds/libx/_runtime/ucl/Service.js +36 -189
  160. package/node_modules/@sap/cds/libx/common/utils/streaming.js +16 -16
  161. package/node_modules/@sap/cds/libx/http/location.js +3 -3
  162. package/node_modules/@sap/cds/libx/odata/middleware/batch.js +16 -10
  163. package/node_modules/@sap/cds/libx/odata/middleware/metadata.js +9 -6
  164. package/node_modules/@sap/cds/libx/odata/middleware/service-document.js +1 -1
  165. package/node_modules/@sap/cds/libx/odata/parse/afterburner.js +19 -3
  166. package/node_modules/@sap/cds/libx/odata/parse/cqn2odata.js +83 -86
  167. package/node_modules/@sap/cds/libx/queue/SchedulingService.js +17 -2
  168. package/node_modules/@sap/cds/libx/queue/processing.js +1 -3
  169. package/node_modules/@sap/cds/libx/rest/RestAdapter.js +3 -7
  170. package/node_modules/@sap/cds/libx/rest/middleware/delete.js +3 -3
  171. package/node_modules/@sap/cds/libx/rest/middleware/parse.js +13 -8
  172. package/node_modules/@sap/cds/libx/rest/middleware/read.js +3 -3
  173. package/node_modules/@sap/cds/libx/rest/middleware/upsert.js +2 -2
  174. package/node_modules/@sap/cds/package.json +1 -1
  175. package/node_modules/@sap/cds-compiler/CHANGELOG.md +60 -8
  176. package/node_modules/@sap/cds-compiler/bin/cdsc.js +2 -2
  177. package/node_modules/@sap/cds-compiler/doc/IncompatibleChanges_v7.md +46 -19
  178. package/node_modules/@sap/cds-compiler/lib/api/main.js +10 -7
  179. package/node_modules/@sap/cds-compiler/lib/api/options.js +8 -2
  180. package/node_modules/@sap/cds-compiler/lib/base/csnRefs.js +5 -2
  181. package/node_modules/@sap/cds-compiler/lib/base/message-registry.js +4 -0
  182. package/node_modules/@sap/cds-compiler/lib/base/optionProcessor.js +12 -0
  183. package/node_modules/@sap/cds-compiler/lib/checks/enricher.js +1 -0
  184. package/node_modules/@sap/cds-compiler/lib/compiler/assert-consistency.js +8 -7
  185. package/node_modules/@sap/cds-compiler/lib/compiler/checks.js +3 -3
  186. package/node_modules/@sap/cds-compiler/lib/compiler/define.js +301 -75
  187. package/node_modules/@sap/cds-compiler/lib/compiler/dictionaries.js +2 -0
  188. package/node_modules/@sap/cds-compiler/lib/compiler/extend.js +40 -26
  189. package/node_modules/@sap/cds-compiler/lib/compiler/populate.js +45 -60
  190. package/node_modules/@sap/cds-compiler/lib/compiler/propagator.js +16 -7
  191. package/node_modules/@sap/cds-compiler/lib/compiler/resolve.js +41 -15
  192. package/node_modules/@sap/cds-compiler/lib/compiler/shared.js +7 -6
  193. package/node_modules/@sap/cds-compiler/lib/compiler/tweak-assocs.js +62 -50
  194. package/node_modules/@sap/cds-compiler/lib/compiler/utils.js +29 -14
  195. package/node_modules/@sap/cds-compiler/lib/edm/edmAnnoPreprocessor.js +6 -5
  196. package/node_modules/@sap/cds-compiler/lib/gen/CdlGrammar.checksum +1 -1
  197. package/node_modules/@sap/cds-compiler/lib/gen/CdlParser.js +1119 -1109
  198. package/node_modules/@sap/cds-compiler/lib/gen/Dictionary.json +19 -3
  199. package/node_modules/@sap/cds-compiler/lib/json/from-csn.js +10 -16
  200. package/node_modules/@sap/cds-compiler/lib/json/to-csn.js +58 -39
  201. package/node_modules/@sap/cds-compiler/lib/main.d.ts +8 -0
  202. package/node_modules/@sap/cds-compiler/lib/main.js +1 -0
  203. package/node_modules/@sap/cds-compiler/lib/model/csnUtils.js +2 -0
  204. package/node_modules/@sap/cds-compiler/lib/parsers/AstBuildingParser.js +2 -1
  205. package/node_modules/@sap/cds-compiler/lib/parsers/XprTree.js +8 -0
  206. package/node_modules/@sap/cds-compiler/lib/render/toCdl.js +22 -1
  207. package/node_modules/@sap/cds-compiler/lib/render/toSql.js +17 -2
  208. package/node_modules/@sap/cds-compiler/lib/render/utils/common.js +6 -1
  209. package/node_modules/@sap/cds-compiler/lib/render/utils/operators.js +27 -0
  210. package/node_modules/@sap/cds-compiler/lib/render/utils/standardDatabaseFunctions.js +125 -3
  211. package/node_modules/@sap/cds-compiler/lib/tool-lib/revealInternalProperties.js +2 -1
  212. package/node_modules/@sap/cds-compiler/lib/transform/db/applyTransformations.js +3 -0
  213. package/node_modules/@sap/cds-compiler/lib/transform/db/associations.js +8 -2
  214. package/node_modules/@sap/cds-compiler/lib/transform/db/backlinks.js +22 -6
  215. package/node_modules/@sap/cds-compiler/lib/transform/db/constraints.js +62 -1
  216. package/node_modules/@sap/cds-compiler/lib/transform/effective/annotations.js +40 -0
  217. package/node_modules/@sap/cds-compiler/lib/transform/effective/main.js +1 -0
  218. package/node_modules/@sap/cds-compiler/lib/transform/forRelationalDB.js +6 -1
  219. package/node_modules/@sap/cds-compiler/lib/transform/translateAssocsToJoins.js +19 -8
  220. package/node_modules/@sap/cds-compiler/package.json +1 -1
  221. package/node_modules/@sap/cds-mtxs/CHANGELOG.md +15 -0
  222. package/node_modules/@sap/cds-mtxs/bin/cds-mtx.js +34 -16
  223. package/node_modules/@sap/cds-mtxs/lib/utils.js +19 -4
  224. package/node_modules/@sap/cds-mtxs/package.json +1 -1
  225. package/node_modules/@sap/cds-mtxs/srv/cf/abstract-provisioning-service.js +1 -1
  226. package/node_modules/@sap/cds-mtxs/srv/extensibility-service.js +22 -13
  227. package/node_modules/@sap/cds-mtxs/srv/model-provider.js +17 -2
  228. package/node_modules/@sap/cds-mtxs/srv/plugins/hana/ctnr-mgr-base.js +1 -1
  229. package/node_modules/@sap/cds-mtxs/srv/plugins/hana.js +4 -8
  230. package/node_modules/@sap/xsenv/CHANGELOG.md +7 -0
  231. package/node_modules/@sap/xsenv/package.json +10 -7
  232. package/node_modules/body-parser/node_modules/content-type/README.md +2 -0
  233. package/node_modules/body-parser/node_modules/content-type/dist/index.d.ts +20 -0
  234. package/node_modules/body-parser/node_modules/content-type/dist/index.js +20 -14
  235. package/node_modules/body-parser/node_modules/content-type/dist/index.js.map +1 -1
  236. package/node_modules/body-parser/node_modules/content-type/package.json +1 -1
  237. package/node_modules/hdb/cfg/VERSION +1 -1
  238. package/node_modules/hdb/lib/protocol/ClientInfo.js +16 -3
  239. package/node_modules/hdb/lib/protocol/Connection.js +1 -0
  240. package/node_modules/hdb/package.json +3 -2
  241. package/node_modules/negotiator/lib/accept.js +54 -0
  242. package/node_modules/negotiator/lib/charset.js +10 -34
  243. package/node_modules/negotiator/lib/encoding.js +9 -30
  244. package/node_modules/negotiator/lib/language.js +15 -36
  245. package/node_modules/negotiator/lib/mediaType.js +16 -134
  246. package/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
  247. package/node_modules/negotiator/node_modules/content-type/README.md +71 -0
  248. package/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
  249. package/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
  250. package/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
  251. package/node_modules/negotiator/node_modules/content-type/package.json +52 -0
  252. package/node_modules/negotiator/package.json +16 -10
  253. package/node_modules/qs/.github/THREAT_MODEL.md +3 -3
  254. package/node_modules/qs/CHANGELOG.md +15 -0
  255. package/node_modules/qs/README.md +19 -1
  256. package/node_modules/qs/dist/qs.js +21 -21
  257. package/node_modules/qs/eslint.config.mjs +1 -1
  258. package/node_modules/qs/lib/parse.js +3 -4
  259. package/node_modules/qs/lib/stringify.js +23 -8
  260. package/node_modules/qs/lib/utils.js +10 -3
  261. package/node_modules/qs/package.json +4 -4
  262. package/node_modules/qs/test/parse.js +192 -3
  263. package/node_modules/qs/test/stringify.js +317 -0
  264. package/node_modules/qs/test/utils.js +12 -0
  265. package/node_modules/type-is/node_modules/content-type/README.md +2 -0
  266. package/node_modules/type-is/node_modules/content-type/dist/index.d.ts +20 -0
  267. package/node_modules/type-is/node_modules/content-type/dist/index.js +20 -14
  268. package/node_modules/type-is/node_modules/content-type/dist/index.js.map +1 -1
  269. package/node_modules/type-is/node_modules/content-type/package.json +1 -1
  270. package/node_modules/ws/lib/permessage-deflate.js +3 -1
  271. package/node_modules/ws/package.json +1 -1
  272. package/package.json +3 -3
  273. package/bin/completion/scripts/cds-bash.sh +0 -20
  274. package/bin/completion/scripts/cds-fish.fish +0 -13
  275. package/bin/completion/scripts/cds-ps.ps1 +0 -19
  276. package/bin/completion/scripts/cds-zsh.sh +0 -13
  277. package/lib/deploy/index.js +0 -25
  278. package/lib/import/asyncapi/importAsyncAPI.js +0 -33
  279. package/lib/import/asyncapi/index.js +0 -18
  280. package/lib/import/asyncapi/parserContext.js +0 -454
  281. package/lib/import/asyncapi/utils.js +0 -49
  282. package/node_modules/@sap/cds/libx/_runtime/ucl/queries.js +0 -61
  283. package/node_modules/negotiator/HISTORY.md +0 -114
  284. /package/{lib → bin}/upgrade/context.js +0 -0
  285. /package/{lib → bin}/upgrade/migrations/cds10/migrations.json +0 -0
  286. /package/{lib → bin}/upgrade/preflight/cds-version.js +0 -0
  287. /package/{lib → bin}/upgrade/preflight/project-type.js +0 -0
  288. /package/{lib → bin}/upgrade/steps/analyze/classification.js +0 -0
  289. /package/{lib → bin}/upgrade/steps/analyze/detector.js +0 -0
  290. /package/{lib → bin}/upgrade/steps/analyze/evaluator.js +0 -0
  291. /package/{lib → bin}/upgrade/steps/analyze/reporter.js +0 -0
  292. /package/{lib → bin}/upgrade/steps/analyze/setup.js +0 -0
  293. /package/{lib → bin}/upgrade/steps/analyze/utils/detection.js +0 -0
  294. /package/{lib → bin}/upgrade/steps/analyze/utils/files.js +0 -0
  295. /package/{lib → bin}/upgrade/steps/analyze/utils/grep.js +0 -0
  296. /package/{lib → bin}/upgrade/steps/analyze/utils/yaml.js +0 -0
  297. /package/{lib → bin}/upgrade/util/workspaces.js +0 -0
  298. /package/{lib → bin}/watch/esm-resolve-registration.js +0 -0
  299. /package/{lib → bin}/watch/livereload-server.js +0 -0
  300. /package/{lib → bin}/watch/node-watch.js +0 -0
  301. /package/{lib → bin}/watch/open.js +0 -0
  302. /package/{lib → bin}/watch/watcher.js +0 -0
@@ -232,44 +232,6 @@ function _xpr(expr, target, kind, isLambda, navPrefix = []) {
232
232
  return res.join('%20')
233
233
  }
234
234
 
235
- const _keysOfWhere = (where, kind, target) => {
236
- if (!Array.isArray(where) || !where.length) return ''
237
-
238
- if (kind === 'rest') {
239
- const keys = where.length === 1 ? getProp(where[0], 'val') : getProp(where[2], 'val')
240
- return `/${keys}`
241
- }
242
-
243
- if (where.length === 3) {
244
- const [left, op, right] = where
245
- if (op === '=' && (('val' in left && right.ref) || (left.ref && 'val' in right))) {
246
- const formattedValue =
247
- 'val' in left
248
- ? formatVal(left.val, right.ref.join('/'), target, kind)
249
- : formatVal(right.val, left.ref.join('/'), target, kind)
250
-
251
- return `(${encodeURIComponent(formattedValue)})`
252
- }
253
- }
254
-
255
- const res = []
256
- for (const cur of where) {
257
- if (hasValidProps(cur, 'ref')) {
258
- res.push(_format(cur))
259
- } else if (hasValidProps(cur, 'val')) {
260
- // find previous ref
261
- const element = res[res.length - 2]
262
- res.push(_format(cur, element, target, kind))
263
- } else if (cur === 'and') {
264
- res.push(',')
265
- } else {
266
- res.push(cur)
267
- }
268
- }
269
-
270
- return `(${res.join('')})`
271
- }
272
-
273
235
  function _getQueryTarget(entity, propOrEntity, model) {
274
236
  if (!entity) {
275
237
  // if there is no entity yet, we need to look it up in the model
@@ -302,8 +264,8 @@ const _params = (args, kind, target) => {
302
264
  return `(${_params.join(',')})`
303
265
  }
304
266
 
305
- function _from(from, kind, model) {
306
- let ref = getProp(from, 'ref')
267
+ function _from(subject, kind, model) {
268
+ let ref = getProp(subject, 'ref')
307
269
  ref = (Array.isArray(ref) && ref) || [ref]
308
270
 
309
271
  const path = []
@@ -311,21 +273,63 @@ function _from(from, kind, model) {
311
273
 
312
274
  for (const curRef of ref) {
313
275
  if (hasValidProps(curRef, 'where', 'id')) {
276
+ // Format a request URL based on req.subject[n].ref
277
+
314
278
  const { where, id } = curRef
315
279
  queryTarget = model && _getQueryTarget(queryTarget, id, model)
316
- const keys = _keysOfWhere(where, kind, queryTarget)
280
+
281
+ // Format URL keys from req.subject.ref[n].where
282
+ let keys = ''
283
+
284
+ if (Array.isArray(where) && where.length) {
285
+ if (kind === 'rest') {
286
+ if (where.length !== 1 && where[1] !== '=')
287
+ cds.error(
288
+ 500,
289
+ `Invalid subject: 'where' must specify a key via equality (key = value), got: ${JSON.stringify(where)}`
290
+ )
291
+ const val = where.length === 1 ? getProp(where[0], 'val') : getProp(where[2], 'val')
292
+ keys = `/${val}`
293
+ } else if (where.length === 3) {
294
+ const [left, op, right] = where
295
+
296
+ if (op === '=' && left.ref && 'val' in right) {
297
+ keys = `(${encodeURIComponent(formatVal(right.val, left.ref.join('/'), queryTarget, kind))})`
298
+ // REVISIT: keys in req.subject should already use the correct order
299
+ } else if (op === '=' && right.ref && 'val' in left) {
300
+ keys = `(${encodeURIComponent(formatVal(left.val, right.ref.join('/'), queryTarget, kind))})`
301
+ } else {
302
+ cds.error(
303
+ 500,
304
+ `Invalid subject: 'where' must specify a key via equality (key = value), got: ${JSON.stringify(where)}`
305
+ )
306
+ }
307
+ } else {
308
+ const res = []
309
+ for (const cur of where) {
310
+ if (hasValidProps(cur, 'ref')) {
311
+ res.push(_format(cur))
312
+ } else if (hasValidProps(cur, 'val')) {
313
+ const element = res[res.length - 2]
314
+ res.push(_format(cur, element, queryTarget, kind))
315
+ } else if (cur === 'and') {
316
+ res.push(',')
317
+ } else {
318
+ res.push(cur)
319
+ }
320
+ }
321
+
322
+ keys = `(${res.join('')})`
323
+ }
324
+ }
325
+
317
326
  path.push(`${id}${keys}`)
318
327
  } else if (hasValidProps(curRef, 'id') && 'args' in curRef) {
319
328
  const { args, id } = curRef
320
329
  queryTarget = model && _getQueryTarget(queryTarget, id, model)
321
330
  const params = _params(args, kind, queryTarget)
322
331
  path.push(`${id}${params}`)
323
- // args are only set in case the entity has parameters.
324
- // Check model if that is the case and if so, conform to the OData representation for
325
- // Views with parameters, meaning the actual entity is behind /Set
326
- if (queryTarget?.params) {
327
- path.push(`Set`)
328
- }
332
+ if (queryTarget?.params) path.push(`Set`)
329
333
  } else if (typeof curRef === 'string') {
330
334
  queryTarget = model && _getQueryTarget(queryTarget, curRef, model)
331
335
  path.push(curRef)
@@ -523,7 +527,9 @@ function getOptions(cqnPart, url, kind, target, isCount) {
523
527
  for (const opt in cqnPart) {
524
528
  const cqnPartOpt = cqnPart[opt]
525
529
  if (cqnPartOpt === undefined) continue
526
- if (!hasValidProps(cqnPart, opt)) throw new Error(`Feature not supported: SELECT statement with .${opt}`)
530
+
531
+ if (!hasValidProps(cqnPart, opt)) cds.error(500, `Feature not supported: SELECT statement with .${opt}`)
532
+
527
533
  const parser = parsers[opt]
528
534
  const parsed = parser && parser(cqnPartOpt, url, kind, target, isCount)
529
535
  const parsedOpts = (Array.isArray(parsed) && parsed) || (parsed && [parsed]) || []
@@ -541,18 +547,22 @@ const _isCount = SELECT => {
541
547
  return false
542
548
  }
543
549
 
544
- const _select = (cqn, kind, model) => {
550
+ const _select = (cqn, subject, kind, model) => {
551
+ const { url, queryTarget } = _from(subject, kind, model)
552
+
545
553
  const SELECT = getProp(cqn, 'SELECT')
546
- const { url, queryTarget } = _from(getProp(SELECT, 'from'), kind, model)
547
554
  const isCount = _isCount(SELECT)
548
555
  const queryOptions = getOptions(SELECT, url, kind, queryTarget, isCount).join('&')
549
- const path = `${url}${isCount ? '/$count' : ''}${queryOptions ? `?${queryOptions}` : ''}`
550
- return { method: 'GET', path }
556
+
557
+ return {
558
+ method: 'GET',
559
+ path: `${url}${isCount ? '/$count' : ''}${queryOptions ? `?${queryOptions}` : ''}`
560
+ }
551
561
  }
552
562
 
553
- const _insert = (cqn, kind, model) => {
563
+ const _insert = (cqn, subject, kind, model) => {
554
564
  const INSERT = getProp(cqn, 'INSERT')
555
- const { url } = _from(getProp(INSERT, 'into'), kind, model)
565
+ const { url } = _from(subject, kind, model)
556
566
  const body = _copyData(
557
567
  Array.isArray(INSERT.entries) && INSERT.entries.length === 1 ? INSERT.entries[0] : INSERT.entries
558
568
  )
@@ -572,44 +582,31 @@ const _copyData = data => {
572
582
  return copied
573
583
  }
574
584
 
575
- const _update = (cqn, kind, model, method) => {
576
- const UPDATE = getProp(cqn, 'UPDATE')
577
- const { url, queryTarget } = _from(getProp(UPDATE, 'entity'), kind, model)
578
- let keys = ''
579
-
580
- if (UPDATE.where) {
581
- if (_isOdataUrlWithKeys(url, kind)) {
582
- throw new Error('Cannot generate URL for UPDATE CQN. Conflicting .from and .where')
583
- }
584
- keys = _keysOfWhere(getProp(UPDATE, 'where'), kind, queryTarget)
585
- }
586
-
585
+ const _update = (cqn, subject, kind, model, method = 'PATCH') => {
586
+ const { url } = _from(subject, kind, model)
587
587
  // TODO: support for .set as well
588
- const body = _copyData(UPDATE.data)
589
- return { method: method || 'PATCH', path: `${url}${keys}`, body }
588
+ const body = _copyData(getProp(cqn, 'UPDATE').data)
589
+ return { method, path: url, body }
590
590
  }
591
591
 
592
- const _delete = (cqn, kind, model) => {
593
- const DELETE = getProp(cqn, 'DELETE')
594
- const { url, queryTarget } = _from(getProp(DELETE, 'from'), kind, model)
595
- let keys = ''
592
+ const _delete = (subject, kind, model) => {
593
+ const { url } = _from(subject, kind, model)
594
+ return { method: 'DELETE', path: url }
595
+ }
596
596
 
597
- if (DELETE.where) {
598
- if (_isOdataUrlWithKeys(url, kind)) {
599
- throw new Error('Cannot generate URL for DELETE CQN. Conflicting .from and .where')
600
- }
597
+ module.exports.cqn2odata = (cqn, { kind, model } = {}) => {
598
+ let { query, subject, method } = cqn
601
599
 
602
- keys = _keysOfWhere(getProp(DELETE, 'where'), kind, queryTarget)
600
+ if (!subject) {
601
+ query = cds.ql(cqn)
602
+ subject = query._subject && { ref: query._subject.ref }
603
+ method = undefined
603
604
  }
604
605
 
605
- return { method: 'DELETE', path: `${url}${keys}` }
606
- }
607
-
608
- module.exports.cqn2odata = (cqn, { kind, model, method }) => {
609
- if (cqn.SELECT) return _select(cqn, kind, model)
610
- if (cqn.INSERT) return _insert(cqn, kind, model)
611
- if (cqn.UPDATE) return _update(cqn, kind, model, method)
612
- if (cqn.DELETE) return _delete(cqn, kind, model)
606
+ if (query.SELECT) return _select(query, subject, kind, model)
607
+ if (query.INSERT) return _insert(query, subject, kind, model)
608
+ if (query.UPDATE) return _update(query, subject, kind, model, method)
609
+ if (query.DELETE) return _delete(subject, kind, model)
613
610
 
614
- throw new Error('Unknown CQN object cannot be translated to URL: ' + JSON.stringify(cqn))
611
+ throw new Error('Unknown CQN object cannot be translated to URL: ' + JSON.stringify(query))
615
612
  }
@@ -9,6 +9,20 @@ const $taskProcessorRegistered = Symbol('task processor registered')
9
9
 
10
10
  const FLUSH = 'flush'
11
11
 
12
+ // Node's `setTimeout` delay is a 32-bit signed int; larger values emit a
13
+ // `TimeoutOverflowWarning` and are clamped to 1ms (firing almost immediately).
14
+ const MAX_TIMEOUT_MS = 2 ** 31 - 1
15
+
16
+ // Like `setTimeout`, but chains multiple timers when `delay` exceeds the 32-bit
17
+ // limit so the callback still fires at its exact scheduled time. The timer is
18
+ // unref'd so it never keeps the process alive.
19
+ const _setLongTimeout = (cb, delay) => {
20
+ const step = Math.min(delay, MAX_TIMEOUT_MS)
21
+ const remaining = delay - step
22
+ const timer = setTimeout(remaining > 0 ? () => _setLongTimeout(cb, remaining) : cb, step)
23
+ return timer.unref()
24
+ }
25
+
12
26
  let T0
13
27
 
14
28
  module.exports = class SchedulingService extends cds.Service {
@@ -116,7 +130,7 @@ module.exports = class SchedulingService extends cds.Service {
116
130
 
117
131
  if (!flushOpts?.after) return cb()
118
132
 
119
- setTimeout(cb, flushOpts?.after).unref()
133
+ _setLongTimeout(cb, flushOpts.after)
120
134
  }
121
135
 
122
136
  scheduleTask = async (tasks, queueName, taskOpts, context = cds.context) => {
@@ -154,7 +168,8 @@ module.exports = class SchedulingService extends cds.Service {
154
168
  if (this._markers.has(marker.ID)) return
155
169
 
156
170
  this._markers.add(marker.ID)
157
- setTimeout(() => this._markers.delete(marker.ID), after + this.marker_interval_ms * 2).unref()
171
+ // Evict the in-memory dedup entry once the marker can no longer be re-derived.
172
+ _setLongTimeout(() => this._markers.delete(marker.ID), after + this.marker_interval_ms * 2)
158
173
 
159
174
  await cds.tx(new cds.EventContext({ tenant: T0 }), async () => {
160
175
  LOG._debug && LOG.debug(`Write marker for queue ${queueName} of tenant ${tenant} at ${marker.timestamp}`)
@@ -152,9 +152,7 @@ const _processTasks = (queueName, tenant, _opts = {}) => {
152
152
  const config = tenant ? { tenant, user: cds.User.privileged } : { user: cds.User.privileged }
153
153
  config.after = 1 // make sure spawn puts its cb on the `timer` queue (via setTimeout), which is also used by `taskRunner`
154
154
 
155
- // REVISIT: do we really need cds.spawn (e.g., for extended model)?
156
- const _spawn = (fn, o) => setTimeout(() => cds._with(o, fn), 1).unref()
157
- const _begin = cds.db.kind === 'sqlite' ? _spawn : cds.spawn.bind(cds)
155
+ const _begin = cds.spawn.bind(cds)
158
156
  const _tx = cds.tx.bind(cds)
159
157
 
160
158
  const processing_fn = async () => {
@@ -13,11 +13,9 @@ const operation = require('./middleware/operation')
13
13
 
14
14
  const { postProcessData } = require('./post-processing')
15
15
 
16
-
17
16
  class RestRequest extends cds.Request {}
18
17
  RestRequest.prototype.protocol = 'rest' // for batch subrequests where prototype is not properly set
19
18
 
20
-
21
19
  class RestAdapter extends HttpAdapter {
22
20
  request4(args) {
23
21
  return new RestRequest(args)
@@ -81,22 +79,20 @@ class RestAdapter extends HttpAdapter {
81
79
 
82
80
  if (status && res.statusCode === 200) res.status(status) //> only set status if not yet modified
83
81
 
84
- if (req.method === 'HEAD') res.type('json').set('content-length', JSON.stringify(result).length).end()
85
- else res.send(typeof result === 'number' ? result.toString() : result)
82
+ res.send(typeof result === 'number' ? result.toString() : result)
86
83
  }, next)
87
84
  })
88
85
 
89
86
  return router
90
87
  }
91
88
 
92
-
93
89
  normalized(err) {
94
90
  err = super.normalized(err)
95
- // Actually `code` is only required by OData, but we had that applied to
91
+ // Actually `code` is only required by OData, but we had that applied to
96
92
  // REST as well, so we keep it here for compatibility with legacy behavior.
97
93
  if (typeof err.code !== 'string') err.code = String(err.code || err.status || '')
98
94
  return err
99
95
  }
100
96
  }
101
97
 
102
- module.exports = RestAdapter
98
+ module.exports = RestAdapter
@@ -1,5 +1,5 @@
1
- /**
2
- * @param {import('../RestAdapter')} adapter
1
+ /**
2
+ * @param {import('../RestAdapter')} adapter
3
3
  * the adapter instance in which this middleware is used
4
4
  */
5
5
  module.exports = exports = adapter => exports.delete.bind(adapter)
@@ -8,7 +8,7 @@ module.exports = exports = adapter => exports.delete.bind(adapter)
8
8
  * @type {import('express').Handler}
9
9
  * @this import('../RestAdapter')
10
10
  */
11
- exports.delete = async function rest_delete (req, res) {
11
+ exports.delete = async function rest_delete(req, res) {
12
12
  const { _query: query, _data: data, _params: params } = req
13
13
 
14
14
  await this.service.dispatch(this.request4({ query, data, params, req, res }))
@@ -1,5 +1,5 @@
1
- /**
2
- * @param {import('../RestAdapter')} adapter
1
+ /**
2
+ * @param {import('../RestAdapter')} adapter
3
3
  * the adapter instance in which this middleware is used
4
4
  */
5
5
  module.exports = exports = adapter => exports.parse.bind(adapter)
@@ -84,12 +84,17 @@ exports.parse = function parse_odata_url(req, res, next) {
84
84
  cds.error(errorMsg, { code: 400 })
85
85
  }
86
86
  if (!one) {
87
- if (req.method === 'PATCH') throw cds.error ({ status: 405, code: `INVALID_${req.method}` })
88
- const entity = definition, keys = {}, data = req.body || {}
89
- for (let k in entity.keys) keys[k] = data[k]
90
- || entity.keys[k]['@cds.on.insert']?.['='] === '$user' && cds.context?.user?.id
91
- || cds.error ({ status: 405, code: `INVALID_${req.method}` })
92
- from.ref[0] = { id: from.ref[0], where: cds.ql.predicate (keys) }
87
+ if (req.method === 'PATCH') throw cds.error({ status: 405, code: `INVALID_${req.method}` })
88
+ const entity = definition,
89
+ keys = {},
90
+ data = req.body || {}
91
+ for (let k in entity.keys) {
92
+ keys[k] =
93
+ data[k] ||
94
+ (entity.keys[k]['@cds.on.insert']?.['='] === '$user' && cds.context?.user?.id) ||
95
+ cds.error({ status: 405, code: `INVALID_${req.method}` })
96
+ }
97
+ from.ref[0] = { id: from.ref[0], where: cds.ql.predicate(keys) }
93
98
  }
94
99
  query = UPDATE(_target)
95
100
  break
@@ -1,5 +1,5 @@
1
- /**
2
- * @param {import('../RestAdapter')} adapter
1
+ /**
2
+ * @param {import('../RestAdapter')} adapter
3
3
  * the adapter instance in which this middleware is used
4
4
  */
5
5
  module.exports = exports = adapter => exports.read.bind(adapter)
@@ -8,7 +8,7 @@ module.exports = exports = adapter => exports.read.bind(adapter)
8
8
  * @type {import('express').Handler}
9
9
  * @this import('../RestAdapter')
10
10
  */
11
- exports.read = async function rest_read (req, res) {
11
+ exports.read = async function rest_read(req, res) {
12
12
  const { _query: query, _target: target, _data: data, _params: params } = req
13
13
 
14
14
  let result,
@@ -1,5 +1,5 @@
1
- /**
2
- * @param {import('../RestAdapter')} adapter
1
+ /**
2
+ * @param {import('../RestAdapter')} adapter
3
3
  * the adapter instance in which this middleware is used
4
4
  */
5
5
  module.exports = exports = adapter => exports.upsert.bind(adapter)
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds",
3
- "version": "10.0.5",
3
+ "version": "10.1.0",
4
4
  "description": "SAP Cloud Application Programming Model - CDS for Node.js",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [
@@ -15,6 +15,45 @@ might use a deprecated flag only for a limited period of time.
15
15
 
16
16
 
17
17
 
18
+ ## Version 7.1.0 - 2026-08-27
19
+
20
+ ### Features
21
+
22
+ - **compiler:**
23
+ + It is now possible to extend a view/projection with a `where` or `having` clause, even if it already had one.
24
+ Extensions are joined with `and` if such a clause already exists.
25
+ + Publishing an association with an empty filter is now supported. For a managed association, this turns it
26
+ into an unmanaged one. For an unmanaged association, it has no effect.
27
+ + `select one` is now supported in `parse.cql` and `parse.expr`.
28
+ + Compiler adds helper property `$backlink` to CSN. It enables compiler backends and other CSN consumers
29
+ to better handle backlink comparisons with `$self`. This property may vanish in future releases.
30
+ - **odata:**
31
+ + Update OData vocabularies: Common and UI.
32
+ - **sql:**
33
+ + Wrap predicate columns in `CASE ... WHEN` for SAP HANA.
34
+ + Support common `cds.Vector` functions on SAP HANA and Postgres.
35
+
36
+ ### Bug Fixes
37
+
38
+ - **compiler:**
39
+ + Properly render structures with property `...` in CDL.
40
+ + Extending a `where`, `having` or `group by` clause from unrelated layers no longer results in an error.
41
+ A warning is emitted when extending an `order by` clause from unrelated layers.
42
+ - **effective:**
43
+ + Correctly rewrite references in annotation expressions inside arrays.
44
+ + Add option to replace annotation expressions by strings to evade error situations.
45
+ - **sql:**
46
+ + Don't dump while reporting an error for `$self` in a filter of a `from` reference.
47
+ + Correctly handle renamed target elements in backlink comparisons.
48
+
49
+ ### Improvements
50
+
51
+ - **compiler:**
52
+ + Issue a warning that a type with an unmanaged association can only be used as an include.
53
+ + Omit empty extend for `extend … with definitions` from parsed CSN.
54
+
55
+
56
+
18
57
  ## Version 7.0.3 - 2026-07-21
19
58
 
20
59
  ### Bug Fixes
@@ -34,10 +73,10 @@ might use a deprecated flag only for a limited period of time.
34
73
  - **compiler:**
35
74
  + Avoid dump for a new association in a projection with backlink comparison while reporting a missing backlink target element.
36
75
  + Soften incompatibility concerning backlink comparisons: missing target elements which are referred to in the foreign keys or ON-condition of the backlink
37
- association now only lead to a warning.
76
+ association now only lead to a warning, not an error.
38
77
  + Soften incompatibility concerning structure values for annotations: a structure property like `list` won't be rejected; we avoid an ambiguity with an
39
- _expression_ `(1, 2, 3)` by putting `'=': true` next to `list: …` in the CSN, like in compiler v6. A `xpr` expression is rendered without a sibling `=`,
40
- though.
78
+ _expression_ `(1, 2, 3)` by putting `'=': true` next to `list: …` in the CSN, like in compiler v6.
79
+ A `xpr` expression and an `#` enum symbol are rendered without a sibling `=`, though.
41
80
 
42
81
 
43
82
 
@@ -55,9 +94,9 @@ might use a deprecated flag only for a limited period of time.
55
94
  unless the expression is a simple reference. An object as annotation value with a “primary expression property” like
56
95
  `ref` or `xpr` is always interpreted as an expression. Before, it was only interpreted as expression if the object also
57
96
  contained a property `=`. In order to avoid ambiguities, it is not allowed to provide structured annotation values that
58
- could be confused with expressions.
59
- + Expression-like values that are not simple references are no longer allowed as comparator value for `... up to` when
60
- extending array like annotation values. Simple references now are handled correctly.
97
+ could be confused with expressions. See also the fix in Version 7.0.2 (`=` is only omitted for `xpr` and `#`).
98
+ + Expression-like values that are not simple references or literals are no longer allowed (and partly ignored) as comparator value
99
+ for `... up to` when extending array-like annotation values. Simple references now are handled correctly.
61
100
  + `null` as annotation value is now propagated like any other value. Exceptions are `includes` with multiple structures/aspects:
62
101
  value `null` from a later include does not overwrite a value from an earlier include (no change compared to previous releases).
63
102
  + The compiler now reports an error when multiple `extend ... with <aspect>` statements introduce the same element or action.
@@ -66,6 +105,7 @@ might use a deprecated flag only for a limited period of time.
66
105
  + The rules for the propagation of `key` in queries have been simplified: Keys are propagated, if no explicit key is set in
67
106
  the query and all key elements of the primary base entity and of joined entities are selected.
68
107
  + No longer propagate the `key` property of the elements of structures which are included into a structured type.
108
+ + Replace option `v7KeyPropagation` by option `v6KeyPropagation` with inverted semantics.
69
109
  + It is no longer possible to extend built-in types by adding type properties.
70
110
  + Always propagate annotation with `elements` and `enum` expansion.
71
111
  + Ensure that recompiling a CSN of flavor `gensrc` / `xtended` does not change the element order.
@@ -113,6 +153,16 @@ might use a deprecated flag only for a limited period of time.
113
153
  (for the message IDs `type-missing-argument`, `type-unexpected-argument`, `ref-unsupported-type`, and `ref-unexpected-args`).
114
154
 
115
155
 
156
+ ## Version 6.9.4 - 2026-07-29
157
+
158
+ ### Bug Fixes
159
+
160
+ - **effective:**
161
+ + Correctly rewrite references in annotation expressions inside arrays.
162
+ + Add option to replace annotation expressions by strings to evade error situations.
163
+
164
+
165
+
116
166
  ## Version 6.9.3 - 2026-06-17
117
167
 
118
168
  ### Bug Fixes
@@ -367,8 +417,10 @@ whose target does not exist.
367
417
 
368
418
  ### Added
369
419
 
370
- - compiler: `annotate … with @extension.code: [..., 'additional code']` even works
371
- if no value for that annotation has been provided with the base definition.
420
+ - compiler:
421
+ + `annotate … with @extension.code: [..., 'additional code']` even works
422
+ if no value for that annotation has been provided with the base definition.
423
+ + In the query section of a view definitions, `key` can now also be specified in top level inlines.
372
424
  - to.sql: Calculated elements can now be used next to (but not in) nested projections.
373
425
  - to.edm(x): The `@cds.api.ignore` annotation can now be applied to actions, functions, and their parameters.
374
426
 
@@ -377,7 +377,7 @@ async function executeCommandLine( command, options, args ) {
377
377
  function forEffective( model ) {
378
378
  const features = [
379
379
  'resolveSimpleTypes', 'resolveProjections',
380
- 'remapOdataAnnotations', 'keepLocalized',
380
+ 'stringifyAnnoXpr', 'remapOdataAnnotations', 'keepLocalized',
381
381
  ];
382
382
  for (const feature of features) {
383
383
  if (options[feature]) // map to boolean equivalent
@@ -390,7 +390,7 @@ async function executeCommandLine( command, options, args ) {
390
390
  }
391
391
 
392
392
  function forSeal( model ) {
393
- const features = [ 'remapOdataAnnotations', 'deriveAnalyticalAnnotations' ];
393
+ const features = [ 'stringifyAnnoXpr', 'remapOdataAnnotations', 'deriveAnalyticalAnnotations' ];
394
394
  for (const feature of features) {
395
395
  if (options[feature]) // map to boolean equivalent
396
396
  options[feature] = options[feature] === 'true';