arc-1 1.1.2 → 1.3.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 (357) hide show
  1. package/README.md +16 -6
  2. package/bin/start-cf.sh +32 -0
  3. package/dist/adt/atc-batch.d.ts +71 -0
  4. package/dist/adt/atc-batch.d.ts.map +1 -0
  5. package/dist/adt/atc-batch.js +182 -0
  6. package/dist/adt/atc-batch.js.map +1 -0
  7. package/dist/adt/atc.d.ts +34 -0
  8. package/dist/adt/atc.d.ts.map +1 -1
  9. package/dist/adt/atc.js +58 -12
  10. package/dist/adt/atc.js.map +1 -1
  11. package/dist/adt/aunit.d.ts.map +1 -1
  12. package/dist/adt/aunit.js +50 -7
  13. package/dist/adt/aunit.js.map +1 -1
  14. package/dist/adt/bounded-response.d.ts +12 -0
  15. package/dist/adt/bounded-response.d.ts.map +1 -0
  16. package/dist/adt/bounded-response.js +242 -0
  17. package/dist/adt/bounded-response.js.map +1 -0
  18. package/dist/adt/ci-quality-xml.d.ts +44 -0
  19. package/dist/adt/ci-quality-xml.d.ts.map +1 -0
  20. package/dist/adt/ci-quality-xml.js +98 -0
  21. package/dist/adt/ci-quality-xml.js.map +1 -0
  22. package/dist/adt/ci-quality.d.ts +64 -0
  23. package/dist/adt/ci-quality.d.ts.map +1 -0
  24. package/dist/adt/ci-quality.js +132 -0
  25. package/dist/adt/ci-quality.js.map +1 -0
  26. package/dist/adt/client.d.ts +55 -34
  27. package/dist/adt/client.d.ts.map +1 -1
  28. package/dist/adt/client.js +130 -169
  29. package/dist/adt/client.js.map +1 -1
  30. package/dist/adt/config.d.ts +6 -0
  31. package/dist/adt/config.d.ts.map +1 -1
  32. package/dist/adt/config.js +3 -0
  33. package/dist/adt/config.js.map +1 -1
  34. package/dist/adt/data-result-context.d.ts +46 -0
  35. package/dist/adt/data-result-context.d.ts.map +1 -0
  36. package/dist/adt/data-result-context.js +118 -0
  37. package/dist/adt/data-result-context.js.map +1 -0
  38. package/dist/adt/data-source-name.d.ts +60 -0
  39. package/dist/adt/data-source-name.d.ts.map +1 -0
  40. package/dist/adt/data-source-name.js +156 -0
  41. package/dist/adt/data-source-name.js.map +1 -0
  42. package/dist/adt/data-source-policy.d.ts +139 -0
  43. package/dist/adt/data-source-policy.d.ts.map +1 -0
  44. package/dist/adt/data-source-policy.js +759 -0
  45. package/dist/adt/data-source-policy.js.map +1 -0
  46. package/dist/adt/ddic-xml.d.ts +78 -4
  47. package/dist/adt/ddic-xml.d.ts.map +1 -1
  48. package/dist/adt/ddic-xml.js +647 -54
  49. package/dist/adt/ddic-xml.js.map +1 -1
  50. package/dist/adt/devtools.d.ts +1 -0
  51. package/dist/adt/devtools.d.ts.map +1 -1
  52. package/dist/adt/devtools.js +24 -4
  53. package/dist/adt/devtools.js.map +1 -1
  54. package/dist/adt/errors.d.ts +10 -0
  55. package/dist/adt/errors.d.ts.map +1 -1
  56. package/dist/adt/errors.js +17 -0
  57. package/dist/adt/errors.js.map +1 -1
  58. package/dist/adt/http-deadline.d.ts +10 -0
  59. package/dist/adt/http-deadline.d.ts.map +1 -1
  60. package/dist/adt/http-deadline.js +7 -0
  61. package/dist/adt/http-deadline.js.map +1 -1
  62. package/dist/adt/http.d.ts +6 -3
  63. package/dist/adt/http.d.ts.map +1 -1
  64. package/dist/adt/http.js +94 -55
  65. package/dist/adt/http.js.map +1 -1
  66. package/dist/adt/internal-data-operations.d.ts +56 -0
  67. package/dist/adt/internal-data-operations.d.ts.map +1 -0
  68. package/dist/adt/internal-data-operations.js +99 -0
  69. package/dist/adt/internal-data-operations.js.map +1 -0
  70. package/dist/adt/package-contents.d.ts +12 -0
  71. package/dist/adt/package-contents.d.ts.map +1 -0
  72. package/dist/adt/package-contents.js +19 -0
  73. package/dist/adt/package-contents.js.map +1 -0
  74. package/dist/adt/relation-objects.d.ts +385 -0
  75. package/dist/adt/relation-objects.d.ts.map +1 -0
  76. package/dist/adt/relation-objects.js +262 -0
  77. package/dist/adt/relation-objects.js.map +1 -0
  78. package/dist/adt/repository-relations.d.ts +41 -0
  79. package/dist/adt/repository-relations.d.ts.map +1 -0
  80. package/dist/adt/repository-relations.js +246 -0
  81. package/dist/adt/repository-relations.js.map +1 -0
  82. package/dist/adt/request-attempt-budget.d.ts +26 -0
  83. package/dist/adt/request-attempt-budget.d.ts.map +1 -0
  84. package/dist/adt/request-attempt-budget.js +63 -0
  85. package/dist/adt/request-attempt-budget.js.map +1 -0
  86. package/dist/adt/result-limits.d.ts +3 -0
  87. package/dist/adt/result-limits.d.ts.map +1 -0
  88. package/dist/adt/result-limits.js +7 -0
  89. package/dist/adt/result-limits.js.map +1 -0
  90. package/dist/adt/safety.d.ts +2 -0
  91. package/dist/adt/safety.d.ts.map +1 -1
  92. package/dist/adt/safety.js +6 -0
  93. package/dist/adt/safety.js.map +1 -1
  94. package/dist/adt/server-driven.d.ts +6 -1
  95. package/dist/adt/server-driven.d.ts.map +1 -1
  96. package/dist/adt/server-driven.js +24 -20
  97. package/dist/adt/server-driven.js.map +1 -1
  98. package/dist/adt/sql-source-analyzer.d.ts +12 -0
  99. package/dist/adt/sql-source-analyzer.d.ts.map +1 -0
  100. package/dist/adt/sql-source-analyzer.js +158 -0
  101. package/dist/adt/sql-source-analyzer.js.map +1 -0
  102. package/dist/adt/table-query.d.ts +43 -0
  103. package/dist/adt/table-query.d.ts.map +1 -0
  104. package/dist/adt/table-query.js +160 -0
  105. package/dist/adt/table-query.js.map +1 -0
  106. package/dist/adt/text-elements.d.ts +48 -0
  107. package/dist/adt/text-elements.d.ts.map +1 -0
  108. package/dist/adt/text-elements.js +116 -0
  109. package/dist/adt/text-elements.js.map +1 -0
  110. package/dist/adt/types.d.ts +16 -0
  111. package/dist/adt/types.d.ts.map +1 -1
  112. package/dist/adt/uiad.d.ts +19 -0
  113. package/dist/adt/uiad.d.ts.map +1 -0
  114. package/dist/adt/uiad.js +173 -0
  115. package/dist/adt/uiad.js.map +1 -0
  116. package/dist/adt/xml-parser.d.ts +12 -15
  117. package/dist/adt/xml-parser.d.ts.map +1 -1
  118. package/dist/adt/xml-parser.js +44 -12
  119. package/dist/adt/xml-parser.js.map +1 -1
  120. package/dist/authz/policy.d.ts.map +1 -1
  121. package/dist/authz/policy.js +4 -1
  122. package/dist/authz/policy.js.map +1 -1
  123. package/dist/cache/cache.d.ts +3 -23
  124. package/dist/cache/cache.d.ts.map +1 -1
  125. package/dist/cache/cache.js +2 -3
  126. package/dist/cache/cache.js.map +1 -1
  127. package/dist/cache/caching-layer.d.ts +6 -25
  128. package/dist/cache/caching-layer.d.ts.map +1 -1
  129. package/dist/cache/caching-layer.js +3 -45
  130. package/dist/cache/caching-layer.js.map +1 -1
  131. package/dist/cache/memory.d.ts +1 -4
  132. package/dist/cache/memory.d.ts.map +1 -1
  133. package/dist/cache/memory.js +1 -10
  134. package/dist/cache/memory.js.map +1 -1
  135. package/dist/cache/sqlite.d.ts +1 -3
  136. package/dist/cache/sqlite.d.ts.map +1 -1
  137. package/dist/cache/sqlite.js +2 -17
  138. package/dist/cache/sqlite.js.map +1 -1
  139. package/dist/cli-checks.d.ts +3 -0
  140. package/dist/cli-checks.d.ts.map +1 -1
  141. package/dist/cli-checks.js +16 -0
  142. package/dist/cli-checks.js.map +1 -1
  143. package/dist/cli.d.ts.map +1 -1
  144. package/dist/cli.js +7 -2
  145. package/dist/cli.js.map +1 -1
  146. package/dist/context/compressor.d.ts +2 -3
  147. package/dist/context/compressor.d.ts.map +1 -1
  148. package/dist/context/compressor.js +30 -49
  149. package/dist/context/compressor.js.map +1 -1
  150. package/dist/context/contract.d.ts +5 -0
  151. package/dist/context/contract.d.ts.map +1 -1
  152. package/dist/context/contract.js +2 -2
  153. package/dist/context/contract.js.map +1 -1
  154. package/dist/context/deps.d.ts +11 -0
  155. package/dist/context/deps.d.ts.map +1 -1
  156. package/dist/context/deps.js +2 -2
  157. package/dist/context/deps.js.map +1 -1
  158. package/dist/context/parse-cache.d.ts +23 -0
  159. package/dist/context/parse-cache.d.ts.map +1 -0
  160. package/dist/context/parse-cache.js +71 -0
  161. package/dist/context/parse-cache.js.map +1 -0
  162. package/dist/context/relation-walk.d.ts +48 -0
  163. package/dist/context/relation-walk.d.ts.map +1 -0
  164. package/dist/context/relation-walk.js +137 -0
  165. package/dist/context/relation-walk.js.map +1 -0
  166. package/dist/context/types.d.ts +4 -4
  167. package/dist/context/types.d.ts.map +1 -1
  168. package/dist/handlers/activate.d.ts +1 -1
  169. package/dist/handlers/activate.d.ts.map +1 -1
  170. package/dist/handlers/activate.js +33 -56
  171. package/dist/handlers/activate.js.map +1 -1
  172. package/dist/handlers/activation-results.d.ts +3 -0
  173. package/dist/handlers/activation-results.d.ts.map +1 -0
  174. package/dist/handlers/activation-results.js +8 -0
  175. package/dist/handlers/activation-results.js.map +1 -0
  176. package/dist/handlers/context.d.ts.map +1 -1
  177. package/dist/handlers/context.js +8 -32
  178. package/dist/handlers/context.js.map +1 -1
  179. package/dist/handlers/data-element-fields.d.ts +26 -0
  180. package/dist/handlers/data-element-fields.d.ts.map +1 -0
  181. package/dist/handlers/data-element-fields.js +27 -0
  182. package/dist/handlers/data-element-fields.js.map +1 -0
  183. package/dist/handlers/diagnose-ci.d.ts +9 -0
  184. package/dist/handlers/diagnose-ci.d.ts.map +1 -0
  185. package/dist/handlers/diagnose-ci.js +120 -0
  186. package/dist/handlers/diagnose-ci.js.map +1 -0
  187. package/dist/handlers/diagnose-fields.d.ts +51 -0
  188. package/dist/handlers/diagnose-fields.d.ts.map +1 -0
  189. package/dist/handlers/diagnose-fields.js +48 -0
  190. package/dist/handlers/diagnose-fields.js.map +1 -0
  191. package/dist/handlers/diagnose.d.ts +4 -1
  192. package/dist/handlers/diagnose.d.ts.map +1 -1
  193. package/dist/handlers/diagnose.js +41 -6
  194. package/dist/handlers/diagnose.js.map +1 -1
  195. package/dist/handlers/dispatch.d.ts +3 -1
  196. package/dist/handlers/dispatch.d.ts.map +1 -1
  197. package/dist/handlers/dispatch.js +96 -11
  198. package/dist/handlers/dispatch.js.map +1 -1
  199. package/dist/handlers/feature-cache.d.ts +4 -3
  200. package/dist/handlers/feature-cache.d.ts.map +1 -1
  201. package/dist/handlers/feature-cache.js +4 -3
  202. package/dist/handlers/feature-cache.js.map +1 -1
  203. package/dist/handlers/hyperfocused.js +1 -1
  204. package/dist/handlers/live-relations.d.ts +5 -0
  205. package/dist/handlers/live-relations.d.ts.map +1 -0
  206. package/dist/handlers/live-relations.js +89 -0
  207. package/dist/handlers/live-relations.js.map +1 -0
  208. package/dist/handlers/manage.d.ts.map +1 -1
  209. package/dist/handlers/manage.js +7 -4
  210. package/dist/handlers/manage.js.map +1 -1
  211. package/dist/handlers/navigate.d.ts +1 -1
  212. package/dist/handlers/navigate.d.ts.map +1 -1
  213. package/dist/handlers/navigate.js +33 -7
  214. package/dist/handlers/navigate.js.map +1 -1
  215. package/dist/handlers/object-types.d.ts +6 -4
  216. package/dist/handlers/object-types.d.ts.map +1 -1
  217. package/dist/handlers/object-types.js +45 -10
  218. package/dist/handlers/object-types.js.map +1 -1
  219. package/dist/handlers/query-errors.d.ts +9 -1
  220. package/dist/handlers/query-errors.d.ts.map +1 -1
  221. package/dist/handlers/query-errors.js +40 -28
  222. package/dist/handlers/query-errors.js.map +1 -1
  223. package/dist/handlers/query.d.ts +2 -2
  224. package/dist/handlers/query.d.ts.map +1 -1
  225. package/dist/handlers/query.js +15 -22
  226. package/dist/handlers/query.js.map +1 -1
  227. package/dist/handlers/read.d.ts +1 -1
  228. package/dist/handlers/read.d.ts.map +1 -1
  229. package/dist/handlers/read.js +112 -23
  230. package/dist/handlers/read.js.map +1 -1
  231. package/dist/handlers/relation-input.d.ts +42 -0
  232. package/dist/handlers/relation-input.d.ts.map +1 -0
  233. package/dist/handlers/relation-input.js +22 -0
  234. package/dist/handlers/relation-input.js.map +1 -0
  235. package/dist/handlers/relation-tool.d.ts +5 -0
  236. package/dist/handlers/relation-tool.d.ts.map +1 -0
  237. package/dist/handlers/relation-tool.js +36 -0
  238. package/dist/handlers/relation-tool.js.map +1 -0
  239. package/dist/handlers/schemas.d.ts +119 -62
  240. package/dist/handlers/schemas.d.ts.map +1 -1
  241. package/dist/handlers/schemas.js +150 -13
  242. package/dist/handlers/schemas.js.map +1 -1
  243. package/dist/handlers/search.d.ts +1 -1
  244. package/dist/handlers/search.d.ts.map +1 -1
  245. package/dist/handlers/search.js +43 -8
  246. package/dist/handlers/search.js.map +1 -1
  247. package/dist/handlers/tool-descriptions.d.ts.map +1 -1
  248. package/dist/handlers/tool-descriptions.js +12 -13
  249. package/dist/handlers/tool-descriptions.js.map +1 -1
  250. package/dist/handlers/tool-registry.d.ts +1 -0
  251. package/dist/handlers/tool-registry.d.ts.map +1 -1
  252. package/dist/handlers/tool-registry.js +4 -0
  253. package/dist/handlers/tool-registry.js.map +1 -1
  254. package/dist/handlers/tools.d.ts +2 -3
  255. package/dist/handlers/tools.d.ts.map +1 -1
  256. package/dist/handlers/tools.js +139 -84
  257. package/dist/handlers/tools.js.map +1 -1
  258. package/dist/handlers/where-used.d.ts +2 -0
  259. package/dist/handlers/where-used.d.ts.map +1 -1
  260. package/dist/handlers/where-used.js +23 -5
  261. package/dist/handlers/where-used.js.map +1 -1
  262. package/dist/handlers/write/batch-results.d.ts +28 -0
  263. package/dist/handlers/write/batch-results.d.ts.map +1 -0
  264. package/dist/handlers/write/batch-results.js +106 -0
  265. package/dist/handlers/write/batch-results.js.map +1 -0
  266. package/dist/handlers/write/create-transport.d.ts +7 -0
  267. package/dist/handlers/write/create-transport.d.ts.map +1 -0
  268. package/dist/handlers/write/create-transport.js +38 -0
  269. package/dist/handlers/write/create-transport.js.map +1 -0
  270. package/dist/handlers/write/create.d.ts.map +1 -1
  271. package/dist/handlers/write/create.js +276 -413
  272. package/dist/handlers/write/create.js.map +1 -1
  273. package/dist/handlers/write/uiad.d.ts +8 -0
  274. package/dist/handlers/write/uiad.d.ts.map +1 -0
  275. package/dist/handlers/write/uiad.js +115 -0
  276. package/dist/handlers/write/uiad.js.map +1 -0
  277. package/dist/handlers/write/update-delete.d.ts +6 -6
  278. package/dist/handlers/write/update-delete.d.ts.map +1 -1
  279. package/dist/handlers/write/update-delete.js +46 -15
  280. package/dist/handlers/write/update-delete.js.map +1 -1
  281. package/dist/handlers/write-helpers.d.ts +0 -6
  282. package/dist/handlers/write-helpers.d.ts.map +1 -1
  283. package/dist/handlers/write-helpers.js +88 -80
  284. package/dist/handlers/write-helpers.js.map +1 -1
  285. package/dist/handlers/write.d.ts.map +1 -1
  286. package/dist/handlers/write.js +5 -1
  287. package/dist/handlers/write.js.map +1 -1
  288. package/dist/public/index.d.ts +1 -1
  289. package/dist/public/index.d.ts.map +1 -1
  290. package/dist/public/index.js +1 -1
  291. package/dist/public/index.js.map +1 -1
  292. package/dist/public/ui/app.js +0 -2
  293. package/dist/server/audit.d.ts +42 -1
  294. package/dist/server/audit.d.ts.map +1 -1
  295. package/dist/server/audit.js.map +1 -1
  296. package/dist/server/config.d.ts +4 -0
  297. package/dist/server/config.d.ts.map +1 -1
  298. package/dist/server/config.js +67 -0
  299. package/dist/server/config.js.map +1 -1
  300. package/dist/server/context.d.ts +5 -0
  301. package/dist/server/context.d.ts.map +1 -1
  302. package/dist/server/context.js.map +1 -1
  303. package/dist/server/destination-registry.d.ts +10 -2
  304. package/dist/server/destination-registry.d.ts.map +1 -1
  305. package/dist/server/destination-registry.js +12 -3
  306. package/dist/server/destination-registry.js.map +1 -1
  307. package/dist/server/effective-policy-log.d.ts +2 -1
  308. package/dist/server/effective-policy-log.d.ts.map +1 -1
  309. package/dist/server/effective-policy-log.js +18 -2
  310. package/dist/server/effective-policy-log.js.map +1 -1
  311. package/dist/server/http.d.ts +1 -1
  312. package/dist/server/http.d.ts.map +1 -1
  313. package/dist/server/http.js +1 -0
  314. package/dist/server/http.js.map +1 -1
  315. package/dist/server/logger.d.ts.map +1 -1
  316. package/dist/server/logger.js +4 -1
  317. package/dist/server/logger.js.map +1 -1
  318. package/dist/server/multi-target-runtime.d.ts.map +1 -1
  319. package/dist/server/multi-target-runtime.js +7 -2
  320. package/dist/server/multi-target-runtime.js.map +1 -1
  321. package/dist/server/multi-target-tools.d.ts.map +1 -1
  322. package/dist/server/multi-target-tools.js +3 -1
  323. package/dist/server/multi-target-tools.js.map +1 -1
  324. package/dist/server/runtime-memory.d.ts +13 -0
  325. package/dist/server/runtime-memory.d.ts.map +1 -0
  326. package/dist/server/runtime-memory.js +42 -0
  327. package/dist/server/runtime-memory.js.map +1 -0
  328. package/dist/server/server-instructions.d.ts +2 -0
  329. package/dist/server/server-instructions.d.ts.map +1 -0
  330. package/dist/server/server-instructions.js +33 -0
  331. package/dist/server/server-instructions.js.map +1 -0
  332. package/dist/server/server.d.ts +3 -2
  333. package/dist/server/server.d.ts.map +1 -1
  334. package/dist/server/server.js +42 -78
  335. package/dist/server/server.js.map +1 -1
  336. package/dist/server/shutdown.d.ts +6 -0
  337. package/dist/server/shutdown.d.ts.map +1 -0
  338. package/dist/server/shutdown.js +52 -0
  339. package/dist/server/shutdown.js.map +1 -0
  340. package/dist/server/sinks/btp-auditlog.d.ts +10 -8
  341. package/dist/server/sinks/btp-auditlog.d.ts.map +1 -1
  342. package/dist/server/sinks/btp-auditlog.js +86 -68
  343. package/dist/server/sinks/btp-auditlog.js.map +1 -1
  344. package/dist/server/sinks/file.d.ts +1 -1
  345. package/dist/server/sinks/file.d.ts.map +1 -1
  346. package/dist/server/sinks/file.js +12 -21
  347. package/dist/server/sinks/file.js.map +1 -1
  348. package/dist/server/types.d.ts +8 -0
  349. package/dist/server/types.d.ts.map +1 -1
  350. package/dist/server/types.js +4 -0
  351. package/dist/server/types.js.map +1 -1
  352. package/dist/server/ui-state.d.ts.map +1 -1
  353. package/dist/server/ui-state.js +1 -0
  354. package/dist/server/ui-state.js.map +1 -1
  355. package/dist/server/ui.js +0 -2
  356. package/dist/server/ui.js.map +1 -1
  357. package/package.json +15 -13
@@ -2,23 +2,23 @@
2
2
  * SAPWrite actions — create + batch_create.
3
3
  */
4
4
  import { createObject, lockObject, safeUpdateObject, safeUpdateSource, unlockObject, updateObject, } from '../../adt/crud.js';
5
- import { normalizeAdtLanguage, rewriteKtdText } from '../../adt/ddic-xml.js';
5
+ import { formatKtdWriteReport, normalizeAdtLanguage, rewriteKtdDocument, } from '../../adt/ddic-xml.js';
6
6
  import { activate, activateBatch } from '../../adt/devtools.js';
7
- import { AdtApiError } from '../../adt/errors.js';
7
+ import { AdtApiError, AdtSafetyError } from '../../adt/errors.js';
8
8
  import { spliceFmSignature } from '../../adt/fm-signature.js';
9
- import { checkPackage } from '../../adt/safety.js';
9
+ import { checkOperation, checkPackage, OperationType } from '../../adt/safety.js';
10
10
  import { isServerDrivenObjectType } from '../../adt/server-driven.js';
11
- import { getTransport, getTransportInfo } from '../../adt/transport.js';
11
+ import { getTransport } from '../../adt/transport.js';
12
12
  import { escapeXmlAttr, parseFunctionModuleProperties } from '../../adt/xml-parser.js';
13
13
  import { validateAffHeader } from '../../aff/validator.js';
14
- import { logger } from '../../server/logger.js';
15
- import { buildBatchActivationStatuses, formatBatchActivationStatuses, } from '../activate.js';
16
- import { invalidateInactiveList } from '../cache-security.js';
14
+ import { activationDetailMatchesObject } from '../activation-results.js';
17
15
  import { guardCdsSyntax } from '../cds-hints.js';
18
16
  import { getCachedFeatures, isDomainsEndpointAvailable, isTablesEndpointAvailable, isTableTypesEndpointAvailable, } from '../feature-cache.js';
19
- import { functionGroupObjectUrl, functionModuleObjectUrl, normalizeObjectType, normalizeWriteObjectType, objectBasePath, objectUrlForType, sourceUrlForType, } from '../object-types.js';
17
+ import { canonicalTablType, functionGroupObjectUrl, functionModuleObjectUrl, normalizeWriteObjectType, objectBasePath, objectUrlForType, sourceUrlForType, } from '../object-types.js';
20
18
  import { errorResult, textResult } from '../shared.js';
21
- import { buildCreateXml, createContentTypeForType, DOMA_WRITE_UNAVAILABLE_HINT, dtelNeedsPostCreateUpdate, getMetadataWriteProperties, isMetadataWriteType, mergePreWriteWarnings, resolveWriteSystemType, runPreWriteLint, runRapPreflightValidation, SKTD_V2_CONTENT_TYPE, stripFmParamCommentBlock, TABL_DT_WRITE_UNAVAILABLE_HINT, TTYP_WRITE_UNAVAILABLE_HINT, tryPostSaveSyntaxCheck, vendorContentTypeForType, } from '../write-helpers.js';
19
+ import { buildCreateXml, createContentTypeForType, DOMA_WRITE_UNAVAILABLE_HINT, getMetadataWriteProperties, isMetadataWriteType, mergePreWriteWarnings, resolveWriteSystemType, runPreWriteLint, runRapPreflightValidation, SKTD_V2_CONTENT_TYPE, stripFmParamCommentBlock, TABL_DT_WRITE_UNAVAILABLE_HINT, TTYP_WRITE_UNAVAILABLE_HINT, tryPostSaveSyntaxCheck, vendorContentTypeForType, } from '../write-helpers.js';
20
+ import { batchCreateResult, batchEntryResult, batchFailureMessage, failBatchEntry, } from './batch-results.js';
21
+ import { resolveCreateTransport } from './create-transport.js';
22
22
  const FUNCTION_MODULE_MEDIA_TYPE = /^application\/vnd\.sap\.adt\.functions\.fmodules(?:\.v\d+)?\+xml\b/i;
23
23
  function normalizePackageOverride(rawPackage, fallback) {
24
24
  if (rawPackage === undefined || rawPackage === null) {
@@ -300,7 +300,7 @@ function isKtdCreateEndpointUnavailable(err) {
300
300
  return combined.includes('resource') && combined.includes('/sap/bc/adt/documentation/ktd/documents');
301
301
  }
302
302
  export async function writeActionCreate(ctx) {
303
- const { client, args, config, type, name, source, transport, lintOverride, preflightOverride, objectUrl, srcUrl, invalidateWrittenObject, } = ctx;
303
+ const { client, args, config, type, name, source, hasSource, transport, lintOverride, preflightOverride, objectUrl, srcUrl, invalidateWrittenObject, } = ctx;
304
304
  // FUNC and FUGR structural includes both INHERIT the parent group's package — SAP ignores
305
305
  // _package for them — so the allowlist must be checked against the group's real package.
306
306
  // Gating on args.package here would let a caller write into a disallowed package by claiming $TMP.
@@ -311,39 +311,12 @@ export async function writeActionCreate(ctx) {
311
311
  : String(args.package ?? '$TMP');
312
312
  await checkPackage(client.safety, pkg, client.getPackageHierarchyResolver());
313
313
  const description = String(args.description ?? name);
314
- // Pre-flight: check transport requirements for non-$TMP packages when no transport provided.
315
- // SAP requires a transport number for objects in transportable packages.
316
- // Instead of letting SAP return a cryptic error, we detect this early and return
317
- // an actionable error message guiding the LLM to use SAPTransport first.
318
314
  let effectiveTransport = transport;
319
- if (!transport && pkg.toUpperCase() !== '$TMP') {
320
- try {
321
- const transportInfo = await getTransportInfo(client.http, client.safety, objectUrl, pkg, 'I');
322
- if (transportInfo.lockedTransport) {
323
- // Object is already locked in a transport — use it automatically
324
- effectiveTransport = transportInfo.lockedTransport;
325
- }
326
- else if (!transportInfo.isLocal && transportInfo.recording) {
327
- // Transport IS required but none provided — return guidance
328
- const existingList = transportInfo.existingTransports.length > 0
329
- ? `\n\nExisting transports for this package:\n${transportInfo.existingTransports
330
- .slice(0, 10)
331
- .map((t) => ` - ${t.id}: ${t.description} (${t.owner})`)
332
- .join('\n')}`
333
- : '';
334
- return errorResult(`Package "${pkg}" requires a transport number for object creation, but none was provided.\n\n` +
335
- `To fix this, either:\n` +
336
- `1. Use SAPTransport(action="list") to find an existing modifiable transport\n` +
337
- `2. Use SAPTransport(action="create", description="...") to create a new one\n` +
338
- `3. Then retry SAPWrite(action="create", ..., transport="<transport_id>")` +
339
- existingList);
340
- }
341
- // isLocal=true or recording=false → no transport needed, proceed without one
342
- }
343
- catch {
344
- // If transportInfo check fails (older system, permissions, etc.), proceed without it.
345
- // SAP will return its own error if a transport is actually needed.
346
- }
315
+ if (!transport) {
316
+ const resolved = await resolveCreateTransport(client, objectUrl, pkg, config.minimalErrors);
317
+ if (resolved.error)
318
+ return errorResult(resolved.error);
319
+ effectiveTransport = resolved.transport;
347
320
  }
348
321
  // MSAG transport-vs-task guard. Some SAP releases silently drop message inserts when
349
322
  // given a task number as corrNr — CL_ADT_MESSAGE_CLASS_API=>create() passes corrNr to
@@ -412,19 +385,36 @@ export async function writeActionCreate(ctx) {
412
385
  }
413
386
  throw err;
414
387
  }
415
- // If initial Markdown was provided, follow up with an update PUT to write it.
416
- // Same envelope contract as the update path: fetch-then-rewrite ensures we
417
- // PUT back exactly the shape SAP gave us (with all the server-assigned
418
- // metadata), only swapping <sktd:text>.
419
- if (source) {
420
- const { source: currentEnvelope } = await client.getKtd(name);
421
- const body = rewriteKtdText(currentEnvelope, source);
422
- await safeUpdateObject(client.http, client.safety, objectUrl, body, SKTD_V2_CONTENT_TYPE, effectiveTransport, getCachedFeatures()?.abapRelease);
388
+ // Bodies and/or short texts require a follow-up PUT of the server-provided envelope.
389
+ // The POST already succeeded, so every later failure is reported as partial success.
390
+ const shortTexts = args.shortTexts;
391
+ if (hasSource || shortTexts?.length) {
392
+ let summary;
393
+ try {
394
+ const { source: currentEnvelope } = await client.getKtd(name);
395
+ const report = { proseHeadings: [] };
396
+ const body = rewriteKtdDocument(currentEnvelope, hasSource ? source : undefined, shortTexts, report);
397
+ summary = formatKtdWriteReport(currentEnvelope, body, report);
398
+ await safeUpdateObject(client.http, client.safety, objectUrl, body, SKTD_V2_CONTENT_TYPE, effectiveTransport, getCachedFeatures()?.abapRelease);
399
+ }
400
+ catch (err) {
401
+ // The POST above already succeeded, so the KTD exists. Say so — a generic
402
+ // read/rewrite/PUT failure invites the same create again, which 409s.
403
+ invalidateWrittenObject(type, name);
404
+ const documentationPart = shortTexts?.length ? 'documentation' : 'documentation body';
405
+ return errorResult(`Created SKTD ${name} in package ${pkg}, but the ${documentationPart} was NOT written: ` +
406
+ `${err instanceof Error ? err.message : String(err)}\n` +
407
+ `The object exists — verify it with SAPRead. Inspect the reported cause: correct or remove invalid ` +
408
+ `${[hasSource && 'source', shortTexts?.length && 'shortTexts'].filter(Boolean).join(' and ')} input, ` +
409
+ `or wait for a transient read/PUT failure to recover. Then use ` +
410
+ `SAPWrite(action="update", type="SKTD", name="${name}", …); do not retry create.`);
411
+ }
423
412
  invalidateWrittenObject(type, name);
424
- return textResult(`Created SKTD ${name} in package ${pkg} and wrote Markdown content.\nNext step: SAPActivate(type="SKTD", name="${name}").\n${ktdResult}`);
413
+ const writtenPart = shortTexts?.length ? 'its documentation' : 'Markdown content';
414
+ return textResult(`Created SKTD ${name} in package ${pkg} and wrote ${writtenPart}.\n${summary}\nNext step: SAPActivate(type="SKTD", name="${name}").\n${ktdResult}`);
425
415
  }
426
416
  invalidateWrittenObject();
427
- return textResult(`Created SKTD ${name} in package ${pkg} (no Markdown content written — pass "source" to write the body).\nNext step: SAPActivate(type="SKTD", name="${name}").\n${ktdResult}`);
417
+ return textResult(`Created SKTD ${name} in package ${pkg} (no Markdown content written — pass "source" for node bodies, and optionally "shortTexts" for per-node short texts).\nNext step: SAPActivate(type="SKTD", name="${name}").\n${ktdResult}`);
428
418
  }
429
419
  // Build type-specific creation XML body.
430
420
  // SAP ADT requires the root element to match the object type —
@@ -472,10 +462,11 @@ export async function writeActionCreate(ctx) {
472
462
  await persistFunctionModuleProcessingMetadata(client, objectUrl, name, metadataProperties.processingType, metadataProperties.updateTaskKind, effectiveTransport);
473
463
  }
474
464
  if (isMetadataWriteType(type)) {
475
- // SAP's DTEL POST ignores labels, searchHelp, etc. they require a follow-up PUT.
465
+ // SAP's DTEL POST stores only a shell without the description, labels or custom lengths, so
466
+ // every DTEL create needs this follow-up PUT.
476
467
  // Use withStatefulSession directly (not safeUpdateObject) to keep the lock cycle
477
468
  // on the main client's session, avoiding lock contention with subsequent operations.
478
- if (type === 'DTEL' && dtelNeedsPostCreateUpdate(metadataProperties)) {
469
+ if (type === 'DTEL') {
479
470
  const ct = vendorContentTypeForType(type);
480
471
  await client.http.withStatefulSession(async (session) => {
481
472
  const lock = await lockObject(session, client.safety, objectUrl, 'MODIFY', getCachedFeatures()?.abapRelease, systemType);
@@ -543,420 +534,292 @@ export async function writeActionCreate(ctx) {
543
534
  return textResult(`Created ${type} ${name} in package ${pkg}.\n${result}`);
544
535
  }
545
536
  export async function writeActionBatchCreate(ctx) {
546
- const { client, args, config, cachingLayer, cacheSecurity, transport, lintOverride, preflightOverride, invalidateWrittenObject, } = ctx;
537
+ const { client, args, config, transport, lintOverride, preflightOverride, invalidateWrittenObject } = ctx;
547
538
  const objects = args.objects;
548
539
  if (!objects || !Array.isArray(objects) || objects.length === 0) {
549
540
  return errorResult('"objects" array is required and must be non-empty for batch_create action.');
550
541
  }
551
- // Opt-in deferred-activation: writes every object as an inactive draft first,
552
- // then issues a single terminal activateBatch over the written subset. Use case:
553
- // composition-linked DDLS / interdependent RAP graphs where per-object inline
554
- // activate() can't resolve cross-references to not-yet-active siblings.
542
+ checkOperation(client.safety, OperationType.Create, 'CreateObject');
555
543
  const activateAtEnd = args.activateAtEnd === true || String(args.activateAtEnd) === 'true';
556
544
  const defaultPackage = normalizePackageOverride(args.package, '$TMP');
557
- const batchPlan = objects.map((obj) => {
558
- const objType = normalizeWriteObjectType(String(obj.type ?? ''));
559
- const objName = String(obj.name ?? '');
560
- const objPackage = normalizePackageOverride(obj.package, defaultPackage);
561
- const explicitTransport = normalizeTransportOverride(obj.transport) ?? transport;
562
- const group = objType === 'FUNC' ? String(obj.group ?? args.group ?? '').trim() : undefined;
563
- const objectUrl = objType === 'FUNC' ? (group ? functionModuleObjectUrl(group, objName) : '') : objectUrlForType(objType, objName);
564
- return { obj, type: objType, name: objName, packageName: objPackage, explicitTransport, group, objectUrl };
545
+ const warnings = [];
546
+ const batchPlan = objects.map((obj, index) => {
547
+ const type = normalizeWriteObjectType(String(obj.type ?? ''));
548
+ const name = String(obj.name ?? '');
549
+ const packageName = normalizePackageOverride(obj.package, defaultPackage);
550
+ const group = type === 'FUNC' ? String(obj.group ?? args.group ?? '').trim() : undefined;
551
+ const objectUrl = type === 'FUNC' ? (group ? functionModuleObjectUrl(group, name) : '') : objectUrlForType(type, name);
552
+ const metadata = getMetadataWriteProperties(obj);
553
+ if (type === 'FUNC')
554
+ metadata.group = group;
555
+ const source = obj.source ? String(obj.source) : undefined;
556
+ return {
557
+ obj,
558
+ type,
559
+ name,
560
+ packageName,
561
+ group,
562
+ objectUrl,
563
+ metadata,
564
+ source,
565
+ transport: normalizeTransportOverride(obj.transport) ?? transport,
566
+ description: String(obj.description ?? name),
567
+ metadataObject: isMetadataWriteType(type),
568
+ body: '',
569
+ contentType: '',
570
+ sourceUrl: '',
571
+ result: batchEntryResult(index, type, name, packageName),
572
+ };
565
573
  });
566
- const functionWithoutGroup = batchPlan.find((item) => item.type === 'FUNC' && !item.group);
567
- if (functionWithoutGroup) {
568
- return errorResult(`FUNC ${functionWithoutGroup.name} in batch_create requires "group" on the object entry or top-level request.`);
569
- }
570
- // A FUNC is contained by its FUGR and inherits the parent's package. Resolve
571
- // that package before any safety or transport check; trusting the batch's
572
- // caller-supplied/default package would gate the wrong object boundary.
573
- try {
574
- for (const plan of batchPlan) {
574
+ const results = batchPlan.map((plan) => plan.result);
575
+ const activationMessages = [];
576
+ const report = (preflight = false) => batchCreateResult(results, { preflight, activateAtEnd, warnings, activationMessages });
577
+ const seen = new Map();
578
+ // Validate the complete input before package/transport reads or the first create.
579
+ for (const plan of batchPlan) {
580
+ const errors = [];
581
+ try {
582
+ if (!plan.name.trim() || plan.name !== plan.name.trim() || plan.name !== plan.name.toUpperCase()) {
583
+ errors.push(`Object name "${plan.name}" must be non-empty, uppercase, and have no surrounding whitespace (e.g. "${plan.name.trim().toUpperCase()}"); source may stay mixed case.`);
584
+ }
585
+ const keyType = plan.type === 'INCL' ? 'PROG' : ['CLAS', 'INTF'].includes(plan.type) ? 'OO' : canonicalTablType(plan.type);
586
+ const key = `${keyType}\0${plan.name.toUpperCase()}`;
587
+ const previous = seen.get(key);
588
+ if (previous !== undefined)
589
+ errors.push(`Duplicate object: entry ${plan.result.index + 1} repeats entry ${previous + 1}.`);
590
+ else
591
+ seen.set(key, plan.result.index);
592
+ if (plan.type === 'FUNC' && !plan.group) {
593
+ errors.push(`FUNC ${plan.name} in batch_create requires "group" on the object entry or top-level request.`);
594
+ }
595
+ if (isServerDrivenObjectType(plan.type)) {
596
+ errors.push(`batch_create does not support server-driven object type ${plan.type}. Create it with a single SAPWrite(action="create", type="${plan.type}", name="${plan.name}") call.`);
597
+ }
598
+ if ((plan.type === 'TABL' || plan.type === 'TABL/DT') && isTablesEndpointAvailable() === false)
599
+ errors.push(TABL_DT_WRITE_UNAVAILABLE_HINT);
600
+ if (plan.type === 'DOMA' && isDomainsEndpointAvailable() === false)
601
+ errors.push(DOMA_WRITE_UNAVAILABLE_HINT);
602
+ if (plan.type === 'TTYP' && isTableTypesEndpointAvailable() === false)
603
+ errors.push(TTYP_WRITE_UNAVAILABLE_HINT);
604
+ if (plan.type === 'INCL' && plan.name.startsWith('L')) {
605
+ errors.push('Function-group structural includes require a single SAPWrite create with group; batch_create does not support them.');
606
+ }
607
+ const aff = validateAffHeader(plan.type, { description: plan.description, originalLanguage: 'en' });
608
+ if (!aff.valid)
609
+ errors.push(`AFF metadata validation failed:\n- ${(aff.errors ?? []).join('\n- ')}`);
575
610
  if (plan.type === 'FUNC') {
576
- plan.packageName = await resolveFunctionGroupCreatePackage(client, plan.group,
577
- // The top-level package is the default for ordinary batch entries,
578
- // not a claim about a contained FUNC. Only an item-level package is
579
- // explicit enough to assert the inherited FUGR package.
580
- plan.obj.package);
611
+ const prepared = prepareFunctionModuleCreateSource(plan.name, plan.source, plan.obj.parameters);
612
+ plan.source = prepared.shouldWrite ? prepared.source : undefined;
613
+ warnings.push(...prepared.warnings.map((warning) => `${plan.type} ${plan.name}: ${warning}`));
614
+ }
615
+ if (!plan.metadataObject && plan.source && !isServerDrivenObjectType(plan.type)) {
616
+ const cds = guardCdsSyntax(plan.type, plan.source, getCachedFeatures());
617
+ if (cds)
618
+ errors.push(cds.content[0].text);
619
+ const rap = runRapPreflightValidation(plan.source, plan.type, plan.name, getCachedFeatures(), config.systemType, preflightOverride);
620
+ if (rap.blocked)
621
+ errors.push(rap.result.content[0].text);
622
+ if (rap.warnings)
623
+ warnings.push(`${plan.type} ${plan.name}: ${rap.warnings}`);
624
+ const lint = runPreWriteLint(plan.source, plan.type, plan.name, config, lintOverride);
625
+ if (lint.blocked)
626
+ errors.push(`source rejected by lint: ${lint.result.content[0].text}`);
627
+ if (lint.warnings)
628
+ warnings.push(`${plan.type} ${plan.name}: ${lint.warnings}`);
629
+ }
630
+ if (!isServerDrivenObjectType(plan.type)) {
631
+ plan.sourceUrl =
632
+ plan.type === 'FUNC' ? `${plan.objectUrl}/source/main` : sourceUrlForType(plan.type, plan.name);
633
+ }
634
+ if (plan.type === 'DTEL' ||
635
+ plan.type === 'TTYP' ||
636
+ (plan.type === 'FUNC' && plan.metadata.processingType !== undefined) ||
637
+ (!plan.metadataObject && plan.source)) {
638
+ plan.result.write = 'not_attempted';
581
639
  }
582
640
  }
583
- }
584
- catch (err) {
585
- return errorResult(err instanceof Error ? err.message : String(err));
586
- }
587
- // Check every target package before starting any creates.
588
- // Resolver is shared across the loop so subtree BFS happens once even when
589
- // many objects target descendants of the same `ZFOO/**` root.
590
- {
591
- const resolver = client.getPackageHierarchyResolver();
592
- for (const pkg of new Set(batchPlan.map((item) => item.packageName))) {
593
- await checkPackage(client.safety, pkg, resolver);
641
+ catch (err) {
642
+ errors.push(batchFailureMessage(err, false));
594
643
  }
644
+ if (errors.length)
645
+ failBatchEntry(plan.result, 'preflight', errors.join('\n'));
595
646
  }
596
- // Pre-flight transport check for batch_create (same logic as single create),
597
- // but keyed by each effective package because objects can override package.
598
- const autoTransportByPackage = new Map();
599
- const firstPlanNeedingTransportByPackage = new Map();
647
+ if (results.some((entry) => entry.status === 'failed'))
648
+ return report(true);
649
+ // Resolve FUNC's actual inherited package and enforce every package before mutations.
600
650
  for (const plan of batchPlan) {
601
- if (!plan.explicitTransport &&
602
- plan.packageName.toUpperCase() !== '$TMP' &&
603
- !firstPlanNeedingTransportByPackage.has(plan.packageName)) {
604
- firstPlanNeedingTransportByPackage.set(plan.packageName, plan);
605
- }
606
- }
607
- for (const [pkg, plan] of firstPlanNeedingTransportByPackage) {
608
651
  try {
609
- const transportInfo = await getTransportInfo(client.http, client.safety, plan.objectUrl, pkg, 'I');
610
- if (transportInfo.lockedTransport) {
611
- autoTransportByPackage.set(pkg, transportInfo.lockedTransport);
612
- }
613
- else if (!transportInfo.isLocal && transportInfo.recording) {
614
- const existingList = transportInfo.existingTransports.length > 0
615
- ? `\n\nExisting transports for this package:\n${transportInfo.existingTransports
616
- .slice(0, 10)
617
- .map((t) => ` - ${t.id}: ${t.description} (${t.owner})`)
618
- .join('\n')}`
619
- : '';
620
- return errorResult(`Package "${pkg}" requires a transport number for object creation, but none was provided.\n\n` +
621
- `To fix this, either:\n` +
622
- `1. Use SAPTransport(action="list") to find an existing modifiable transport\n` +
623
- `2. Use SAPTransport(action="create", description="...") to create a new one\n` +
624
- `3. Then retry SAPWrite(action="batch_create", ..., transport="<transport_id>")` +
625
- existingList);
652
+ if (plan.type === 'FUNC') {
653
+ plan.packageName = await resolveFunctionGroupCreatePackage(client, plan.group, plan.obj.package);
654
+ plan.result.packageName = plan.packageName;
626
655
  }
656
+ await checkPackage(client.safety, plan.packageName, client.getPackageHierarchyResolver());
627
657
  }
628
658
  catch (err) {
629
- logger.warn('SAPWrite batch_create transport preflight failed; continuing without auto transport', {
630
- package: pkg,
631
- type: plan.type,
632
- name: plan.name,
633
- error: err instanceof Error ? err.message : String(err),
634
- });
635
- // If transportInfo check fails, proceed — SAP will return its own error if needed.
659
+ failBatchEntry(plan.result, 'preflight', batchFailureMessage(err, config.minimalErrors));
636
660
  }
637
661
  }
638
- const results = [];
639
- const batchWarnings = [];
640
- // Per-batch cache for the MSAG transport-vs-task guard. The bug is universal so the
641
- // guard fires for every MSAG entry, but a batch typically shares one transport — cache
642
- // the lookup result to avoid one HTTP roundtrip per object.
643
- const transportLookupCache = new Map();
644
- // Accumulated objects whose create + source-write phase succeeded — used by the
645
- // terminal activateBatch when activateAtEnd=true. Order matches the input order.
646
- const writtenObjects = [];
647
- // BTP/Steampunk needs cloud-correct create XML (G-3) + a real responsible user (G-5);
648
- // constant across the batch, so resolve once.
662
+ if (results.some((entry) => entry.status === 'failed'))
663
+ return report(true);
664
+ const autoTransportByPackage = new Map();
665
+ for (const plan of batchPlan) {
666
+ if (plan.transport || autoTransportByPackage.has(plan.packageName))
667
+ continue;
668
+ autoTransportByPackage.set(plan.packageName, undefined);
669
+ try {
670
+ const resolved = await resolveCreateTransport(client, plan.objectUrl, plan.packageName, config.minimalErrors);
671
+ if (resolved.error)
672
+ failBatchEntry(plan.result, 'preflight', resolved.error);
673
+ else
674
+ autoTransportByPackage.set(plan.packageName, resolved.transport);
675
+ }
676
+ catch (error) {
677
+ failBatchEntry(plan.result, 'preflight', batchFailureMessage(error, config.minimalErrors));
678
+ }
679
+ }
680
+ if (results.some((entry) => entry.status === 'failed'))
681
+ return report(true);
649
682
  const systemType = resolveWriteSystemType(config, client);
650
683
  const cloud = systemType === 'btp';
651
684
  const responsible = config.username || (await client.getEffectiveUser());
685
+ const transportLookupCache = new Map();
652
686
  for (const plan of batchPlan) {
653
- const { obj, type: objType, name: objName, packageName: objPackage, objectUrl: objUrl } = plan;
654
- const objTransport = plan.explicitTransport ?? autoTransportByPackage.get(objPackage);
655
- const metadataObject = isMetadataWriteType(objType);
656
- let objSource = obj.source ? String(obj.source) : undefined;
657
- const objDescription = String(obj.description ?? objName);
658
- if (objType === 'FUNC') {
659
- const prepared = prepareFunctionModuleCreateSource(objName, objSource, obj.parameters);
660
- objSource = prepared.shouldWrite ? prepared.source : undefined;
661
- batchWarnings.push(...prepared.warnings.map((warning) => `${objType} ${objName}: ${warning}`));
662
- }
663
- // Mixed-case object name rejection (matches the create-path check above).
664
- // Universal SAP convention — TADIR is uppercase on every release.
665
- // Cheap check first: no HTTP call, fail fast on bad names.
666
- if (objName && objName !== objName.toUpperCase()) {
667
- results.push({
668
- type: objType,
669
- name: objName,
670
- packageName: objPackage,
671
- status: 'failed',
672
- error: `Object name "${objName}" contains lowercase characters. SAP object names must be uppercase (e.g. "${objName.toUpperCase()}"). Source code inside the object can use mixed case.`,
673
- });
674
- break;
675
- }
676
- // MSAG transport-vs-task guard (per-batch cache to avoid per-object roundtrip).
677
- if (objType === 'MSAG' && objTransport) {
678
- let tr = transportLookupCache.get(objTransport);
679
- if (tr === undefined) {
680
- tr = await getTransport(client.http, client.safety, objTransport);
681
- transportLookupCache.set(objTransport, tr);
682
- }
683
- if (!tr) {
684
- results.push({
685
- type: objType,
686
- name: objName,
687
- packageName: objPackage,
688
- status: 'failed',
689
- error: `Transport "${objTransport}" is not a valid transport request. MSAG creation requires a transport request number, not a task number.`,
690
- });
691
- break;
687
+ try {
688
+ plan.transport ??= autoTransportByPackage.get(plan.packageName);
689
+ if (plan.type === 'MSAG' && plan.transport) {
690
+ if (!transportLookupCache.has(plan.transport))
691
+ transportLookupCache.set(plan.transport, await getTransport(client.http, client.safety, plan.transport));
692
+ if (!transportLookupCache.get(plan.transport)) {
693
+ failBatchEntry(plan.result, 'preflight', `Transport "${plan.transport}" is not a valid transport request. MSAG creation requires a transport request number, not a task number.`);
694
+ continue;
695
+ }
692
696
  }
693
697
  }
694
- // AFF header validation per object (if schema available)
695
- const affResult = validateAffHeader(objType, { description: objDescription, originalLanguage: 'en' });
696
- if (!affResult.valid) {
697
- results.push({
698
- type: objType,
699
- name: objName,
700
- packageName: objPackage,
701
- status: 'failed',
702
- error: `AFF metadata validation failed:\n- ${(affResult.errors ?? []).join('\n- ')}`,
703
- });
704
- break;
698
+ catch (err) {
699
+ failBatchEntry(plan.result, 'preflight', batchFailureMessage(err, config.minimalErrors));
700
+ continue;
705
701
  }
706
702
  try {
707
- // Pre-validate source with lint BEFORE creating the object to avoid orphaned objects.
708
- // Metadata objects (DOMA/DTEL) are XML-only and intentionally skip source lint.
709
- if (!metadataObject && objSource) {
710
- const cdsGuard = guardCdsSyntax(objType, objSource, getCachedFeatures());
711
- if (cdsGuard) {
712
- results.push({
713
- type: objType,
714
- name: objName,
715
- packageName: objPackage,
716
- status: 'failed',
717
- error: cdsGuard.content[0].text,
718
- });
719
- break;
720
- }
721
- const preflightWarnings = runRapPreflightValidation(objSource, objType, objName, getCachedFeatures(), config.systemType, preflightOverride);
722
- if (preflightWarnings.blocked) {
723
- results.push({
724
- type: objType,
725
- name: objName,
726
- packageName: objPackage,
727
- status: 'failed',
728
- error: preflightWarnings.result.content[0].text,
729
- });
730
- break;
731
- }
732
- if (preflightWarnings.warnings) {
733
- batchWarnings.push(`${objType} ${objName}: ${preflightWarnings.warnings}`);
734
- }
735
- const lintWarnings = runPreWriteLint(objSource, objType, objName, config, lintOverride);
736
- if (lintWarnings.blocked) {
737
- results.push({
738
- type: objType,
739
- name: objName,
740
- packageName: objPackage,
741
- status: 'failed',
742
- error: `source rejected by lint: ${lintWarnings.result.content[0].text}`,
743
- });
744
- break;
745
- }
746
- }
747
- // Step 1: Create the object (per-entry transparent-table discovery gate;
748
- // mirrors the single-create site above. TABL/DS skips it — /structures/ always exists.)
749
- if ((objType === 'TABL' || objType === 'TABL/DT') && isTablesEndpointAvailable() === false) {
750
- results.push({
751
- type: objType,
752
- name: objName,
753
- packageName: objPackage,
754
- status: 'failed',
755
- error: TABL_DT_WRITE_UNAVAILABLE_HINT,
756
- });
757
- break;
758
- }
759
- // DOMA create needs /ddic/domains/ (absent wholesale on NW 7.50/7.51) — mirror the gate above.
760
- if (objType === 'DOMA' && isDomainsEndpointAvailable() === false) {
761
- results.push({
762
- type: objType,
763
- name: objName,
764
- packageName: objPackage,
765
- status: 'failed',
766
- error: DOMA_WRITE_UNAVAILABLE_HINT,
767
- });
768
- break;
769
- }
770
- // TTYP create needs /ddic/tabletypes/ (absent on NW 7.50) — mirror the TABL/DT gate above.
771
- if (objType === 'TTYP' && isTableTypesEndpointAvailable() === false) {
772
- results.push({
773
- type: objType,
774
- name: objName,
775
- packageName: objPackage,
776
- status: 'failed',
777
- error: TTYP_WRITE_UNAVAILABLE_HINT,
778
- });
779
- break;
780
- }
781
- // Server-driven objects need the AFF engine (handleServerDrivenObjectWrite), which only runs on
782
- // the single-object path. objectBasePath() has no SDO case and silently falls through to the
783
- // PROG path, so without this guard a batched SDO would POST a program create body.
784
- if (isServerDrivenObjectType(objType)) {
785
- results.push({
786
- type: objType,
787
- name: objName,
788
- packageName: objPackage,
789
- status: 'failed',
790
- error: `batch_create does not support server-driven object type ${objType}. Create it with a single SAPWrite(action="create", type="${objType}", name="${objName}") call.`,
791
- });
792
- break;
793
- }
794
- const createUrl = objUrl.replace(/\/[^/]+$/, '');
795
- const objMetadataProps = getMetadataWriteProperties(obj);
796
- if (objType === 'FUNC') {
797
- objMetadataProps.group = plan.group;
798
- }
799
- // Sets behaviorExtension + baseBdef on the metadata so buildCreateXml emits the adtTemplate;
800
- // the return value (used for the optional read-back warning) isn't needed in the batch path.
801
- applyBdefBehaviorExtensionMetadata(objType, objSource, objMetadataProps);
802
- const body = buildCreateXml(objType, objName, objPackage, objDescription, objMetadataProps, config.language, responsible, cloud);
803
- const contentType = createContentTypeForType(objType, cloud);
804
- const needsPackageParam = objType === 'BDEF' || objType === 'TABL' || objType === 'TABL/DT' || objType === 'TABL/DS';
703
+ applyBdefBehaviorExtensionMetadata(plan.type, plan.source, plan.metadata);
704
+ plan.body = buildCreateXml(plan.type, plan.name, plan.packageName, plan.description, plan.metadata, config.language, responsible, cloud);
705
+ plan.contentType = createContentTypeForType(plan.type, cloud);
706
+ }
707
+ catch (err) {
708
+ // Construction errors are local input diagnostics, not SAP response details.
709
+ failBatchEntry(plan.result, 'preflight', batchFailureMessage(err, false));
710
+ }
711
+ }
712
+ if (results.some((entry) => entry.status === 'failed'))
713
+ return report(true);
714
+ const writtenPlans = [];
715
+ for (const plan of batchPlan) {
716
+ const entry = plan.result;
717
+ let phase = 'create';
718
+ try {
719
+ const needsPackage = plan.type === 'BDEF' || canonicalTablType(plan.type) === 'TABL';
720
+ entry.creation = 'unknown';
805
721
  try {
806
- await createObject(client.http, client.safety, createUrl, body, contentType, objTransport, needsPackageParam ? objPackage : undefined, getCachedFeatures()?.abapRelease, systemType, objName);
722
+ await createObject(client.http, client.safety, plan.objectUrl.replace(/\/[^/]+$/, ''), plan.body, plan.contentType, plan.transport, needsPackage ? plan.packageName : undefined, getCachedFeatures()?.abapRelease, systemType, plan.name);
807
723
  }
808
- catch (createErr) {
809
- if (createErr instanceof AdtApiError && (createErr.statusCode === 400 || createErr.statusCode === 409)) {
810
- const syntaxDetail = await tryPostSaveSyntaxCheck(client, objType, objName);
811
- if (syntaxDetail) {
812
- createErr.message += syntaxDetail;
813
- }
724
+ catch (err) {
725
+ if (err instanceof AdtSafetyError)
726
+ entry.creation = 'not_attempted';
727
+ // Keep the original create failure even if the optional diagnostic cannot run.
728
+ if (!config.minimalErrors && err instanceof AdtApiError && [400, 409].includes(err.statusCode)) {
729
+ const detail = await tryPostSaveSyntaxCheck(client, plan.type, plan.name).catch(() => undefined);
730
+ if (detail)
731
+ err.message += detail;
814
732
  }
815
- throw createErr;
733
+ throw err;
816
734
  }
817
- if (objType === 'FUNC' && objMetadataProps.processingType !== undefined) {
818
- await persistFunctionModuleProcessingMetadata(client, objUrl, objName, objMetadataProps.processingType, objMetadataProps.updateTaskKind, objTransport);
735
+ entry.creation = 'confirmed';
736
+ phase = 'write';
737
+ if (entry.write !== 'not_required')
738
+ entry.write = 'unknown';
739
+ if (plan.type === 'FUNC' && plan.metadata.processingType !== undefined) {
740
+ await persistFunctionModuleProcessingMetadata(client, plan.objectUrl, plan.name, plan.metadata.processingType, plan.metadata.updateTaskKind, plan.transport);
819
741
  }
820
- // Step 1b: DTEL POST ignores labels — follow up with PUT on main session
821
- if (objType === 'DTEL' && dtelNeedsPostCreateUpdate(objMetadataProps)) {
742
+ if (plan.type === 'DTEL') {
822
743
  await client.http.withStatefulSession(async (session) => {
823
- const lock = await lockObject(session, client.safety, objUrl, 'MODIFY', getCachedFeatures()?.abapRelease);
824
- const lockTransport = objTransport ?? (lock.corrNr || undefined);
744
+ const lock = await lockObject(session, client.safety, plan.objectUrl, 'MODIFY', getCachedFeatures()?.abapRelease);
825
745
  try {
826
- await updateObject(session, client.safety, objUrl, body, lock.lockHandle, contentType, lockTransport);
746
+ await updateObject(session, client.safety, plan.objectUrl, plan.body, lock.lockHandle, plan.contentType, plan.transport ?? (lock.corrNr || undefined));
827
747
  }
828
748
  finally {
829
- await unlockObject(session, objUrl, lock.lockHandle);
749
+ await unlockObject(session, plan.objectUrl, lock.lockHandle);
830
750
  }
831
751
  });
832
752
  }
833
- // TTYP POST creates SAP's default table-type shell; PUT the real row type before activation.
834
- if (objType === 'TTYP') {
835
- await putTtypMetadataAfterCreate(client, objUrl, objName, body, contentType, objTransport);
836
- }
837
- // Step 2: Write source if provided
838
- if (!metadataObject && objSource) {
839
- const srcUrl = objType === 'FUNC' ? `${objUrl}/source/main` : sourceUrlForType(objType, objName);
840
- await safeUpdateSource(client.http, client.safety, objUrl, srcUrl, objSource, objTransport, getCachedFeatures()?.abapRelease);
753
+ if (plan.type === 'TTYP')
754
+ await putTtypMetadataAfterCreate(client, plan.objectUrl, plan.name, plan.body, plan.contentType, plan.transport);
755
+ if (!plan.metadataObject && plan.source) {
756
+ await safeUpdateSource(client.http, client.safety, plan.objectUrl, plan.sourceUrl, plan.source, plan.transport, getCachedFeatures()?.abapRelease);
841
757
  }
842
- // objUrl is already subtype-aware (including parent-group routing for FUNC),
843
- // so source and activation share one canonical address.
844
- const activationUrl = objUrl;
758
+ if (entry.write !== 'not_required')
759
+ entry.write = 'confirmed';
845
760
  if (activateAtEnd) {
846
- // Step 3 deferred: track this object for the terminal activateBatch call.
847
- // Cache invalidation also moves to AFTER the terminal activate succeeds —
848
- // invalidating now would let the next read see a draft we couldn't activate.
849
- writtenObjects.push({ type: objType, name: objName, url: activationUrl });
850
- results.push({ type: objType, name: objName, packageName: objPackage, status: 'success' });
761
+ writtenPlans.push(plan);
851
762
  }
852
763
  else {
853
- // Step 3: Activate the object (inline, default behavior).
854
- const activationResult = await activate(client.http, client.safety, activationUrl);
855
- if (!activationResult.success) {
856
- results.push({
857
- type: objType,
858
- name: objName,
859
- packageName: objPackage,
860
- status: 'failed',
861
- error: `activation failed: ${activationResult.messages.join('; ')}`,
862
- });
764
+ phase = 'activate';
765
+ entry.activation = 'unknown';
766
+ const outcome = await activate(client.http, client.safety, plan.objectUrl);
767
+ if (!outcome.success) {
768
+ entry.activation = 'failed';
769
+ failBatchEntry(entry, phase, `activation failed: ${batchFailureMessage(outcome.messages.join('; '), config.minimalErrors)}`);
863
770
  break;
864
771
  }
865
- invalidateWrittenObject(objType, objName);
866
- results.push({ type: objType, name: objName, packageName: objPackage, status: 'success' });
772
+ entry.activation = 'confirmed';
867
773
  }
774
+ entry.status = 'success';
868
775
  }
869
776
  catch (err) {
870
- results.push({
871
- type: objType,
872
- name: objName,
873
- packageName: objPackage,
874
- status: 'failed',
875
- error: err instanceof Error ? err.message : String(err),
876
- });
777
+ failBatchEntry(entry, phase, batchFailureMessage(err, config.minimalErrors));
877
778
  break;
878
779
  }
780
+ finally {
781
+ if (entry.creation !== 'not_attempted')
782
+ invalidateWrittenObject(plan.type, plan.name);
783
+ }
879
784
  }
880
- // Add 'skipped' entries for objects that were never attempted due to early break
881
- for (let i = results.length; i < objects.length; i++) {
882
- const skippedPlan = batchPlan[i];
883
- const skipped = skippedPlan?.obj ?? objects[i];
884
- results.push({
885
- type: skippedPlan?.type ?? normalizeObjectType(String(skipped?.type ?? '')),
886
- name: skippedPlan?.name ?? String(skipped?.name ?? ''),
887
- packageName: skippedPlan?.packageName ?? normalizePackageOverride(skipped?.package, defaultPackage),
888
- status: 'failed',
889
- error: 'skipped stopped after previous failure',
890
- });
891
- }
892
- // ── Terminal activateBatch (activateAtEnd=true) ─────────────────────
893
- // After every write-phase succeeded (or broke off early), issue ONE batch
894
- // activate over the already-written subset. This is the killer feature
895
- // for composition-linked DDLS and RAP behavior stacks — SAP's activator
896
- // sees the whole graph in a single POST and resolves cross-references
897
- // internally, so parent → child siblings activate cleanly.
898
- let terminalActivationFailure;
899
- if (activateAtEnd && writtenObjects.length > 0) {
900
- const activationOutcome = await activateBatch(client.http, client.safety, writtenObjects);
901
- if (activationOutcome.success) {
902
- // Defensive: per-object status was already 'success' from the write phase.
903
- // Cache invalidation moves here so a failed terminal activate doesn't strand
904
- // a stale 'active' cache entry. Invalidate inactive-lists once for the user.
905
- for (const o of writtenObjects) {
906
- cachingLayer?.invalidate(o.type, o.name, 'all');
785
+ if (activateAtEnd && writtenPlans.length > 0) {
786
+ for (const plan of writtenPlans)
787
+ plan.result.activation = 'unknown';
788
+ try {
789
+ const outcome = await activateBatch(client.http, client.safety, writtenPlans.map((plan) => ({ type: plan.type, name: plan.name, url: plan.objectUrl })));
790
+ if (!outcome.success) {
791
+ activationMessages.push(`${writtenPlans.length}/${writtenPlans.length} written, batch activation failed.`);
792
+ const unassigned = outcome.details.filter((detail) => !writtenPlans.some((plan) => activationDetailMatchesObject(detail.uri, plan.objectUrl)));
793
+ // Flat messages duplicate structured details; retain only otherwise-unrepresented messages.
794
+ const messages = [
795
+ ...unassigned.map((detail) => detail.text),
796
+ ...outcome.messages.filter((message) => !outcome.details.some((detail) => detail.text === message)),
797
+ ];
798
+ if (messages.length)
799
+ activationMessages.push(batchFailureMessage([...new Set(messages)].join('; '), config.minimalErrors));
907
800
  }
908
- invalidateInactiveList(cachingLayer, client, cacheSecurity);
909
- }
910
- else {
911
- // Flip every written-but-not-yet-activated entry to 'failed', preserving the
912
- // "create + source-write succeeded" context. Reuse the existing per-object
913
- // diagnostic mapper so callers see the activation messages keyed by object name.
914
- const batchStatuses = buildBatchActivationStatuses(writtenObjects, activationOutcome);
915
- const statusDetails = formatBatchActivationStatuses(batchStatuses);
916
- terminalActivationFailure = statusDetails;
917
- const statusByName = new Map(batchStatuses.map((s) => [`${s.type}\x00${s.name}`, s]));
918
- for (const result of results) {
919
- if (result.status !== 'success')
801
+ for (const plan of writtenPlans) {
802
+ if (outcome.success) {
803
+ plan.result.activation = 'confirmed';
920
804
  continue;
921
- const key = `${result.type}\x00${result.name}`;
922
- const matched = statusByName.get(key);
923
- if (!matched)
924
- continue;
925
- // Some entries may still report status 'active' if the activator returned
926
- // success: false but had no per-object error details keep them as 'success'.
927
- if (matched.status === 'active')
928
- continue;
929
- result.status = 'failed';
930
- const detail = matched.messages.length > 0 ? ` — ${matched.messages.join('; ')}` : '';
931
- // Preserve the "create + source-write succeeded" context so the user sees that
932
- // the failure was specifically the activation step, not the write step.
933
- result.error = `${writtenObjects.length}/${writtenObjects.length} written, batch activation failed${detail}`;
805
+ }
806
+ const errors = outcome.details.filter((detail) => detail.severity === 'error' && activationDetailMatchesObject(detail.uri, plan.objectUrl));
807
+ plan.result.activation = errors.length ? 'failed' : 'unknown';
808
+ const message = errors.length
809
+ ? batchFailureMessage(errors.map((detail) => detail.text).join('; '), config.minimalErrors)
810
+ : 'Activation remains unknown: batch activation failed without an object-specific error.';
811
+ failBatchEntry(plan.result, 'activate', message);
934
812
  }
935
813
  }
814
+ catch (err) {
815
+ for (const plan of writtenPlans)
816
+ failBatchEntry(plan.result, 'activate', batchFailureMessage(err, config.minimalErrors));
817
+ }
818
+ finally {
819
+ for (const plan of writtenPlans)
820
+ invalidateWrittenObject(plan.type, plan.name);
821
+ }
936
822
  }
937
- // ────────────────────────────────────────────────────────────────────
938
- const summary = results
939
- .map((r) => r.status === 'success'
940
- ? `${r.name} (${r.type}) ✓ [${r.packageName}]`
941
- : `${r.name} (${r.type}) ✗ [${r.packageName}] — ${r.error}`)
942
- .join(', ');
943
- const successCount = results.filter((r) => r.status === 'success').length;
944
- const hasFailure = results.some((r) => r.status === 'failed');
945
- const warningSuffix = batchWarnings.length > 0 ? `\n\nRAP preflight warnings:\n- ${batchWarnings.join('\n- ')}` : '';
946
- const activateAtEndSuffix = terminalActivationFailure !== undefined ? `\n\nBatch activation diagnostics:${terminalActivationFailure}` : '';
947
- const packageNames = [...new Set(batchPlan.map((item) => item.packageName))];
948
- const packageSummary = packageNames.length === 1
949
- ? `in package ${packageNames[0]}`
950
- : packageNames.length <= 3
951
- ? `across packages [${packageNames.join(', ')}]`
952
- : `across ${packageNames.length} packages`;
953
- const activateAtEndPrefix = activateAtEnd ? '; activated as a single batch' : '';
954
- if (hasFailure) {
955
- const cleanupHint = successCount > 0
956
- ? ` Note: ${successCount} already-created object(s) remain on the SAP system and may need manual cleanup.`
957
- : '';
958
- return errorResult(`Batch created ${successCount}/${objects.length} objects ${packageSummary}${activateAtEndPrefix}: ${summary}${cleanupHint}${warningSuffix}${activateAtEndSuffix}`);
959
- }
960
- return textResult(`Batch created ${successCount} objects ${packageSummary}${activateAtEndPrefix}: ${summary}${warningSuffix}${activateAtEndSuffix}`);
823
+ return report();
961
824
  }
962
825
  //# sourceMappingURL=create.js.map